Making New Shapes
Every shape so far has existed from the moment the game starts — set up once in the editor, with its own tab and its own code file. Game.spawnShape(type, x, y, color, size) is different: it makes a brand-new shape while the game is running, and hands you back its id so you can control it afterwards:
export function update(self) {
if (self.isTouchingOnce("food")) {
const newId = Game.spawnShape("circle", self.getX(), self.getY(), "lime", 10);
self.tail.push(newId);
}
}
type is "circle" or "rectangle", color and size are optional (size is the radius for a circle, or the width and height for a rectangle). This is exactly what grows a snake's tail: every time it eats something, spawn one more segment and remember its id.
A spawned shape doesn't have any code of its own — there's no .js file behind it, so nothing moves it by itself. That's on purpose: it means whichever shape spawned it is fully in control, the same way it already controls any other shape:
export function update(self) {
for (const segmentId of self.tail) {
const segment = Game.getShape(segmentId);
if (segment) {
segment.setX(self.getX());
segment.setY(self.getY());
}
}
}
And since a spawned shape is a real shape, it works with everything you already know — including self.isTouching(id), which is how a snake would notice it just ran into its own tail:
export function update(self) {
for (const segmentId of self.tail) {
if (self.isTouching(segmentId)) {
UI.showMessage("Game Over!", "red");
}
}
}
Game.restart() (a page reload) makes every one of them disappear along with everything else resetting — you don't need to clean them up yourself.