The aspect button now carries two offers, so it becomes a menu: fit one side to the texture ratio, or set both sides to the exact pixel size as a single undo step. Dropping a row on the outer quarter of another row inserts it into that gap instead of parenting into it, reordering among the siblings there. Order is the stored order, so ReorderObjectCommand is a splice; equal z falls back to tree order, which makes this the way to draw one object in front of another. Rejected on container content like the other structural edits. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
102 lines
3.7 KiB
JavaScript
102 lines
3.7 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Writes the fixture scene tools/e2e.mjs asserts against: two top-level crates with a collider
|
|
* child each, a camera, the Mover behavior owning the red crate and a Spawner. The objects are
|
|
* uniquely named and positioned exactly where the checks expect them.
|
|
*
|
|
* Usage: PATH=/opt/node22/bin:$PATH node tools/seed-e2e.mjs [projectId] [baseUrl]
|
|
*/
|
|
import { edge, node, sceneObject, variable } from './lib/builders.mjs';
|
|
import { PROJECTS, cliArgs, writeProject } from './lib/projects.mjs';
|
|
|
|
const { projectId, baseUrl } = cliArgs('seed-e2e.mjs', PROJECTS.fixture);
|
|
|
|
/**
|
|
* "Mover" in five nodes: every frame, move the owner along the normalized arrow-key axis,
|
|
* scaled by the `speed` variable per second (On update's dt feeds the dt input). "Collide with
|
|
* solids" stops the owner's solid colliders at other solid colliders.
|
|
*/
|
|
const moverGraph = {
|
|
nodes: [
|
|
node('n-update', 'OnUpdate', 0, 0),
|
|
node('n-owner', 'Owner', 0, 120),
|
|
node('n-axis', 'AxisInput', 0, 200),
|
|
node('n-speed', 'GetVariable', 0, 400, { variable: 'speed' }),
|
|
node('n-move', 'Move', 340, 0, { collide: true }),
|
|
],
|
|
edges: [
|
|
edge('e1', 'n-update', 'exec', 'n-move', 'exec'),
|
|
edge('e6', 'n-update', 'dt', 'n-move', 'dt'),
|
|
edge('e2', 'n-owner', 'object', 'n-move', 'object'),
|
|
edge('e3', 'n-axis', 'x', 'n-move', 'dx'),
|
|
edge('e4', 'n-axis', 'y', 'n-move', 'dy'),
|
|
edge('e5', 'n-speed', 'value', 'n-move', 'scale'),
|
|
],
|
|
};
|
|
|
|
/** "Spawner": pressing Space places a new Crate at world (0, 150) and logs its name. */
|
|
const spawnerGraph = {
|
|
nodes: [
|
|
node('s-key', 'OnKeyDown', 0, 0, { key: ' ' }),
|
|
node('s-spawn', 'Spawn', 260, 0, { asset: 'crate', y: 150 }),
|
|
node('s-log', 'Log', 520, 0),
|
|
],
|
|
edges: [
|
|
edge('s-e1', 's-key', 'exec', 's-spawn', 'exec'),
|
|
edge('s-e2', 's-spawn', 'exec', 's-log', 'exec'),
|
|
edge('s-e3', 's-spawn', 'object', 's-log', 'message'),
|
|
],
|
|
};
|
|
|
|
const project = {
|
|
formatVersion: 2,
|
|
id: projectId,
|
|
name: 'E2E fixture',
|
|
activeSceneId: 'scene1',
|
|
assets: [
|
|
{
|
|
id: 'scene1',
|
|
type: 'scene',
|
|
name: 'Level 1',
|
|
props: { backgroundColor: 0x22252c },
|
|
objects: [
|
|
sceneObject('o1', 'crate', { x: -150 }),
|
|
sceneObject('o2', 'crate', {
|
|
name: 'Blue crate',
|
|
x: 120,
|
|
y: -80,
|
|
overrides: { width: 140, color: 0x4488ff },
|
|
}),
|
|
sceneObject('o3', 'cam'),
|
|
// The Mover is parented to the red crate (its owner); its position is local to it.
|
|
sceneObject('o4', 'mover', { y: 110, parentId: 'o1' }),
|
|
// Both crates carry the shared collider as a centred child; the blue one is widened to
|
|
// match its 140-wide sprite override.
|
|
sceneObject('oc1', 'collider', { parentId: 'o1' }),
|
|
sceneObject('oc2', 'collider', { parentId: 'o2', overrides: { width: 140 } }),
|
|
// Top-level spawner (no owner) left of the red crate: Space spawns a crate.
|
|
sceneObject('o5', 'spawner', { x: -300 }),
|
|
],
|
|
},
|
|
{ id: 'crate', type: 'sprite', name: 'Crate', props: { width: 100, height: 60, color: 0xe0553f } },
|
|
{ id: 'cam', type: 'camera', name: 'Main camera', props: { width: 640, height: 360, zoom: 1 } },
|
|
{
|
|
id: 'collider',
|
|
type: 'collider',
|
|
name: 'Crate collider',
|
|
props: { shape: 'box', width: 100, height: 60, radius: 50, isTrigger: false },
|
|
},
|
|
{
|
|
id: 'mover',
|
|
type: 'behavior',
|
|
name: 'Mover',
|
|
props: { speed: 200 },
|
|
variables: [variable('speed', 'number', 0)],
|
|
graph: moverGraph,
|
|
},
|
|
{ id: 'spawner', type: 'behavior', name: 'Spawner', props: {}, variables: [], graph: spawnerGraph },
|
|
],
|
|
};
|
|
|
|
await writeProject(projectId, baseUrl, project);
|