Add a node that names the collision layer of an object (#211)
The issue asked for three nodes: Set layer, Set collides with and Layer of. A probe showed that the finding behind the first two is outdated. The Properties node of type Collider already writes both settings while the game runs, and both collision systems see the change at once. As agreed, only Layer of is built, and writing stays with Properties. - Layer of is a data node with the input object and the outputs name and number. The name is what the project calls the layer. - NodeContext.collisionLayerOf answers the number, and the new field collisionLayers on GraphLookup brings the names to the node. - layerHolderOf in solids.ts says whose layer counts. An object stands for its first collider child, a collider or tilemap for itself. - Decided here: a collider that is switched off counts too, because its layer is set either way. - Decided here: without a collider the name is empty and the number is -1. - The node is a read, not a run, so it answers at the moment it is asked. Right after a Set in the same flow it names the new layer. - The output number was not asked for. It fits the layer port of Properties as it is. Changing the layer during a run had no test at all so far. Five tests now hold it in both systems. Tests: - Gate: 198 files, 2929 tests (2914 + 15), exit 0 - e2e 1-6: 285 checks passed (06: +2 for block 6z-x) - Five unit tests for the change during a run: a mover is blocked, passes and is blocked again, a ray with a layer filter follows the change, and a dynamic body falls through the ground in Play - Six unit tests for Layer of, three against each system, and four cases in the node catalog - 6z-x finds the node in the palette, checks its ports and logs "Ghost" and "1" for the owner's collider in a Play run - Two counter-probes for the change during a run fail 1 and 4 tests, and only new ones, so those places were unguarded before - Six counter-probes for Layer of fail 2 to 3 tests each. All eight ran on stein2, not in the container. - The browser counter-probe fails 6z-x with "Layer 1" and "1" - The e2e sum of 1619 is calculated (1617 measured in CI run 276, plus 2). Neither the full suite nor the smokes ran for this state. - Not looked at in the browser: that the layer port of Properties shows the same picker as the inspector. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d39bf83ea9
commit
3e8c88c66d
12 files changed
+329
-7
No files matched your search
@@ -1107,6 +1107,7 @@ export async function graph(t) {
|
||||
await pluralLookups();
|
||||
await eventOrder();
|
||||
await animationPlaying();
|
||||
await layerOf();
|
||||
|
||||
await mutate(`__gm.state.select({ kind: 'asset', assetId: 'mover' });`);
|
||||
|
||||
@@ -1831,4 +1832,101 @@ export async function graph(t) {
|
||||
scene.objects = scene.objects.filter((o) => !o.id.startsWith('ap-'));
|
||||
`);
|
||||
}
|
||||
|
||||
// 6z-x. Layer of (#211): typed into the palette it arrives with its ports. In a run it names the
|
||||
// layer of the owner's collider as the project calls it, and its number.
|
||||
async function layerOf() {
|
||||
await mutate(`
|
||||
const p = __gm.state.project;
|
||||
window.__layersBefore = p.collisionLayers;
|
||||
p.collisionLayers = ['Default', 'Ghost'];
|
||||
p.assets.push(
|
||||
{
|
||||
id: 'lo-col',
|
||||
type: 'collider',
|
||||
name: 'Ghost collider',
|
||||
props: { shape: 'box', width: 20, height: 20, radius: 10, isTrigger: false, layer: 1 },
|
||||
},
|
||||
{
|
||||
id: 'loy',
|
||||
type: 'behavior',
|
||||
name: 'Layer asker',
|
||||
props: { enabled: true, runInEditor: false },
|
||||
variables: [],
|
||||
graph: { nodes: [], edges: [] },
|
||||
},
|
||||
);
|
||||
__gm.state.select({ kind: 'asset', assetId: 'loy' });
|
||||
`);
|
||||
await showTab('graph');
|
||||
const spot = await json('__gm.graph.emptySpot()');
|
||||
await click(spot.x, spot.y, 'right');
|
||||
await typeText('layer of');
|
||||
await press('Enter');
|
||||
const asker = (await asset('loy')).graph.nodes[0];
|
||||
const types = asker ? await json(`__gm.graph.portTypes(${JSON.stringify(asker.id)})`) : null;
|
||||
check(
|
||||
'typing "layer of" into the palette adds the node: object in, name and number out',
|
||||
asker?.type === 'LayerOf' &&
|
||||
JSON.stringify(Object.keys(types?.inputs ?? {})) === JSON.stringify(['object']) &&
|
||||
JSON.stringify(types?.outputs) === JSON.stringify({ name: 'string', number: 'number' }),
|
||||
JSON.stringify({ asker, types }),
|
||||
);
|
||||
if (!asker) return;
|
||||
const id = JSON.stringify(asker.id);
|
||||
await mutate(`
|
||||
const p = __gm.state.project;
|
||||
const behavior = p.assets.find((a) => a.id === 'loy');
|
||||
const node = (id, type, x, y, params = {}) => ({ id, type, x, y, params });
|
||||
const edge = (id, from, fromPort, to, toPort) =>
|
||||
({ id, from: { node: from, port: fromPort }, to: { node: to, port: toPort } });
|
||||
behavior.graph.nodes.push(
|
||||
node('lo-start', 'OnStart', -300, -200),
|
||||
node('lo-owner', 'Owner', -560, 0),
|
||||
node('lo-name', 'Log', 420, -200),
|
||||
node('lo-number', 'Log', 700, -200),
|
||||
);
|
||||
behavior.graph.edges.push(
|
||||
edge('lo-e1', 'lo-start', 'exec', 'lo-name', 'exec'),
|
||||
edge('lo-e2', 'lo-name', 'exec', 'lo-number', 'exec'),
|
||||
edge('lo-e3', 'lo-owner', 'object', ${id}, 'object'),
|
||||
edge('lo-e4', ${id}, 'name', 'lo-name', 'message'),
|
||||
edge('lo-e5', ${id}, 'number', 'lo-number', 'message'),
|
||||
);
|
||||
const scene = __gm.state.activeScene;
|
||||
const sprite = p.assets.find((a) => a.type === 'sprite');
|
||||
for (const [id, assetId, parentId, name, x] of [
|
||||
['lo-target', sprite.id, '', 'Ghost 6', -6000],
|
||||
['lo-part', 'lo-col', 'lo-target', 'Ghost part', 0],
|
||||
['lo-holder', 'loy', 'lo-target', 'Layer holder', 0],
|
||||
]) {
|
||||
const o = structuredClone(scene.objects[0]);
|
||||
Object.assign(o, { id, assetId, parentId, name, position: { x, y: x, z: 0 }, overrides: {}, instanceEdits: {} });
|
||||
delete o.groupId;
|
||||
scene.objects.push(o);
|
||||
}
|
||||
`);
|
||||
await clickText('Fit');
|
||||
await shot('29-layer-of');
|
||||
const logFrom = t.consoleLog.length;
|
||||
await t.play();
|
||||
await sleep(600);
|
||||
await stop();
|
||||
const logged = t.consoleLog
|
||||
.slice(logFrom)
|
||||
.filter((line) => line.startsWith('[behavior Layer holder]'))
|
||||
.map((line) => line.split(']').pop().trim());
|
||||
check(
|
||||
'a run names the layer of the owner’s collider the way the project calls it, and its number',
|
||||
JSON.stringify(logged) === JSON.stringify(['Ghost', '1']),
|
||||
JSON.stringify(logged),
|
||||
);
|
||||
await mutate(`
|
||||
const p = __gm.state.project;
|
||||
p.collisionLayers = window.__layersBefore;
|
||||
p.assets = p.assets.filter((a) => a.id !== 'loy' && a.id !== 'lo-col');
|
||||
const scene = __gm.state.activeScene;
|
||||
scene.objects = scene.objects.filter((o) => !o.id.startsWith('lo-'));
|
||||
`);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user