The new node "Raycast all" answers with every collider along the ray instead of only the nearest one: it carries the same inputs and settings as Raycast (Ignore owner, Hit triggers, Layers from #158) plus "max hits" where 0 means all, and it hands back parallel lists sorted by distance - objects, colliders, points x/y, normals x/y, distances - next to count, so index i of every list speaks about the same hit; a collider counts once no matter how many convex parts its shape is built from, while a tilemap counts per cell it is hit in, and the filter sits in Solids.raycastAll like the one of #158, the one path the editor run and Play both take, so all 12 new unit tests run through describe.each against both systems; the node needed the value system to learn something it could not do - a graph list carried numbers, text, booleans and bundles but no object handles, asList turned an object into its name, so objects/colliders would have been lists of names, which loses the object when two carry the same name, and collider children usually do; Value now knows a travelling list that may hold handles and "Item at" gives such an item out as an object, ready for Destroy, Apply force or Name of, while the single door into a stored file - writeTyped/asTypeOf - runs the list through the new storedList() so every object becomes its name again and nothing living reaches a project file, which is its own unit test over JSON.stringify; three list nodes follow the handles: "Index of" compares objects by identity because a name is not the object of that name, "Set item" and "Fill" write an object as an object, and "Join text" prints the names instead of the innards; this decision was mine to make because the question ran into the MCP timeout - the alternative was a list of names plus "Object by name", which is the smaller change but lossy; 4 new e2e checks in the new block 43e (shield and floor in one go reading 2 and "145, 280", the second object answering to its name, which only a real object in a list can do, "max hits" 1 stopping at the nearest, and the node showing the row) and 18 new unit tests, 12 in raycast.test.ts, 4 on the value system and 2 in the node catalog; two counter-checks hold: not counting tilemap cells one by one drops 2 unit tests, and answering with the nearest hit only drops module 43 with 2 FAILs; one finding on the way - the node read "maxHits" while every other input carries a spoken label, because int() took the key as its label, so it has a third parameter now, and the e2e check caught that, no unit test did (#157)
game-maker / gate (push) Successful in 2m4s
game-maker / suite (push) Successful in 7m26s

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5 committed 2026-09-20 18:54:12 +02:00
1 parent 0fdde6cb1e
commit ffdf0a8ddb
20 files changed
+607 -33

No files matched your search

+69
View File
@@ -1001,6 +1001,75 @@ const CASES: Record<string, Case[]> = {
},
},
],
RaycastAll: [
{
name: 'puts the hits out as parallel lists, nearest first, and counts them (#157)',
inputs: { fromX: 10, fromY: 20, dirX: 0, dirY: 1, length: 300, maxHits: 2 },
context: {
raycastAll: (from, direction, length, options) => {
expect([from, direction, length, options.ignore?.id, options.triggers, options.maxHits]).toEqual([
{ x: 10, y: 20 },
{ x: 0, y: 1 },
300,
'owner',
false,
2,
]);
return [
{
object: crate,
collider: painted,
point: { x: 10, y: 120 },
normal: { x: 0, y: -1 },
distance: 100,
},
{
object: dancer,
collider: dancer,
point: { x: 10, y: 220 },
normal: { x: 0, y: 1 },
distance: 200,
},
];
},
},
next: 'exec',
outputs: {
count: 2,
// The objects travel as objects, not as their names: that is what a list may carry now.
objects: [crate, dancer],
colliders: [painted, dancer],
pointsX: [10, 10],
pointsY: [120, 220],
normalsX: [0, 0],
normalsY: [-1, 1],
distances: [100, 200],
},
},
{
name: 'a ray that meets nothing puts out empty lists and a count of 0',
inputs: { fromX: 0, fromY: 0, dirX: 1, dirY: 0, length: 50, maxHits: -3 },
params: { ignoreOwner: false, triggers: true },
context: {
raycastAll: (_from, _direction, _length, options) => {
// A negative "max hits" is no limit at all, the same as 0.
expect([options.ignore, options.triggers, options.maxHits]).toEqual([null, true, 0]);
return [];
},
},
next: 'exec',
outputs: {
count: 0,
objects: [],
colliders: [],
pointsX: [],
pointsY: [],
normalsX: [],
normalsY: [],
distances: [],
},
},
],
IgnoreOneWay: [
{
name: 'asks the context to let the object through for the seconds given',