Tilemaps can block movement: a tile gains a Solid switch, the solid cells of all layers are merged into rectangles and the placed tilemap takes part in the collision system as one body, reported as other in collision events (#132)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
10b49fac46
commit
4c411c1ff3
10 files changed
+715
-43
No files matched your search
@@ -320,6 +320,77 @@ export async function tilemap(t) {
|
||||
JSON.stringify({ reloaded, inMemory }),
|
||||
);
|
||||
|
||||
// 40l. Collision (#132). The switch sits on the tile, and the map outlines where it now blocks.
|
||||
// The map holds a 3 × 3 block and, on the second layer, one cell at (4, 0).
|
||||
await openTab(t, 'scene');
|
||||
await select({ kind: 'asset', assetId: 'at-grass' });
|
||||
const solidRow = await json("__gm.inspectorField('Solid')");
|
||||
if (solidRow) await click(solidRow.x, solidRow.y);
|
||||
await settle();
|
||||
check(
|
||||
'a tile has a Solid switch in the inspector, and clicking it makes the tile solid',
|
||||
Boolean(solidRow) && (await t.asset('at-grass'))?.props.solid === true,
|
||||
JSON.stringify({ solidRow, props: (await t.asset('at-grass'))?.props }),
|
||||
);
|
||||
await mutate(`__gm.lookAt(0, ${LANE})`);
|
||||
await settle();
|
||||
const outlined = await json(`__gm.tilemap.solids(${JSON.stringify(MAP_OBJECT)})`);
|
||||
check(
|
||||
'the scene view outlines the blocking cells, merged into rectangles',
|
||||
outlined?.visible === true &&
|
||||
JSON.stringify(outlined.rects) ===
|
||||
JSON.stringify([
|
||||
{ x: 0, y: 0, w: 3, h: 3 },
|
||||
{ x: 4, y: 0, w: 1, h: 1 },
|
||||
]),
|
||||
JSON.stringify(outlined),
|
||||
);
|
||||
await shot('40-tilemap-solid');
|
||||
|
||||
// In the game a crate of its own walks into the block: the seeded Crate sprite with a 100 × 60
|
||||
// collider and the Mover (ArrowRight moves its owner, colliding with solids) below it. Its own
|
||||
// rather than the seeded red crate, which an earlier step of a full run may have removed. It
|
||||
// starts at −200 with 30 units of free travel: the map sits so that the block's face is at −120.
|
||||
const mapBefore = (await t.object(MAP_OBJECT)).position;
|
||||
await mutate(`
|
||||
const scene = __gm.state.activeScene;
|
||||
const object = (id, assetId, parentId, position, overrides) => ({ id, assetId, name: '', position,
|
||||
editorOffset: { x: 0, y: 0 }, overrides, parentId, instanceEdits: {} });
|
||||
scene.objects.push(
|
||||
object('tm-crate', 'crate', '', { x: -200, y: ${LANE}, z: 0 }, {}),
|
||||
object('tm-crate-collider', 'collider', 'tm-crate', { x: 0, y: 0, z: 0 },
|
||||
{ shape: 'box', width: 100, height: 60, isTrigger: false }),
|
||||
object('tm-crate-mover', 'mover', 'tm-crate', { x: 0, y: 0, z: 0 }, {}));
|
||||
scene.objects.find((o) => o.id === ${JSON.stringify(MAP_OBJECT)}).position = { x: 24, y: ${LANE + 24}, z: 0 };
|
||||
__gm.state.select(null)`);
|
||||
await settle();
|
||||
const playAgainstMap = async () => {
|
||||
await t.play();
|
||||
await t.holdKey('ArrowRight', 700);
|
||||
const crate = await t.entity('tm-crate');
|
||||
await t.stop();
|
||||
await settle();
|
||||
return crate;
|
||||
};
|
||||
const stopped = await playAgainstMap();
|
||||
check(
|
||||
'Move with "Collide with solids" stops the crate flush against the solid cells',
|
||||
stopped && Math.abs(stopped.x - -170) < 0.01,
|
||||
`world x=${stopped?.x}`,
|
||||
);
|
||||
await mutate("__gm.state.project.assets.find((a) => a.id === 'at-grass').props.solid = false");
|
||||
const passed = await playAgainstMap();
|
||||
check(
|
||||
'a tile that is not solid lets the crate walk over it',
|
||||
passed && passed.x > -170 + 20,
|
||||
`world x=${passed?.x}`,
|
||||
);
|
||||
await mutate(`
|
||||
const scene = __gm.state.activeScene;
|
||||
scene.objects = scene.objects.filter((o) => !o.id.startsWith('tm-crate'));
|
||||
scene.objects.find((o) => o.id === ${JSON.stringify(MAP_OBJECT)}).position = ${JSON.stringify(mapBefore)};`);
|
||||
await settle();
|
||||
|
||||
// Leave the editor as the rest of the suite expects to find it.
|
||||
await evaluate("__gm.state.setTilemapPaint({ tool: 'select' })");
|
||||
await evaluate('__gm.state.select(null)');
|
||||
|
||||
Reference in new issue
Block a user