An Object variable could only be set by a graph while the game runs.
A placed object always showed "(null)", although the other objects of
its scene are there to choose from and the runtime can already find
any object by its id. Now the placed object stores a reference to one
of them, and the running game turns it into the real object.
- Stored as {"$object": "<id>"}. The empty placeholder stays as it
was, so old projects load unchanged and empty still means "(null)".
- The row of an Object variable on a placed object is a dropdown with
None and every object of the scene, indented like the Hierarchy.
The same goes for Object fields in structs and Object items in
lists, also in the Variables and List tabs.
- The behavior asset keeps "(null)". An asset has no scene, so "Apply
to asset" is gone from Object rows.
- The runtime resolves the reference when the Variables or Properties
node reads it. A destroyed target reads as null, a missing one shows
"(missing)" in the row.
- Inside a container only objects of the same container are offered.
The reference keeps the template id, so every placement finds its
own copy.
- Deleting the target clears the reference in the same undo step.
Paste points references inside the pasted group at the copies and
leaves outside references alone. Create container and Unpack carry
references along with the new ids.
- A value a graph writes while running stays in the run. The scene
keeps the picked object, also in "Run in editor".
Found on the way:
- Setting an override coerced every object slot to empty, in plain
values, struct fields and list items. The shared objectPlace() now
keeps the reference.
Not done:
- No eyedropper to pick in the scene, the dropdown is enough for now.
- Deleting a template object in the container editor does not clean up
references in other scenes. They read as null there.
Tests:
- ./check.sh: 187 files, 2724 tests, CHECK EXIT 0
- e2e 1-9, 51, 57: ALL PASSED, 389 checks
- e2e 1, 57 on the final bundle: ALL PASSED, 17 checks
- 13 unit tests in object-references.test.ts, 5 checks in e2e 57
- Counter-checks: 8 unit breaks fail their tests, the browser break
without a pick list fails 57b-57d
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An object could only live in a dedicated object variable, kept in slots
beside the variable bag. Struct fields and list items could not hold
one, so a spawned object packed into a struct or list was lost. This
replaces the slots with one mechanism for all three places.
- "No object" has one resting form everywhere: {"$object": ""}. A
running game can tell an object slot from an empty text that way.
- During play the object handle lives directly in the value, also
nested in structs and lists. objectSlots.ts and the special paths for
Keep and Properties are gone.
- liveObjects.ts strips objects to (null) at every exit: overrides the
editor run writes back to the scene, saved values of persistent
Variables assets, live values in the inspector, and the world
snapshot of the test hooks (objects as names there).
- A destroyed object reads as (null) through isAlive, alone, in a
struct field or as a list item.
- "+ Field" and "+ Item" offer Object. The inspector and the list tab
show a fixed (null) row without input.
- Lists from Raycast all stay objects when written to a variable.
Before they turned into names.
- A struct that only changes by an object writes no override. A list
that grows by an object writes [(null)], its length really changed.
Found on the way: CI at 98211a8 failed only in e2e 21d. #237 added
asset to the field kinds without updating that fixed expectation. 21d
now expects ...,animation,asset,object,struct.
No project on the server has object variables, so no old data is
affected.
Tests:
- ./check.sh: 169 files, 2475 tests, CHECK EXIT 0.
- e2e 6, 21, 23, 32: ALL PASSED, 213 checks.
- object-variable.test.ts rewritten: variable, struct field and list
item over several frames, destroyed objects, nothing in scene, file
or save, editor run reload, Keep.
- New e2e 21d-ii adds an Object item by click. 21d and 23k updated.
- 7 counter-probes, each fails 1 to 9 tests. Originals back via cmp.
- Not tested on its own: stripping of inspector live values and the
names in the test snapshot, the "+ Field -> Object" menu by click.
- e2e check total 1407, only calculated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A spawned object was only reachable in the run that spawned it. The
Spawn output could not go into a variable because "object" was a
port-only kind, so the next run had no way back to the instance.
- "Object" is now a variable kind. The reference lives for the run
only. The file stores the declaration and an empty value.
- The live reference sits in slots beside the variable bag
(graph/objectSlots.ts), not in it. The bag is the behavior's props,
and syncToModel would have written an object into the scene.
- A destroyed object, or one from a restarted or left scene, reads as
nothing. A dead reference is dropped from its slot.
- Keep (#229) hands the slots of kept variables on to the next call
of a sub-behavior.
- Properties of kind Behavior reads and writes another behavior's
object variable through the same slots.
- The inspector shows "(null)" for it and offers no input.
- A persistent Variables asset with an object variable shows a
warning under "Persistent". Saved values never keep the object,
every run starts it at (null).
Objects inside lists and struct fields are left out on purpose.
Emptiness is checked with Equals against an empty b. A node "Is
valid" does not exist yet.
The test helper graphEditor().variables() now also reads the kinds
of a Variables asset a node points to.
Tests:
- ./check.sh: 169 files, 2463 tests, CHECK EXIT 0
- e2e 6 + 23: ALL PASSED, 146 checks (new 23k, 2 checks)
- e2e 32: ALL PASSED, 18 checks (new 32f-ii)
- 10 unit tests in tests/core/object-variable.test.ts, one of them
runs a persistent asset over two runs
- 5 counter-probes (value in bag, isAlive always true, no Keep
hand-over, Properties without slots, object in saved values) each
fail their test
- e2e check total 1406, computed only
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Collapsed, the node reads "Set 2 variables", "Get width" or "No properties";
expanded it is always "Variables" / "Properties". The Properties node shows
its object type (and behavior) as a line under the title instead.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Get/Set property, Get/Set properties, Set enabled and Set text are gone.
A single Properties node takes a fixed "object" input; every chosen
property is an output that reads the current value, and one switched to
Set is also an input written when the flow arrives. Exec pins only exist
while at least one entry writes.
The node carries a type (Sprite, Text, Collider, Camera, Container,
Behavior, Any) that decides what "+ Property" offers: the fields of that
type, the variables of the chosen behavior, or for Any a kind to pick
and a name to type. Each port takes the default and field of its type,
so a Camera width starts at 640. Switching the type keeps the ports and
marks those the new type lacks. At runtime the type is only an editor
aid; the node writes to whatever object it gets.
Old project files are converted on load. The type is inferred from the
keys: the first object type that has them all, else the one behavior
that declares them all, else Any. enabled belongs to every object, so a
converted Set enabled lands on Any.
The jump-run and tetris seeders use Properties throughout; draw-piece
switches its cell on in the same node that sets the colour.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>