Let Spawn take an asset over an edge and find a child by name (#237)

An asset could only be picked as a setting on the Spawn node. It could
not be wired in, held in a variable or put into a list. Finding an
object below the own one needed a scene-wide search by name.

- "Asset" is now a kind like Animation or Sound: port, variable, list
  item and a new "Asset" literal node, with its own cube icon.
- Spawn and Is asset have an `asset` input. It uses the key of the old
  setting, so a saved asset reads as the value of the input and older
  projects keep spawning without a migration.
- New node "Child by name" searches below an object level by level.
  A child wins over a grandchild of the same name. Nothing found or no
  object gives null.

Tests:
- ./check.sh: 168 files, 2453 tests, CHECK EXIT 0.
- 9 new unit tests in tests/core/asset-kind.test.ts, catalog cases for
  Spawn, Is asset, Child by name and the literal.
- e2e 6z-ii (2 checks): node rows and a real drag of the literal onto
  `asset`. e2e 6: 119 checks passed. Spawn checks in 3 and 9 passed.
- Counter-checks: depth-first search fails 1, Spawn reading only the
  old setting fails 3, runtime ignoring the parent fails 1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5.5 committed 2026-09-26 19:36:49 +02:00
1 parent 1b3cb625a3
commit 923baf2557
18 files changed
+345 -24

No files matched your search

+13
View File
@@ -410,6 +410,19 @@ export class World {
return this.entities.find((e) => e.name === name);
}
/** The nearest entity with that name below `parent`, level by level – a child before a grandchild. */
childByName(parent: Entity, name: string): Entity | undefined {
for (
let level = this.childrenOf(parent);
level.length > 0;
level = level.flatMap((e) => this.childrenOf(e))
) {
const found = level.find((e) => e.name === name);
if (found) return found;
}
return undefined;
}
/**
* The camera the game is seen through: of the cameras that are switched on (itself and every
* parent, see `properties/enabled.ts`) the first one in hierarchy order. Undefined while none
+4
View File
@@ -662,6 +662,10 @@ export class BehaviorSystem implements System {
time: world.time,
dt,
findByName: (name) => world.findByName(name) ?? null,
childByName: (parent, name) => {
const found = resolve(parent);
return (found && world.childByName(found, name)) ?? null;
},
isKeyDown: (key) => this.input.isDown(key),
pointer: world.pointer,
hitTest: (target, x, y) => {