Scripting
Scripts define what happens when players interact with your project.
Quick Reference
| Want to... | Use | Section |
|---|---|---|
| Navigate to cell | goto "CellName" |
Navigation |
| Show/hide object | show X / hide X |
Visibility |
| Spawn object at runtime | spawn "Template" {params} |
Spawn & Destroy |
| Remove spawned object | destroy obj |
Spawn & Destroy |
| Respond to click | onClick: |
Events |
| Respond to key | onKeyDown "Space": |
Events |
| Detect overlap | onOverlap: |
Events |
| Initialize spawned object | onSpawn: |
Events |
| Cleanup before destroy | onDestroy: |
Events |
| Reference other object | other.name |
Special References |
| Wait before next action | wait 500ms or wait 2s |
Timing |
| Wait for movement to settle | wait movement |
Timing |
| Restart current level | restart |
Restart |
| Set variable | set score 10 |
Variables |
| Set object property | set X.opacity 0.5 |
Properties |
| Check condition | if score > 10: |
Conditions |
| Loop over items | foreach item in list: |
Foreach |
| Find first match | first #tag where .x > 0: |
First |
| Reference self | self.property |
Special References |
| Reference parent | parent |
Special References |
| Target by tag | #tagname |
Tags |
| Target by type | all buttons |
Type Selectors |
| Cross-cell reference | @Cell.Object |
Cross-Cell |
| Send message | shout "MSG" |
Messaging |
| Define reusable code | action name: |
Custom Actions |
| Create slideshow | set X pageable[A, B, C] |
Pageable |
| Access array element | arr[i] or arr[i].x |
Arrays |
| Get array length | length(arr) |
Array Functions |
| Generate range | range(5) → [0,1,2,3,4] |
Array Functions |
| Shuffle array | shuffle(arr) |
Array Functions |
| Random element | pick(arr) |
Array Functions |
| Check if cell has object | isEmpty(grid, x, y) |
Grid Functions |
| Get cells without objects | emptyCells(grid) |
Grid Functions |
| Store metadata per cell | set Grid.cell[x][y].prop value |
Grid Cell Data |
| Read metadata per cell | Grid.cell[x][y].prop |
Grid Cell Data |
| Query cells by metadata | cellsWhere(grid, prop, op, val) |
Grid Functions |
| Clear cell metadata | clear Grid |
Grid Cell Data |
| Debug output | log "value is" x |
Log |
| Animate object | shake self 300 |
Animations |
| Stop animation | stop self |
Animations |
| Respond to hover | onHover: / onHoverEnd: |
Events |
| Make object draggable | set X draggable |
Dynamics - Draggable |
| Respond to drag | onDragStart: / onDrag: / onDragEnd: |
Events |
| Detect movement | onMove "down": / onStop: |
Events |
| Detect jump/land | onJump: / onLanding: |
Events |
| Sync parallel scripts | sync |
Parallel Execution |
| Send HTTP request | post url {data} |
HTTP Requests |
| Fetch external data | fetch url into var |
HTTP Requests |
| Angle to direction | cardinal(angle) |
Math Functions |
| AI best move | minimax(grid, ...) |
Grid Functions |
| Exit action early | return |
Control Flow |
| Exit loop early | break |
Control Flow |
| Cell entrance animation | transition fade 500 |
Cell Transitions |
Events
Scripts are organized by events - when the code runs:
| Event | Where | When it fires |
|---|---|---|
onEnter |
Cell, Object | Player enters the cell |
onExit |
Cell, Object | Player leaves the cell |
onClick |
Object | Object is clicked |
onKeyDown |
Cell | Key is pressed |
onKeyUp |
Cell | Key is released |
onMessage |
Cell, Object | Message received via shout |
onMessageFrom |
Cell, Object | Message from specific source |
onOverlap |
Object | Starts overlapping another object (requires sensor) |
onOverlapEnd |
Object | Stops overlapping another object |
onCollide |
Object | Collides with a blocking object |
onHover |
Object | Mouse enters object bounds |
onHoverEnd |
Object | Mouse leaves object bounds |
onDragStart |
Object | User starts dragging object (requires draggable) |
onDrag |
Object | Each frame while dragging |
onDragEnd |
Object | User releases drag |
onMove |
Object | Movement direction changes |
onStop |
Object | Object stops moving |
onJump |
Object | Object performs a jump |
onLanding |
Object | Object lands on a surface |
onSpawn |
Object | Object is spawned (created at runtime) |
onDestroy |
Object | Before spawned object is destroyed |
onEnter:
show WelcomeText
onClick:
goto "NextRoom"
onKeyDown "Space":
set player.jumping true
onMessage "KEY_FOUND":
set hasKey true
onOverlap:
if other.name == "Coin":
hide other
onOverlapEnd:
set inZone false
onCollide:
if other.name == "Spike":
goto "GameOver"
onHover:
set self.opacity 0.8
onHoverEnd:
set self.opacity 1
onMove "down":
set self.state "walkDown"
onStop:
set self.state "idle"
onDragStart:
set self.opacity 0.7
onDragEnd:
set self.opacity 1
Combined events - trigger same code from multiple events:
onClick | onKeyDown "Space":
goto "NextRoom"
Note: onEnter and onExit fire for ALL objects that have them, not just the cell. They execute in z-order.
Note: onOverlap and onOverlapEnd require at least one of the two overlapping objects to have sensor: true. Use the other keyword inside these handlers to reference the other object in the overlap pair. See Dynamics - Sensor for setup details.
Note: onCollide fires when a movable object collides with a blocking object. The other keyword references the blocker. Unlike onOverlap, this fires for blocking collisions (the mover is stopped), not pass-through overlaps.
Note: onDragStart, onDrag, and onDragEnd require the object to have draggable enabled. See Dynamics - Draggable for setup details.
Note: onMove fires when an object starts moving in a new direction. Use the direction parameter to filter: onMove "down": only triggers for downward movement. Directions are "up", "down", "left", "right".
Note: onStop fires once when an object comes to rest (velocity becomes zero). Use for resetting to idle state.
Referencing Objects
By Name
Reference objects directly by their name:
show Button1
set Text1.opacity 0.5
hide MyShape
Component Children
Access children inside components with dot notation:
set Dialog.Title.content "Hello!"
set TrafficLight.RedLight.visible true
hide MyComponent.Inner
Special References
Reference objects relative to the current context:
| Reference | Meaning |
|---|---|
self |
The current object |
parent |
The containing component or cell |
siblings |
All objects at the same level |
children |
All objects contained within |
other |
The other object in an overlap pair (only in onOverlap/onOverlapEnd) |
set self.color.color "#ff0000"
hide parent
set siblings.visible false
show children
# In onOverlap/onOverlapEnd handlers:
set other.visible false
log other.name
# Check if other has a tag:
if other is #enemy:
goto "GameOver"
Chaining:
set parent.parent.visible false
hide parent.siblings
Tags
Target all objects with a tag using #tagname:
show #enemies
hide #tutorial with fade
set #buttons.opacity 0.5
Reading tag properties uses "any" semantics - true if ANY object matches:
# Toggle all lights: if any visible, hide all; else show all
if #light.visible:
hide #light
else:
show #light
Tag membership check with is:
# Check if an object has a specific tag
if other is #enemy:
goto "GameOver"
if self is #collectible:
hide self
See Objects → Tags for how to assign tags.
Type Selectors
Target all objects of a type using all:
show all buttons
hide all circles with fade
set all shapes.color.color "#ff0000"
set all text.opacity 0.5
| Selector | Matches |
|---|---|
all buttons |
Button objects |
all text |
Text objects |
all shapes |
All shape objects |
all lines |
Line objects |
all grids |
Grid objects |
all rectangles |
Rectangle shapes |
all circles |
Circle shapes |
all ellipses |
Ellipse shapes |
all polygons |
Polygon shapes |
Cross-Cell
Access objects in other cells with @:
set @Lobby.Counter.content "10"
show @Inventory.GoldIcon
set @OtherCell.Button1.visible false
Actions
Navigation
goto "Cell Name" # Navigate to cell
goto "Cell Name" with fade # With transition
goto "Cell Name" with slide-left # Slide transition
goto "Cell Name" with zoom 500 # Custom duration (ms)
Directional navigation - go to adjacent cells on the map:
goto north # Go to cell above
goto south # Go to cell below
goto east # Go to cell right
goto west # Go to cell left
Transitions: fade, slide-left, slide-right, slide-up, slide-down, zoom
Note: goto currentCell is a no-op. To re-initialize a cell, use a custom action called from both onEnter and your reset logic.
Visibility
show ObjectName # Make visible
show ObjectName with fade # With transition
hide ObjectName # Make hidden
hide ObjectName with scale 200 # Custom duration (ms)
Transitions: fade, slide-up, slide-down, scale
Timing
wait 500ms # Wait 500 milliseconds
wait 2s # Wait 2 seconds
wait 2 # Wait 2 seconds (bare number = seconds)
wait movement # Wait for all movement to finish
Note: Bare numbers are interpreted as seconds. Use ms suffix for milliseconds.
wait movement pauses until all movable objects have stopped. Use this when reading positions in keyboard handlers:
onKeyDown:
wait movement # Positions are stale until movement settles
if Player.cellX == 5:
show WinMessage
Parallel Execution
When onEnter triggers multiple objects' scripts, they run in parallel by default. Use sync to wait for other parallel scripts to reach the same point:
# Object A
onEnter:
wait 1s
show MessageA
sync # Wait for Object B to reach sync
goto "NextCell"
# Object B
onEnter:
wait 2s
show MessageB
sync # Object A waits here until B catches up
Without sync, Object A would navigate before Object B finishes.
Variables
set score 100 # Set variable
set score score + 10 # Increment
set game.bestScore 500 # Game scope
set local.name "Bob" # Local scope
Scopes:
| Scope | Syntax | Persistence |
|---|---|---|
| Session | variableName |
Cleared on reset |
| Game | game.variableName |
Persists across resets |
| Local | local.variableName |
Permanent storage |
| Self | self.variableName |
Per-component instance |
Properties
Set object properties:
set Button1.color.color "#ff0000"
set Text1.opacity 0.5
set Shape1.visible false
set Door1.x 0.75
With tags:
set #enemies.visible false
set #buttons.opacity 0.5
Inside a first block, .property targets the matched object:
first #square where .state == "NEUTRAL" {
set .state "RED"
}
Reset
reset session # Clear session variables + overrides
reset game # Same as session
reset all # Clear everything except local
reset visits # Reset current cell visit count only
reset "CellName" # Reset specific cell's visit count
Restart
Completely restart the current level:
restart
This:
- Clears all property overrides (positions, colors, visibility set by scripts)
- Resets dynamics state (objects return to original positions)
- Re-runs all
onEnterscripts
Use for "try again" buttons:
# On a Retry button
onClick:
restart
Restart vs Reset:
| Command | Effect |
|---|---|
restart |
Full level reset - positions, properties, re-runs onEnter |
reset session |
Clears variables and overrides, but no re-initialization |
reset visits |
Clears visit count only |
Messaging
Send messages project-wide:
# Sender
shout "KEY_PICKED_UP"
# Listener (on any object, any cell)
onMessage "KEY_PICKED_UP":
set hasKey true
Targeted messages:
shoutTo Player "HEAL"
shoutTo #enemies "FREEZE"
With parameters:
# Sender
shout "DAMAGE" {amount: 50, type: "fire"}
# Receiver - parameters become variables
onMessage "DAMAGE":
set health health - amount
if type == "fire":
show FireEffect
Filtered listening:
onMessageFrom Enemy "DAMAGE":
set health health - 10
Notes:
- Messages are project-wide - listeners can be in any cell
- Objects CAN hear their own shouts
- Infinite loops are blocked (can't shout same message from its handler)
HTTP Requests
Send data to external services or load dynamic content:
# Send data to a URL
post "https://api.example.com/scores" {name: playerName, score: finalScore}
# Fetch data from a URL
fetch "https://api.example.com/leaderboard" into highscores
log highscores
Notes:
- Both set
httpErrorvariable on failure - Server must have CORS headers enabled
fetchstores JSON response in specified variable
Animations
Fire-and-forget visual effects that don't block script execution.
| Action | Syntax | Description |
|---|---|---|
shake |
shake <target> <duration> [intensity] |
Horizontal oscillation |
vibrate |
vibrate <target> <duration|loop> [intensity] |
Rapid micro-jitter |
pulse |
pulse <target> <duration|loop> [scale] |
Scale breathe effect |
squeeze |
squeeze <target> <duration> [direction] |
Squash & stretch |
bounce |
bounce <target> <duration> [height] |
Vertical hop with squash |
spin |
spin <target> <duration> [direction] |
360° rotation |
stop |
stop <target> [animation] |
Stop animations |
Parameters:
duration: milliseconds orloopfor indefiniteintensity: pixels for shake/vibrate (default: shake=5, vibrate=2)scale: multiplier for pulse (default: 1.15)direction:horizontal/verticalfor squeeze,cw/ccwfor spinheight: pixels for bounce (default: 20)
Examples:
onClick:
shake self 300 # shake for 300ms
onEnter:
pulse StatusLight loop # continuous pulse until cell exit
vibrate Warning loop 1 # subtle continuous vibrate
onMessage "hit":
squeeze self 200 # impact effect
shake self 400 10 # strong shake
onClick:
spin self 500 ccw # spin counter-clockwise
bounce self 600 30 # high bounce
onExit:
stop StatusLight # stop all animations (or auto-cleared)
Notes:
- Animations are fire-and-forget (script continues immediately)
- Multiple animations can run concurrently on the same object
- Loop animations auto-stop on cell exit or use
stopcommand - Animations compose with existing object rotation/flip
Cell Transitions
Animate the entire cell on enter or exit:
onEnter:
transition fade 500 # Fade in over 500ms
onExit:
transition slide-up 300 # Slide up when leaving
Transition types: fade, slide-up, slide-down, slide-left, slide-right, zoom
Note: Cell transitions override any transition specified on the incoming goto command.
Spawn & Destroy
Create and remove objects at runtime. Use for dynamic game elements like bullets, particles, enemies, or puzzle pieces.
Create a new object from a template:
spawn "PieceTemplate" {col: 3, row: 5, color: "red"}
The template is any existing object in the cell (typically hidden with Build Visible = false). Parameters are passed to the onSpawn handler.
Template object script:
onSpawn:
set self.snapToGrid "Board"
set self.cellX col # Parameters become variables
set self.cellY row
set self.color.color color
show self
Destroy a spawned object:
destroy piece # Destroy by variable
destroy self # Destroy self
The onDestroy handler runs first, allowing cleanup or death animations:
onDestroy:
shake self 200
wait 200 # Animation completes before removal
Complete example - spawning game pieces:
# Cell script - spawn pieces on enter
onEnter:
set cols range(7)
foreach col in cols:
spawn "Piece" {col: col, color: "red"}
# Piece template script
onSpawn:
set self.snapToGrid "Grid1"
set self.cellX col
set self.cellY 0
set self.color.color color
set self.blocking true
show self with scale 200
onDestroy:
hide self with fade 150
wait 150
onClick:
destroy self
Notes:
- Template should be hidden (
buildVisible: falsein editor) - Spawned objects inherit all template properties including scripts
- Spawned objects get unique names:
Template_1,Template_2, etc. - Spawned objects are cleared on
reset("session")or cell exit - Only spawned objects can be destroyed - use
hidefor design-time objects onSpawnonly fires on spawned objects, not the templateonDestroyblocks destruction until handler completes (for animations)
Control Flow
Conditions
if score > 10:
show WinMessage
else:
show TryAgain
if visited("Room1") and hasKey:
goto "SecretRoom"
# Else-if chains
if health <= 0:
shout "GAME_OVER"
else if health < 20:
show LowHealthWarning
else:
hide LowHealthWarning
Foreach
Iterate over arrays or tagged objects:
set items [Item1, Item2, Item3]
foreach item in items {
hide item
}
# Iterate over all objects with a tag
foreach piece in #piece {
set piece.visible true
}
# Access loop variable properties
foreach piece in #piece {
log piece.name, "at", piece.cellX, piece.cellY
set piece.color.color "#ff0000"
}
# Show/hide with transitions on loop variables
foreach piece in #piece {
show piece with scale 200
wait 100
}
foreach line in ["#row1", "#row2", "#row3"] {
if all line.state == "BLUE" {
set winner "BLUE"
}
}
First
Find the first object matching a condition:
first #square where .state == "NEUTRAL" {
set .state "RED"
}
# With else clause
first #enemy where .health <= 0 {
hide self
} else {
show AllEnemiesAlive
}
Inside a first block, .property refers to the matched object.
Return and Break
Exit early from actions or loops:
# return - exit the current action or event handler
action checkWin:
if not gameOver:
return # Exit early if game not over
show WinMessage
onClick:
if locked:
return # Stop processing this click
# ... rest of handler
# break - exit the current foreach loop
foreach item in items:
if item.found:
set result item
break # Stop iterating
# This code skipped after break
Notes:
returnexits the entire action or event handlerbreakonly exits the innermostforeachloop- In nested loops,
breakexits the loop it's directly inside
Custom Actions
Define reusable action blocks:
action flash {
set self.color.color "#ffffff"
wait 100
set self.color.color "#ff0000"
}
action toggle {
if self.state == "on":
set self.state "off"
else:
set self.state "on"
}
onClick:
self.flash
Call from other objects:
onClick:
TrafficLight.toggle
StatusLight.flash
Advanced
Arrays
set lines ["#row1", "#row2", "#row3"]
set items [Item1, Item2, Item3]
set numbers [1, 2, 3, 4, 5]
Array indexing - access elements by position (0-based):
set cells emptyCells("Grid1")
set first cells[0] # First element
set x cells[0].x # Property of element
set i 2
set third cells[i] # Variable index
Aggregates
Check or count objects by tag:
# all - true if ALL match
if all #row1.state == "BLUE":
set winner "BLUE"
# any - true if ANY matches
if any #enemies.health <= 0:
show EnemyDefeated
# count - number matching
if count #square.state == "NEUTRAL" == 0:
show GameOver
set remaining count #square.state == "NEUTRAL"
Capabilities
Capabilities are special behaviors you assign to objects. They extend what an object can do.
Pageable
Cycles through child items or a list of objects:
onEnter:
set Tutorial pageable[Step1, Step2, Step3]
onClick: # Next button
next Tutorial with fade
onClick: # Prev button
prev Tutorial with fade
Options:
next Tutorial # Go to next
next Tutorial wrap # Wrap to first at end
next Tutorial else goto "Done" # Action at end
prev Tutorial # Go to previous
prev Tutorial wrap # Wrap to last at start
wrap Tutorial # Jump to first
Auto-ordered: Use component children order:
set MyComponent pageable # Uses children in z-order
Movable (Dynamics)
Makes an object respond to keyboard input for movement:
set Player movable # Enable with defaults
set Player movable speed 0.5 # Custom speed
set Player movable style slide # Smooth grid movement
Jumpable (Dynamics)
Gives an object the ability to jump (Space key by default):
set Player jumpable # Enable with defaults
set Player jumpable height 0.8 # Stronger jump
set Player jumpable multijump 2 # Double jump
set Player jumpable key "w" # Custom jump key
Cell Properties
Set cell-level properties for physics and appearance:
# Physics
set Cell.gravity 0.5 # Downward gravity
set Cell.wind 0.3 # Wind magnitude
set Cell.windAngle 90 # Wind direction (degrees: 0=right, 90=down)
# Appearance
set Cell.backgroundColor "#001122"
set Cell.backgroundPattern "grid"
set Cell.patternColor "rgba(255,255,255,0.1)"
set Cell.patternScale 1.5
See Dynamics for full documentation on:
- Grid objects and attachment
- Movement styles (teleport, slide, hop)
- Grid position (cellX, cellY)
- Blocking and collision
- Movement state (moving, direction)
- Physics (gravity, wind, mass)
- Jump capability
Patterns & Debugging
Sequential Clicks
Multiple onClick blocks execute in sequence:
onClick:
show Step1
onClick:
show Step2
onClick:
show Step3
resetscript # Next click goes back to Step1
Log
Debug output to console:
log "Hello"
log "Score is" score
log turn, winner, "game state"
Open Console from View menu to see output.
Reference
Operators
| Operator | Meaning |
|---|---|
==, != |
Equals, not equals |
>, <, >=, <= |
Comparisons |
and, or, not |
Logic |
+, -, *, / |
Math |
%, mod |
Modulo (remainder) |
Precedence (highest to lowest):
()parentheses-(unary),not*,/,%,mod+,->,<,>=,<=,==,!=andor
Built-in Values
| Value | Description |
|---|---|
random |
Random number 0-1 (fresh each access) |
currentCell |
Current cell's label |
clicks |
Current click count (for sequential onClick) |
Built-in Functions
| Function | Returns |
|---|---|
visited("CellName") |
True if cell was visited |
visits("CellName") |
Number of times visited |
has-object("Name") |
True if cell has named object |
get("varName") |
Get variable value by name |
Math Functions
| Function | Returns |
|---|---|
floor(n) |
Rounds down to integer |
ceil(n) |
Rounds up to integer |
round(n) |
Rounds to nearest integer |
abs(n) |
Absolute value |
min(a, b) |
Smaller of two values |
max(a, b) |
Larger of two values |
cardinal(angle) |
Direction from angle: "right", "down", "left", "up" |
set gridX floor(x * 5)
set clamped min(max(value, 0), 100)
set distance abs(target - position)
set facing cardinal(Player.moveAngle) # "right", "down", "left", "up"
Array Functions
| Function | Returns |
|---|---|
length(arr) |
Number of elements in array |
range(n) |
Array [0, 1, 2, ..., n-1] |
shuffle(arr) |
New array with elements randomly reordered |
pick(arr) |
Random element from array |
set count length(items)
set indices range(5) # [0, 1, 2, 3, 4]
set randomOrder shuffle(items)
set randomItem pick(items)
Grid Functions
Grids have two separate systems - see Dynamics - Grid Systems for full documentation.
| System | Purpose | Functions |
|---|---|---|
| Occupancy | Track where objects are | isEmpty(), emptyCells() |
| Cell Data | Store metadata per cell | Grid.cell[x][y].prop, cellsWhere() |
Occupancy functions (check for objects):
| Function | Returns |
|---|---|
isEmpty(grid, x, y) |
True if no object at (x, y) |
emptyCells(grid) |
Array of {x, y} for cells without objects |
# Check if a cell has no object
if isEmpty("Grid1", 2, 3):
set piece.cellX 2
set piece.cellY 3
# Get all empty cells and pick randomly
set empty emptyCells("Grid1")
if length(empty) > 0:
set cell pick(empty)
set piece.cellX cell.x
set piece.cellY cell.y
Cell data functions (query stored data):
| Function | Returns |
|---|---|
cellsWhere(grid, prop, op, val) |
Array of {x, y} matching condition |
minimax(grid, prop, ai, human, depth, ...) |
Best column for AI move |
# Find cells with specific stored data
set redCells cellsWhere("Board", "owner", "==", "RED")
set revealed cellsWhere("Board", "revealed", "==", true)
# AI for Connect Four / Tic-Tac-Toe
set bestCol minimax("Board", "owner", "YELLOW", "RED", 5, 0, 4)
Grid Cell Data
Store arbitrary metadata per cell. This is separate from object occupancy - a cell can have data even with no object in it.
# Write data to cells
set Board.cell[0][0].owner "RED"
set Board.cell[x][y].revealed true
set Board.cell[col][row].mineCount 3
# Read data from cells
if Board.cell[0][0].owner == "RED":
show RedWins
set value Board.cell[x][y].mineCount
# Clear data
clear Board # Clear entire grid
clear Board.cell[2][3] # Clear one cell
Common mistake - confusing occupancy and data:
# WRONG: This just stores data, doesn't move any object!
set Board.cell[3][4].hasPiece true
# CORRECT: Move the actual object
set Piece.cellX 3
set Piece.cellY 4
# Now isEmpty("Board", 3, 4) is false
Often you need BOTH:
# Connect Four: piece falls (occupancy) AND we track owner (data)
onSpawn:
set self.snapToGrid "Board"
set self.cellX col # Occupancy
set Board.cell[col][row].owner currentPlayer # Data for win checking
See Dynamics - Grid Systems for complete documentation with examples.
Scriptable Properties
| Property | Type | Description |
|---|---|---|
x, y |
number | Position (0-1) |
width, height |
number | Size (0-1) |
visible |
boolean | Visibility |
clickable |
boolean | Accepts clicks (false = pass through) |
opacity |
number | Transparency (0-1) |
rotation |
number | Rotation angle (0-360) |
flipX, flipY |
boolean | Horizontal/vertical flip |
color.color |
color | Fill color (color layer) |
strokeColor |
color | Border color |
strokeWidth |
number | Border width |
content |
string | Text content |
textColor |
color | Text color |
fontSize |
number | Font size (rem) |
cornerRadius |
number | Corner radius |
shadowX, shadowY |
number | Shadow offset |
shadowBlur |
number | Shadow blur radius |
shadowColor |
color | Shadow color |
zIndex |
number | Layer order |
state |
string | Component state |
mass |
number | Object mass (affects wind/input, default: area) |
Movement Properties (Read-only)
These properties can be read but not set - they reflect the object's current dynamics state:
| Property | Type | Description |
|---|---|---|
cellX, cellY |
number | Grid position (0-based) |
moving |
boolean | Is object currently moving |
direction |
string | Current direction: "up", "down", "left", "right", "none" |
velocityX, velocityY |
number | Current velocity |
moveAngle |
number | Movement angle in degrees (0=right, 90=down, -1=stopped) |
moveSpeed |
number | Movement speed magnitude |
# Rotate sprite to face movement direction
onMove:
set self.state cardinal(self.moveAngle)
# Check if player is moving
if Player.moving:
show DustParticle
Grid Properties
For objects attached to a grid. See Dynamics for full documentation.
| Property | Type | Description |
|---|---|---|
snapToGrid |
string | Grid object name to snap to |
cellX |
number | Grid column position (0-based) |
cellY |
number | Grid row position (0-based) |
blocking |
boolean/string | Blocks other objects (can be expression) |
sensor |
boolean | Detects overlaps without blocking movement |
set Piece.snapToGrid "Grid1"
set Piece.cellX 2
set Piece.cellY 3
Fill Layer Properties
Objects and cells support multi-layer fills. Access layer properties by type:
# Gradient layer
set Sun.gradient.angle 180 # Rotation angle (0-360)
set Sky.gradient.angle Sky.gradient.angle + 1 # Animate rotation
# Pattern layer
set Floor.pattern.scale 2 # Pattern scale
set Grid.pattern.color "#ff0000" # Pattern color
# Noise layer
set Clouds.noise.scale 1.5 # Noise scale
set Clouds.noise.intensity 0.8 # Noise intensity (0-1)
# Color layer
set Box.color.color "#00ff00" # Base color
set Box.color.opacity 0.5 # Layer opacity
| Layer Type | Properties |
|---|---|
color |
color, opacity |
gradient |
angle, gradientType, opacity |
pattern |
pattern, color, scale, opacity |
noise |
noiseType, scale, intensity, monochrome, opacity |
image |
mode, scale, opacity |
Note: When multiple layers of the same type exist, the property targets the first matching layer.
Block Styles
Both colon-style and brace-style blocks work:
# Colon style (indentation-based)
onClick:
show Panel
wait 500
hide Panel
# Brace style
onClick {
show Panel
wait 500
hide Panel
}
# Single-line
onClick: show Panel
onClick { show Panel }
Examples
Tic-Tac-Toe
A classic tic-tac-toe game against a simple AI opponent. Click squares to place your mark (blue) and try to get three in a row before the computer (red).
Key features demonstrated:
- Component states for square visuals (NEUTRAL/BLUE/RED)
- Tags for grouping squares by row, column, and diagonal (
#row1,#col1,#dia1) - Custom actions for game logic (
checkWin,computerMove,reset) - Bulk operations with
foreach,first,all,any,count - Game variables for turn tracking and win detection
Download project file - Load this in Purl to explore the full implementation.
Chromastack (2048-style Puzzle)
A matching game where pieces on a grid move together in cardinal directions. When same-colored pieces collide, they merge and change color.
Cell script - animated scatter on enter:
onEnter:
hide #piece # Start invisible
set empty emptyCells("Grid1")
set shuffled shuffle(empty)
set i 0
foreach piece in #piece {
set piece.snapToGrid "Grid1"
set piece.movable true
set piece.blocking true
set cell shuffled[i]
set piece.cellX cell.x
set piece.cellY cell.y
show piece with scale 150 # Animated reveal
wait 100ms # Stagger each piece
set i i + 1
}
Key features demonstrated:
- Grid functions:
emptyCells()to find unoccupied cells - Array functions:
shuffle()for random placement, indexing witharr[i].property - Foreach with tags:
foreach piece in #piece - Loop variable property access:
set piece.cellX,piece.name - Loop variable show/hide:
show piece with scalefor animated reveal - Grid-based movement with
snapToGrid,movable,blocking