04. Patterns
This page is for the question “how do I build the usual game thing?” rather than “what does this word take?” For exact arity, use the Machine board reference .
The Standard Game Loop
Small Froth Machine games usually settle into the same rhythm:
setupinitializes the display and starting stateupdatereads input and advances the modeldrawturns the current state into pixelsframedoes one update-draw-show-sleep cyclerunrepeats frames until you stop
player.x is 5
player.y is 3
frame.ms is 60
game.setup is fn [
matrix.init:;
matrix.brightness!: 0;
set player.x to 5;
set player.y to 3
]
game.update is fn [
when joy.left?: [ set player.x to player.x - 1 ];
when joy.right?: [ set player.x to player.x + 1 ];
when joy.up?: [ set player.y to player.y - 1 ];
when joy.down?: [ set player.y to player.y + 1 ]
]
game.draw is fn [
grid.clear:;
grid.set: player.x, player.y, true;
grid.show:
]
game.frame is fn [
game.update:;
game.draw:;
ms: frame.ms
]
game.run is fn [
while joy.click?: == false [
game.frame:
]
]
For a real game, the next things you usually add are bounds, collision,
scoring, mode state, and an exit condition such as joy.click?:.
Joystick Movement
The joystick words return booleans, so movement is usually a set of small conditional updates.
to movePlayer [
when joy.left?: [ set player.x to player.x - 1 ];
when joy.right?: [ set player.x to player.x + 1 ];
when joy.up?: [ set player.y to player.y - 1 ];
when joy.down?: [ set player.y to player.y + 1 ]
]
Stay On Screen
Clamp coordinates when the player should stop at the edge:
to clampPlayer [
set player.x to clamp: player.x, 0, (grid.width - 1);
set player.y to clamp: player.y, 0, (grid.height - 1)
]
Wrap coordinates when leaving one side should re-enter from the other:
to wrapPlayer [
set player.x to wrap: player.x, grid.width;
set player.y to wrap: player.y, grid.height
]
Map A Knob To A Position
The friendly knob words return 0..100. Scale that to the range you need.
to scaleKnob with percent, max [
percent * max / 100
]
to readKnobPosition [
set player.x to scaleKnob: knob.left:, (grid.width - 1);
set player.y to scaleKnob: knob.right:, (grid.height - 1)
]
Point Collision
Use point collision for a target pixel, pickup, or single-cell obstacle.
to hitPoint? with ax, ay, bx, by [
ax == bx and ay == by
]
when hitPoint?: player.x, player.y, target.x, target.y [
set score to score + 1
]
Rectangle Collision
Use rectangle collision for paddles, walls, zones, and larger sprites.
to insideRect? with px, py, rx, ry, rw, rh [
px >= rx and px < (rx + rw) and py >= ry and py < (ry + rh)
]
when insideRect?: player.x, player.y, wall.x, wall.y, wall.w, wall.h [
set game.over to true
]
Random Targets
Pick a new target with random.below: and the grid dimensions.
to placeTarget [
set target.x to random.below: grid.width;
set target.y to random.below: grid.height
]
Frame Timing
Use one top-level value for the frame delay so the whole game can speed up or slow down in one place.
frame.ms is 80
game.frame is fn [
game.update:;
game.draw:;
ms: frame.ms
]
You can map a knob to timing too:
set frame.ms to 20 + knob.left:
Staged Animation
For most sketches, grid.clear:, draw, then grid.show: is enough. Reach for
the tm1629.next* words when you want to build the whole next frame before it
becomes visible.
tm1629.nextClear:
tm1629.nextPixel!: 2, 3, true
tm1629.nextPixel!: 3, 3, true
tm1629.commitNext:
grid.show:
Built-In Pong
The board ships with a Pong-shaped demo in the base library.
demo.pong.setup:
demo.pong.run:
What it does:
- the left knob controls the left paddle
- the right knob controls the right paddle
- the ball advances one frame at a time
joy.click?exits the run loop
Inspect the shape:
show @demo.pong.setup
show @demo.pong.update
show @demo.pong.draw
show @demo.pong.frame
show @demo.pong.run
The Pong demo is worth studying because it uses the same setup, update,
draw, frame, and run structure.
Game Of Life
The board also has a built-in display helper for Conway’s Game of Life:
tm1629.lifeStep.
Seed a glider like this:
matrix.init:
grid.clear:
grid.set: 2, 1, true
grid.set: 3, 2, true
grid.set: 1, 3, true
grid.set: 2, 3, true
grid.set: 3, 3, true
grid.show:
Then step it:
repeat 24 [
tm1629.lifeStep:;
grid.show:;
ms: 120
]
That example shows why the advanced tm1629.* layer exists: sometimes the
display operation is more than drawing one shape.