game-maker / suite (push) Failing after 3m59s
A shader is an asset carrying a node graph (Texture color, Output, Property, Color, Add/Subtract/Multiply/Divide, Mix, Luminance) that compiles to the GLSL fragment of a Pixi filter. The Shader tab reuses the Behavior canvas; a sprite wears a shader through its Shader picker, and the rows below it are the graph's Property nodes. Property schema, resolution and deviation take the project because a sprite's rows now live in another asset. The built-in registry of the prototype is gone; "Adjust colors" ships as a graph in the fixture and the three example games. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
477 lines
20 KiB
JavaScript
477 lines
20 KiB
JavaScript
#!/usr/bin/env node
|
||
/**
|
||
* Writes the example game: a small jump and run built entirely from assets, behaviors and the
|
||
* node graph – no engine code is involved.
|
||
*
|
||
* Title screen (a container with a panel, texts and a clickable Start button)
|
||
* Level (ground, platforms, walls, ten coins, the player and the HUD labels)
|
||
* Coin (sprite plus trigger collider – placed ten times inside the level container)
|
||
* Win / Lose (containers shown when the coins are collected or the time is up)
|
||
*
|
||
* Screens are containers that are switched on and off with the `enabled` property – the same
|
||
* switch the inspector shows for every object. Everything else is nodes: gravity and jumping
|
||
* on the player, timer, coin counter and screen flow on the "Game" behavior.
|
||
*
|
||
* Usage: node tools/seed-jumprun.mjs [projectId] [baseUrl]
|
||
*/
|
||
import { edge, node, port, propertiesOf, sceneObject, variable, written } from './lib/builders.mjs';
|
||
import { PROJECTS, cliArgs, writeProject } from './lib/projects.mjs';
|
||
import { adjustColorsShader } from './lib/shaders.mjs';
|
||
|
||
const { projectId, baseUrl } = cliArgs('seed-jumprun.mjs', PROJECTS.jumpRun);
|
||
|
||
const SKY = 0x151821;
|
||
const STONE = 0x3c4454;
|
||
const GRASS = 0x4a7d3a;
|
||
const PLAYER = 0x63c74d;
|
||
const COIN = 0xf5c542;
|
||
const PANEL = 0x11141c;
|
||
const BUTTON = 0x63c74d;
|
||
const WHITE = 0xf2f5f7;
|
||
const HINT = 0x9aa3b2;
|
||
const BUTTON_TEXT = 0x10240a;
|
||
|
||
// The camera shows x −320…320, y −180…180; +y is down.
|
||
|
||
/** A solid box: the sprite plus a collider child of the same size. */
|
||
const solid = (id, x, y, w, h, color) => [
|
||
sceneObject(id, 'box', { x, y, overrides: { width: w, height: h, color } }),
|
||
sceneObject(`${id}-c`, 'solid', { parentId: id, overrides: { width: w, height: h } }),
|
||
];
|
||
|
||
const text = (
|
||
id,
|
||
x,
|
||
y,
|
||
content,
|
||
{ size = 28, color = WHITE, align = 'center', name = '', parentId = '', z = 1 } = {},
|
||
) =>
|
||
sceneObject(id, 'label', {
|
||
name,
|
||
x,
|
||
y,
|
||
z,
|
||
parentId,
|
||
overrides: { text: content, fontSize: size, color, align },
|
||
});
|
||
|
||
/** One placed coin: the container object; its content comes from the coin template. */
|
||
const coin = (id, x, y) => sceneObject(id, 'coin', { name: 'Coin', x, y });
|
||
|
||
/** The coin: a sprite with a trigger collider child, so it reports the touch without blocking. */
|
||
const coinTemplate = [
|
||
sceneObject('coin-body', 'coin-sprite', { z: 1 }),
|
||
sceneObject('coin-body-c', 'trigger', { parentId: 'coin-body' }),
|
||
];
|
||
|
||
const levelTemplate = [
|
||
...solid('ground', 0, 165, 660, 30, GRASS),
|
||
...solid('wall-l', -315, 0, 10, 360, STONE),
|
||
...solid('wall-r', 315, 0, 10, 360, STONE),
|
||
...solid('p1', -170, 90, 120, 16, STONE),
|
||
...solid('p2', -30, 30, 140, 16, STONE),
|
||
...solid('p3', 170, 60, 140, 16, STONE),
|
||
...solid('p4', 60, -50, 120, 16, STONE),
|
||
...solid('p5', -230, -30, 120, 16, STONE),
|
||
...solid('p6', 255, -60, 110, 16, STONE),
|
||
// Ten coins: three on the ground, one above each platform, one in the far corner.
|
||
coin('c1', -150, 128),
|
||
coin('c2', 0, 128),
|
||
coin('c3', 150, 128),
|
||
coin('c4', -170, 60),
|
||
coin('c5', -30, 0),
|
||
coin('c6', 170, 30),
|
||
coin('c7', 60, -80),
|
||
coin('c8', -230, -60),
|
||
coin('c9', 255, -90),
|
||
coin('c10', 290, 128),
|
||
sceneObject('player', 'player-sprite', { name: 'Player', x: -270, y: 125, z: 2 }),
|
||
sceneObject('player-c', 'solid', { parentId: 'player', overrides: { width: 30, height: 40 } }),
|
||
sceneObject('player-logic', 'player', { parentId: 'player', y: -60 }),
|
||
// HUD, top left and top right of the camera frame.
|
||
text('time-label', -300, -160, 'Time 30', { size: 22, align: 'left', name: 'TimeLabel', z: 3 }),
|
||
text('coin-label', 300, -160, 'Coins 0/10', { size: 22, align: 'right', name: 'CoinLabel', z: 3 }),
|
||
];
|
||
|
||
/** A full-frame panel with a headline, a line of help text and a button carrying a behavior. */
|
||
const screenTemplate = (prefix, headline, hint, buttonLabel, behaviorId) => [
|
||
sceneObject(`${prefix}-panel`, 'box', { overrides: { width: 640, height: 360, color: PANEL } }),
|
||
text(`${prefix}-title`, 0, -70, headline, { size: 52 }),
|
||
text(`${prefix}-hint`, 0, -10, hint, { size: 20, color: HINT }),
|
||
sceneObject(`${prefix}-button`, 'box', {
|
||
y: 80,
|
||
z: 1,
|
||
overrides: { width: 240, height: 60, color: BUTTON },
|
||
}),
|
||
text(`${prefix}-button-label`, 0, 0, buttonLabel, {
|
||
size: 26,
|
||
color: BUTTON_TEXT,
|
||
parentId: `${prefix}-button`,
|
||
}),
|
||
sceneObject(`${prefix}-button-logic`, behaviorId, { parentId: `${prefix}-button`, y: -60 }),
|
||
];
|
||
|
||
const templates = {
|
||
level: levelTemplate,
|
||
coin: coinTemplate,
|
||
title: screenTemplate('title', 'JUMP & RUN', 'Arrow keys to move, Space to jump.', 'START', 'start-button'),
|
||
win: screenTemplate('win', 'YOU WIN!', 'All ten coins collected in time.', 'PLAY AGAIN', 'restart-button'),
|
||
lose: screenTemplate('lose', 'TIME UP', 'The time is over - try again.', 'PLAY AGAIN', 'restart-button'),
|
||
};
|
||
|
||
/**
|
||
* "Player": gravity, walking, jumping and picking coins up – three strands.
|
||
*
|
||
* On update vy += gravity·dt, then Move(owner) by (axis·speed, vy)·dt with collisions on.
|
||
* Blocked vertically while falling means standing: vy back to 0, onGround true.
|
||
* On key down Space while onGround shoots vy up (negative is up).
|
||
* On collision Touching a coin adds one to the "Game" behavior's counter and destroys the coin –
|
||
* the collider reports its body (the sprite), the coin itself is the container above.
|
||
*/
|
||
const playerGraph = {
|
||
nodes: [
|
||
node('p-upd', 'OnUpdate', 0, 0),
|
||
node('p-owner', 'Owner', 0, 140),
|
||
node('p-axis', 'AxisInput', 0, 220, { left: 'ArrowLeft', right: 'ArrowRight', up: '', down: '' }),
|
||
node('p-speed', 'Variables', 0, 360, {}, [port('speed', 'number')]),
|
||
node('p-mulx', 'Multiply', 220, 300),
|
||
node('p-vy', 'Variables', 0, 440, {}, [port('vy', 'number')]),
|
||
node('p-grav', 'Variables', 0, 520, {}, [port('gravity', 'number')]),
|
||
node('p-mulg', 'Multiply', 220, 500),
|
||
node('p-addvy', 'Add', 420, 460),
|
||
node('p-setvy', 'Variables', 620, 0, {}, [written('vy', 'number')]),
|
||
node('p-vy2', 'Variables', 620, 340, {}, [port('vy', 'number')]),
|
||
node('p-move', 'Move', 840, 0, { collide: true }),
|
||
node('p-br', 'Branch', 1080, 0),
|
||
node('p-seq', 'Sequence', 1260, 0),
|
||
node('p-vy3', 'Variables', 1080, 260, {}, [port('vy', 'number')]),
|
||
node('p-cmp', 'Compare', 1260, 260, { op: '>=', b: 0 }),
|
||
node('p-ground', 'Variables', 1460, 0, {}, [written('onGround', 'boolean')]),
|
||
node('p-zero', 'Variables', 1460, 140, { vy: 0 }, [written('vy', 'number')]),
|
||
node('p-air', 'Variables', 1260, 400, { onGround: false }, [written('onGround', 'boolean')]),
|
||
|
||
node('p-key', 'OnKeyDown', 0, 700, { key: ' ' }),
|
||
node('p-og', 'Variables', 0, 820, {}, [port('onGround', 'boolean')]),
|
||
node('p-brj', 'Branch', 240, 700),
|
||
node('p-jump', 'Variables', 240, 880, {}, [port('jumpSpeed', 'number')]),
|
||
node('p-neg', 'Multiply', 440, 880, { b: -1 }),
|
||
node('p-setjump', 'Variables', 660, 700, {}, [written('vy', 'number')]),
|
||
|
||
node('p-col', 'OnCollisionEnter', 0, 1060),
|
||
node('p-iscoin', 'IsAsset', 260, 1180, { asset: 'coin-sprite' }),
|
||
node('p-brc', 'Branch', 480, 1060),
|
||
node('p-seqc', 'Sequence', 680, 1060),
|
||
node('p-game', 'ObjectByName', 680, 1280, { name: 'Game' }),
|
||
node('p-coins', 'Properties', 1080, 1060, propertiesOf('behavior', 'game'), [written('coins', 'number')]),
|
||
node('p-add1', 'Add', 1080, 1280, { b: 1 }),
|
||
node('p-coinroot', 'Parent', 1080, 1420),
|
||
node('p-destroy', 'Destroy', 1280, 1200),
|
||
],
|
||
edges: [
|
||
edge('pe1', 'p-upd', 'exec', 'p-setvy', 'exec'),
|
||
edge('pe2', 'p-vy', 'vy', 'p-addvy', 'a'),
|
||
edge('pe3', 'p-grav', 'gravity', 'p-mulg', 'a'),
|
||
edge('pe4', 'p-upd', 'dt', 'p-mulg', 'b'),
|
||
edge('pe5', 'p-mulg', 'result', 'p-addvy', 'b'),
|
||
edge('pe6', 'p-addvy', 'result', 'p-setvy', 'vy'),
|
||
edge('pe7', 'p-setvy', 'exec', 'p-move', 'exec'),
|
||
edge('pe8', 'p-owner', 'object', 'p-move', 'object'),
|
||
edge('pe9', 'p-axis', 'x', 'p-mulx', 'a'),
|
||
edge('pe10', 'p-speed', 'speed', 'p-mulx', 'b'),
|
||
edge('pe11', 'p-mulx', 'result', 'p-move', 'dx'),
|
||
edge('pe12', 'p-vy2', 'vy', 'p-move', 'dy'),
|
||
edge('pe13', 'p-upd', 'dt', 'p-move', 'dt'),
|
||
edge('pe14', 'p-move', 'exec', 'p-br', 'exec'),
|
||
edge('pe15', 'p-move', 'blockedY', 'p-br', 'condition'),
|
||
edge('pe16', 'p-br', 'then', 'p-seq', 'exec'),
|
||
edge('pe17', 'p-seq', 'a', 'p-ground', 'exec'),
|
||
edge('pe18', 'p-vy3', 'vy', 'p-cmp', 'a'),
|
||
edge('pe19', 'p-cmp', 'result', 'p-ground', 'onGround'),
|
||
edge('pe20', 'p-seq', 'b', 'p-zero', 'exec'),
|
||
edge('pe21', 'p-br', 'else', 'p-air', 'exec'),
|
||
|
||
edge('pe22', 'p-key', 'exec', 'p-brj', 'exec'),
|
||
edge('pe23', 'p-og', 'onGround', 'p-brj', 'condition'),
|
||
edge('pe24', 'p-brj', 'then', 'p-setjump', 'exec'),
|
||
edge('pe25', 'p-jump', 'jumpSpeed', 'p-neg', 'a'),
|
||
edge('pe26', 'p-neg', 'result', 'p-setjump', 'vy'),
|
||
|
||
edge('pe27', 'p-col', 'exec', 'p-brc', 'exec'),
|
||
edge('pe28', 'p-col', 'other', 'p-iscoin', 'object'),
|
||
edge('pe29', 'p-iscoin', 'result', 'p-brc', 'condition'),
|
||
edge('pe30', 'p-brc', 'then', 'p-seqc', 'exec'),
|
||
edge('pe31', 'p-seqc', 'a', 'p-coins', 'exec'),
|
||
edge('pe32', 'p-game', 'object', 'p-coins', 'object'),
|
||
edge('pe33', 'p-coins', 'coins', 'p-add1', 'a'),
|
||
edge('pe34', 'p-add1', 'result', 'p-coins', 'coins'),
|
||
edge('pe36', 'p-seqc', 'b', 'p-destroy', 'exec'),
|
||
edge('pe37', 'p-col', 'other', 'p-coinroot', 'object'),
|
||
edge('pe38', 'p-coinroot', 'parent', 'p-destroy', 'object'),
|
||
],
|
||
};
|
||
|
||
/** Switches a screen (a container) on or off. */
|
||
const screen = (id, x, y, enabled) =>
|
||
node(id, 'Properties', x, y, { ...propertiesOf('container'), enabled }, [written('enabled', 'boolean')]);
|
||
|
||
/**
|
||
* "Game": owns the run. On start it shows the title and hides everything else; while `running`
|
||
* it counts the clock down, writes both HUD labels and decides win (coins reached) or loss (time
|
||
* gone) by switching the screens.
|
||
*/
|
||
const gameGraph = {
|
||
nodes: [
|
||
node('g-start', 'OnStart', 0, 0),
|
||
node('g-title', 'ObjectByName', 0, 140, { name: 'Title' }),
|
||
node('g-level', 'ObjectByName', 0, 220, { name: 'Level' }),
|
||
node('g-win', 'ObjectByName', 0, 300, { name: 'Win' }),
|
||
node('g-lose', 'ObjectByName', 0, 380, { name: 'Lose' }),
|
||
screen('g-on1', 260, 0, true),
|
||
screen('g-off1', 480, 0, false),
|
||
screen('g-off2', 700, 0, false),
|
||
screen('g-off3', 920, 0, false),
|
||
|
||
node('g-upd', 'OnUpdate', 0, 520),
|
||
node('g-running', 'Variables', 0, 640, {}, [port('running', 'boolean')]),
|
||
node('g-brrun', 'Branch', 220, 520),
|
||
node('g-time', 'Variables', 220, 700, {}, [port('timeLeft', 'number')]),
|
||
node('g-sub', 'Subtract', 420, 700),
|
||
node('g-max', 'Max', 600, 700, { b: 0 }),
|
||
node('g-settime', 'Variables', 440, 520, {}, [written('timeLeft', 'number')]),
|
||
|
||
node('g-timelabel', 'ObjectByName', 440, 860, { name: 'TimeLabel' }),
|
||
node('g-time2', 'Variables', 440, 940, {}, [port('timeLeft', 'number')]),
|
||
node('g-ceil', 'Ceil', 640, 940),
|
||
node('g-join', 'Join', 840, 940, { a: 'Time ' }),
|
||
node('g-settext', 'Properties', 700, 520, propertiesOf('text'), [written('text', 'text')]),
|
||
|
||
node('g-coinlabel', 'ObjectByName', 700, 1080, { name: 'CoinLabel' }),
|
||
node('g-coins', 'Variables', 700, 1160, {}, [port('coins', 'number')]),
|
||
node('g-totext2', 'ToText', 900, 1160, { decimals: 0 }),
|
||
node('g-join2', 'Join', 1080, 1160, { a: 'Coins ' }),
|
||
node('g-join3', 'Join', 1260, 1160, { b: '/10' }),
|
||
node('g-settext2', 'Properties', 960, 520, propertiesOf('text'), [written('text', 'text')]),
|
||
|
||
node('g-needed', 'Variables', 960, 700, {}, [port('needed', 'number')]),
|
||
node('g-cmpwin', 'Compare', 1160, 700, { op: '>=' }),
|
||
node('g-brwin', 'Branch', 1220, 520),
|
||
node('g-stop1', 'Variables', 1420, 440, { running: false }, [written('running', 'boolean')]),
|
||
screen('g-leveloff1', 1620, 440, false),
|
||
screen('g-winon', 1820, 440, true),
|
||
|
||
node('g-cmplose', 'Compare', 1160, 860, { op: '<=', b: 0 }),
|
||
node('g-brlose', 'Branch', 1420, 640),
|
||
node('g-stop2', 'Variables', 1620, 640, { running: false }, [written('running', 'boolean')]),
|
||
screen('g-leveloff2', 1820, 640, false),
|
||
screen('g-loseon', 2020, 640, true),
|
||
],
|
||
edges: [
|
||
edge('ge1', 'g-start', 'exec', 'g-on1', 'exec'),
|
||
edge('ge2', 'g-title', 'object', 'g-on1', 'object'),
|
||
edge('ge3', 'g-on1', 'exec', 'g-off1', 'exec'),
|
||
edge('ge4', 'g-level', 'object', 'g-off1', 'object'),
|
||
edge('ge5', 'g-off1', 'exec', 'g-off2', 'exec'),
|
||
edge('ge6', 'g-win', 'object', 'g-off2', 'object'),
|
||
edge('ge7', 'g-off2', 'exec', 'g-off3', 'exec'),
|
||
edge('ge8', 'g-lose', 'object', 'g-off3', 'object'),
|
||
|
||
edge('ge9', 'g-upd', 'exec', 'g-brrun', 'exec'),
|
||
edge('ge10', 'g-running', 'running', 'g-brrun', 'condition'),
|
||
edge('ge11', 'g-brrun', 'then', 'g-settime', 'exec'),
|
||
edge('ge12', 'g-time', 'timeLeft', 'g-sub', 'a'),
|
||
edge('ge13', 'g-upd', 'dt', 'g-sub', 'b'),
|
||
edge('ge14', 'g-sub', 'result', 'g-max', 'a'),
|
||
edge('ge15', 'g-max', 'result', 'g-settime', 'timeLeft'),
|
||
|
||
edge('ge16', 'g-settime', 'exec', 'g-settext', 'exec'),
|
||
edge('ge17', 'g-timelabel', 'object', 'g-settext', 'object'),
|
||
edge('ge18', 'g-time2', 'timeLeft', 'g-ceil', 'value'),
|
||
edge('ge19', 'g-ceil', 'result', 'g-join', 'b'),
|
||
edge('ge20', 'g-join', 'text', 'g-settext', 'text'),
|
||
|
||
edge('ge21', 'g-settext', 'exec', 'g-settext2', 'exec'),
|
||
edge('ge22', 'g-coinlabel', 'object', 'g-settext2', 'object'),
|
||
edge('ge23', 'g-coins', 'coins', 'g-totext2', 'value'),
|
||
edge('ge24', 'g-totext2', 'text', 'g-join2', 'b'),
|
||
edge('ge25', 'g-join2', 'text', 'g-join3', 'a'),
|
||
edge('ge26', 'g-join3', 'text', 'g-settext2', 'text'),
|
||
|
||
edge('ge27', 'g-settext2', 'exec', 'g-brwin', 'exec'),
|
||
edge('ge28', 'g-coins', 'coins', 'g-cmpwin', 'a'),
|
||
edge('ge29', 'g-needed', 'needed', 'g-cmpwin', 'b'),
|
||
edge('ge30', 'g-cmpwin', 'result', 'g-brwin', 'condition'),
|
||
edge('ge31', 'g-brwin', 'then', 'g-stop1', 'exec'),
|
||
edge('ge32', 'g-stop1', 'exec', 'g-leveloff1', 'exec'),
|
||
edge('ge33', 'g-level', 'object', 'g-leveloff1', 'object'),
|
||
edge('ge34', 'g-leveloff1', 'exec', 'g-winon', 'exec'),
|
||
edge('ge35', 'g-win', 'object', 'g-winon', 'object'),
|
||
|
||
edge('ge36', 'g-brwin', 'else', 'g-brlose', 'exec'),
|
||
edge('ge37', 'g-time2', 'timeLeft', 'g-cmplose', 'a'),
|
||
edge('ge38', 'g-cmplose', 'result', 'g-brlose', 'condition'),
|
||
edge('ge39', 'g-brlose', 'then', 'g-stop2', 'exec'),
|
||
edge('ge40', 'g-stop2', 'exec', 'g-leveloff2', 'exec'),
|
||
edge('ge41', 'g-level', 'object', 'g-leveloff2', 'object'),
|
||
edge('ge42', 'g-leveloff2', 'exec', 'g-loseon', 'exec'),
|
||
edge('ge43', 'g-lose', 'object', 'g-loseon', 'object'),
|
||
],
|
||
};
|
||
|
||
/** "Start button": a click resets the counters on the Game behavior and swaps title for level. */
|
||
const startButtonGraph = {
|
||
nodes: [
|
||
node('s-click', 'OnClick', 0, 0),
|
||
node('s-game', 'ObjectByName', 0, 160, { name: 'Game' }),
|
||
node('s-title', 'ObjectByName', 0, 240, { name: 'Title' }),
|
||
node('s-level', 'ObjectByName', 0, 320, { name: 'Level' }),
|
||
node(
|
||
's-reset',
|
||
'Properties',
|
||
240,
|
||
0,
|
||
{ ...propertiesOf('behavior', 'game'), coins: 0, timeLeft: 30, running: true },
|
||
[written('coins', 'number'), written('timeLeft', 'number'), written('running', 'boolean')],
|
||
),
|
||
screen('s-hide', 900, 0, false),
|
||
screen('s-show', 1120, 0, true),
|
||
],
|
||
edges: [
|
||
edge('se1', 's-click', 'exec', 's-reset', 'exec'),
|
||
edge('se2', 's-game', 'object', 's-reset', 'object'),
|
||
edge('se7', 's-reset', 'exec', 's-hide', 'exec'),
|
||
edge('se8', 's-title', 'object', 's-hide', 'object'),
|
||
edge('se9', 's-hide', 'exec', 's-show', 'exec'),
|
||
edge('se10', 's-level', 'object', 's-show', 'object'),
|
||
],
|
||
};
|
||
|
||
/** "Restart button": one click, one node – the scene starts over from its saved state. */
|
||
const restartButtonGraph = {
|
||
nodes: [node('r-click', 'OnClick', 0, 0), node('r-restart', 'RestartGame', 260, 0)],
|
||
edges: [edge('re1', 'r-click', 'exec', 'r-restart', 'exec')],
|
||
};
|
||
|
||
const project = {
|
||
formatVersion: 2,
|
||
id: projectId,
|
||
name: 'Jump and Run',
|
||
activeSceneId: 'scene1',
|
||
assets: [
|
||
{
|
||
id: 'scene1',
|
||
type: 'scene',
|
||
name: 'Level 1',
|
||
props: { backgroundColor: SKY },
|
||
objects: [
|
||
sceneObject('L', 'level', { name: 'Level' }),
|
||
// All three screens sit on the camera frame; the Game behavior decides on `On start` which
|
||
// one is up. Editor offsets pull them apart in the scene view so they do not cover the level.
|
||
sceneObject('T', 'title', {
|
||
name: 'Title',
|
||
z: 10,
|
||
overrides: { enabled: true },
|
||
editorOffset: { x: -750, y: -500 },
|
||
}),
|
||
sceneObject('W', 'win', {
|
||
name: 'Win',
|
||
z: 10,
|
||
overrides: { enabled: true },
|
||
editorOffset: { x: 0, y: -500 },
|
||
}),
|
||
sceneObject('B', 'lose', {
|
||
name: 'Lose',
|
||
z: 10,
|
||
overrides: { enabled: true },
|
||
editorOffset: { x: 750, y: -500 },
|
||
}),
|
||
sceneObject('ocam', 'cam', { name: 'Main Camera' }),
|
||
sceneObject('ogame', 'game', { name: 'Game', x: -200, y: -220 }),
|
||
],
|
||
},
|
||
{ id: 'cam', type: 'camera', name: 'Main Camera', props: { width: 640, height: 360, zoom: 1 } },
|
||
// The boxes wear the shader at its neutral values: the pass runs, the picture stays the same.
|
||
{
|
||
id: 'box',
|
||
type: 'sprite',
|
||
name: 'Box',
|
||
props: { width: 100, height: 100, color: STONE, shader: 'adjust-colors' },
|
||
},
|
||
adjustColorsShader(),
|
||
{
|
||
id: 'player-sprite',
|
||
type: 'sprite',
|
||
name: 'Player Sprite',
|
||
props: { width: 30, height: 40, color: PLAYER },
|
||
},
|
||
{ id: 'coin-sprite', type: 'sprite', name: 'Coin Sprite', props: { width: 24, height: 24, color: COIN } },
|
||
{
|
||
id: 'label',
|
||
type: 'text',
|
||
name: 'Label',
|
||
props: { text: 'Text', fontSize: 28, color: WHITE, align: 'center' },
|
||
},
|
||
{
|
||
id: 'solid',
|
||
type: 'collider',
|
||
name: 'Solid Collider',
|
||
props: { shape: 'box', width: 100, height: 100, radius: 50, isTrigger: false },
|
||
},
|
||
{
|
||
id: 'trigger',
|
||
type: 'collider',
|
||
name: 'Coin Trigger',
|
||
props: { shape: 'box', width: 24, height: 24, radius: 12, isTrigger: true },
|
||
},
|
||
{
|
||
id: 'player',
|
||
type: 'behavior',
|
||
name: 'Player',
|
||
props: { speed: 170, jumpSpeed: 480, gravity: 1200, vy: 0, onGround: false },
|
||
variables: [
|
||
variable('speed', 'number', 0),
|
||
variable('jumpSpeed', 'number', 0),
|
||
variable('gravity', 'number', 0),
|
||
variable('vy', 'number', 0),
|
||
variable('onGround', 'boolean', false),
|
||
],
|
||
graph: playerGraph,
|
||
},
|
||
{
|
||
id: 'game',
|
||
type: 'behavior',
|
||
name: 'Game',
|
||
props: { timeLeft: 30, coins: 0, needed: 10, running: false },
|
||
variables: [
|
||
variable('timeLeft', 'number', 30),
|
||
variable('coins', 'number', 0),
|
||
variable('needed', 'number', 10),
|
||
variable('running', 'boolean', false),
|
||
],
|
||
graph: gameGraph,
|
||
},
|
||
{
|
||
id: 'start-button',
|
||
type: 'behavior',
|
||
name: 'Start Button',
|
||
props: {},
|
||
variables: [],
|
||
graph: startButtonGraph,
|
||
},
|
||
{
|
||
id: 'restart-button',
|
||
type: 'behavior',
|
||
name: 'Restart Button',
|
||
props: {},
|
||
variables: [],
|
||
graph: restartButtonGraph,
|
||
},
|
||
{ id: 'coin', type: 'container', name: 'Coin', props: {}, objects: templates.coin },
|
||
{ id: 'level', type: 'container', name: 'Level', props: {}, objects: templates.level },
|
||
{ id: 'title', type: 'container', name: 'Title screen', props: {}, objects: templates.title },
|
||
{ id: 'win', type: 'container', name: 'Win screen', props: {}, objects: templates.win },
|
||
{ id: 'lose', type: 'container', name: 'Lose screen', props: {}, objects: templates.lose },
|
||
],
|
||
};
|
||
|
||
await writeProject(projectId, baseUrl, project);
|