Code Generation
ScaffScript uses two constructs to define what code gets written and where:
#[blockName], named content blocks.intg, keyword to map blocks to GM asset paths.
Integration Blocks (#[...])
Section titled “Integration Blocks (#[...])”A block is a named section of content written to a specific GM asset. It’s reusable and can be integrated to multiple assets.
Syntax
Section titled “Syntax”#[blockName]// GML / ScaffScript code here...Example
Section titled “Example”#[main]show_debug_message("Hello, World!");
#[other_block]var xx = 10;var yy = 20;
#[setYourOwnBlockName]function my_function() { show_debug_message("Hello, from my_function in setYourOwnBlockName!");}Blocks extend until the next #[...] header or end of file.
Result
Section titled “Result”show_debug_message("Hello, World!");var xx = 10;var yy = 20;function my_function() { show_debug_message("Hello, from my_function in setYourOwnBlockName!");}Later, you can map these blocks to a specific GM asset using the intg statement.
Event Targeting
Section titled “Event Targeting”You can set an object event for an integration block. This is useful when you want to write code for a specific event of an object.
Syntax
Section titled “Syntax”#[blockName as <EventType>]// code here...
#[blockName as <EventType>[:EventSubtype]]// code here...Use as keyword to target an event.
#[<EventType> Event]// code here...
#[<EventType>[:EventSubtype] Ev]// code here...Use event or ev keyword (case insensitive) to target an event. The block name is the same as the event name (<EventType> or <EventType>[:EventSubtype]).
Example
Section titled “Example”#[objCreate as create]show_debug_message("create event!");
#[press_enter as keypress:keyboard_enter]show_debug_message("F5 pressed!");
#[create event] // block name = `create`show_debug_message("create event!");
#[keydown:keyboard_f5 Ev] // block name = `keydown:keyboard_f5`show_debug_message("F5 held!");
#[coll_player as collision:objPlayer] // `objPlayer` is an existing object in the GameMaker IDEshow_debug_message("collided with objPlayer!");Configure block behavior with flags after -- in the block header name or event. Multiple flags are space-separated.
Syntax
Section titled “Syntax”#[blockName -- flag1 flag2 ...]#[blockName as EventType -- flag1 flag2 ...]#[blockName as EventType:EventSubtype -- flag1 flag2 ...]#[EventType Event -- flag1 flag2 ...]#[EventType:EventSubtype Ev -- flag1 flag2 ...]Flags Reference
Section titled “Flags Reference”| Flag | Effect |
|---|---|
debug | Emptied when debugLevel >= 1 |
dev | development | Emptied when production: true |
prod | production | Emptied when production: false |
skip | disabled | Always emptied |
exclude | Written to .out/, but won’t be integrated to the GM project |
<platform> | Included only when targetPlatform matches |
!<platform> | Excluded when targetPlatform matches |
Example
Section titled “Example”#[main -- dev]var x = 10;show_debug_message("Only in development!");
#[main_android as create -- android exclude]show_debug_message("Android only, and excluded from GM project integration!");
#[keypress:keyboard_enter Event -- !html5]// some code here that won't work in HTML5Block Merging
Section titled “Block Merging”Multiple blocks with the same name are merged, their bodies concatenated with a blank line separator. The order of merged blocks is preserved.
Example
Section titled “Example”#[main]show_debug_message("From first main block!");
#[main]show_debug_message("This is from another 'main' block!");Result
Section titled “Result”#[main]show_debug_message("From first main block!"); show_debug_message("This is from another 'main' block!");Integration Statement (intg)
Section titled “Integration Statement (intg)”Maps blocks to a GM asset path.
Syntax
Section titled “Syntax”intg myBlock to "./path/to/asset" // single blockintg { block1, block2 } to "./path/to/asset" // multiple blocksintg * to "./path/to/asset" // all blocksThe path points to the target GM asset relative to the project root. Omit the .gml extension, ScaffScript appends it for you.
Example
Section titled “Example”intg { definition, main } to "./scripts/myScript"intg { objCreate, keydown:keyboard_f1, definition } to "./objects/objController"intg * to "./scripts/My Folder/myAllScript"
#[main]show_debug_message("Hello, from main block!");
function print() { show_debug_message("Hello, from print function!");}
#[definition]var my_var = 10;var vec2 = function(x, y) { return { x, y };}
#[objCreate as create]show_debug_message("Hello, from objController create event!");
var my_obj = { x: 0, y: 0};
#[keydown:keyboard_f1 Event]show_debug_message("Showing game instructions...");
function show_instructions() { show_debug_message("Game instructions shown!");}
show_instructions();Result
Section titled “Result”var my_var = 10;var vec2 = function(x, y) { return { x, y };}
show_debug_message("Hello, from main block!");
function print() { show_debug_message("Hello, from print function!");}show_debug_message("Hello, from objController create event!");
var my_obj = { x: 0, y: 0};show_debug_message("Showing game instructions...");
function show_instructions() { show_debug_message("Game instructions shown!");}
show_instructions();// the `definition` block doesn't have an event// but it's integrated to the object asset based on the `intg` statement// so, this block won't be integrated to the objectvar my_var = 10;var vec2 = function(x, y) { return { x, y };}// `My Folder` only exists in the GameMaker IDE, not on disk// see the virtual path section below for more informationshow_debug_message("Hello, from main block!");
function print() { show_debug_message("Hello, from print function!");}
var my_var = 10;var vec2 = function(x, y) { return { x, y };}
show_debug_message("Hello, from objController create event!");
var my_obj = { x: 0, y: 0};
show_debug_message("Showing game instructions...");
function show_instructions() { show_debug_message("Game instructions shown!");}
show_instructions();Rules & Explanation
Section titled “Rules & Explanation”-
The used asset is the last segment of the path. Based on the example above,
myScriptandobjControllerare the GameMaker assets. They will be created automatically by ScaffScript if they don’t exist yet, but see the note for the asset creation. -
The
intgstatement with*is a catch-all. It integrates all blocks to the specified asset, either non-event blocks or blocks with events. Use it wisely. -
The integration blocks and integration statement is scoped to the file it’s defined in. You can’t integrate a block from another file.
-
The order of the blocks is preserved based on the order of the
intgstatement. In the example above, theintg { definition, main } to "./scripts/myScript"statement, themainblock is integrated after thedefinitionblock. -
If you integrate a block with an event, you should integrate it to the object asset itself, not to an event file. ScaffScript will handle the event integration for you.
index.ss intg { myCreateEvent } to "./objects/My Object/objController/create"intg { myCreateEvent } to "./objects/My Object/objController/Create_0"index.ss intg { myCreateEvent } to "./objects/My Object/objController"
Edge Cases
Section titled “Edge Cases”If you integrate an asset with multiple intg statements, the used block content is the last intg statement. For example:
intg { main } to "./scripts/myScript"intg * to "./scripts/myScript"
#[main]show_debug_message("Hello, from main block!");
#[definition]var my_var = 10;The myScript asset contains all blocks, because the last intg statement is intg * to "./scripts/myScript".
show_debug_message("Hello, from main block!");var my_var = 10;If you encounter this scenario, that’s mean your code is not organized well.
Output Directory
Section titled “Output Directory”All files are staged in .out/ first. The purpose of this directory is to serve as a preview of what will be integrated to the GameMaker project.
Set clearOutputDir: true to wipe it before each run.
Set noIntegration: true to generate to .out/ only, without touching the GameMaker project.
Example
Section titled “Example”intg * to "./scripts/My-Folder/myScript"intg { definition } to "./objects/objController"
#[main]show_debug_message("Hello, from main block!");
#[definition as create]var my_var = 10;var vec2 = function(x, y) { return { x, y };}Result
Section titled “Result”Directory.out/
Directoryscripts/
DirectoryMy-Folder/
- myScript.gml
Directoryobjects/
DirectoryobjController/
- Create_0.gml
Virtual Path
Section titled “Virtual Path”When the path has more than 2 segments, the segment(s) between the root and the asset name become folders/groups in the GameMaker IDE. We call this a virtual path, because they’re not a physical folders in the file system.
Example
Section titled “Example”intg { myBlock } to "./scripts/Scripts/scrHello"intg { myBlock } to "./scripts/Scripts/My-Folder/scrOther"intg { createEv } to "./objects/Objects/Players/objPlayer1"intg { createEv, destroyEv } to "./objects/Objects/Enemies/objEnemy1"intg { createEv } to "./objects/Singleton/objController"Result
Section titled “Result”Directory.out/
Directoryobjects/
DirectoryObjects/
DirectoryPlayers/
DirectoryobjPlayer1/
- Create_0.gml
DirectoryEnemies/
DirectoryobjEnemy1/
- Create_0.gml
- Destroy_0.gml
DirectorySingleton/
DirectoryobjController/
- Create_0.gml
Directoryscripts/
DirectoryScripts/
DirectoryMy-Folder/
DirectoryscrOther/
- scrOther.gml
DirectoryscrHello/
- scrHello.gml
ScaffScript generates the output based on the intg statement, including the virtual path.
DirectoryObjects/
DirectoryPlayers/
- objPlayer1 // create event included
DirectoryEnemies/
- objEnemy1 // create and destroy event included
DirectoryScripts/
DirectoryMy-Folder/
- scrOther
- scrHello
DirectorySingleton/
- objController // create event included
- The virtual path is segment(s) between
scripts/and the script name, or betweenobjects/and the object name. - If the path doesn’t exist, it will be created. Otherwise, it will be used.
Relationship Between intg and #[...]
Section titled “Relationship Between intg and #[...]”To understand the relationship between intg and #[...] better, let’s make an analogy with bottles, various types of water, and shelves.
- The
intg { ... }are the bottles. They acts as a container for the water (code). The bottle name is the last segment of the path. For example,./scripts/myScript, somyScriptis the bottle name. - The integration blocks (
#[...]) are the types of water. They define what kind of water (code) will be poured into the bottle. The bottle labels are the event defined in the integration block. For example,as createis the bottle label. - The non-last segment(s) of the path (
./scripts/,./objects/,./scripts/My Folder/) are the shelves. They acts as a container for the bottles. - You can have multiple types of water (integration blocks) in a single bottle (integration statement). For example, you can have a
myScriptbottle that contains both#[main]water and#[objCreate]water. - You can have multiple bottles in a shelf (virtual path). For example, you can put
myScriptandmyOtherScriptbottles in the./scripts/My Folder/shelf. - The types of water (integration blocks) isn’t tied to a specific bottle. For example, you can pour
#[main]water intomyScriptandmyOtherScriptbottles. - The bottle (asset) and shelf (virtual path) is created automatically by ScaffScript if they don’t exist yet.
- The shelf (virtual path) is only visible in the GameMaker IDE, not on disk.
- The bottle (asset) is a physical file on disk, and it’s the one that’s being edited when you open the asset in the GameMaker IDE.
- The integration process to GameMaker IDE is like when you’re distributing your bottles to the shelves in the supermarket.
./objects/*shelves is special, so it can only accept labelled bottles (integration blocks with event). Unlabelled bottles (integration blocks without event) will only be put in the shelf without being distributed to the supermarket (GameMaker IDE).