Nested behavior #16

Closed
opened 2026-09-12 15:03:52 +02:00 by admin · 0 comments

Nested Behavior / Behavior als Node in Graph

Was damit gemeint ist?
=> Ein Behavior Asset als Node innerhalb eines Graph

Grundsätzlich soll so ein Behavior Node im Graf mit einem Execution Flow ausgestattet sein, sprich wir brauchen einen Start/End Node:

  • Start Node ist ähnlich wie ein Event Node mit exec output port und dort soll man auch ouput "variablen" festlegen können (quasi wie variablen)
  • End Node ähnlich, hat als Input ein exec port + inputs die als output gelten, wenn man es als node in einen anderen graphen eines behavior assets einbindet.

wenn alles verstanden, direkt loslegen, ansonsten warte ich auf rückfragen :)


Umgesetzt

Verstanden war alles — gebaut ist genau die beschriebene Form. Drei neue Node-Typen:

Start exec-Ausgang plus die Eingaenge, die das Behavior von aussen bekommt. Was hier deklariert wird, ist ein Eingang des aufrufenden Nodes
End exec-Eingang plus die Werte, die zurueckgehen. Was hier deklariert wird, ist ein Ausgang des aufrufenden Nodes
Run behavior der Aufruf selbst. Seine Ports gibt es nicht in seiner Typdefinition — sie werden aus Start- und End-Node des Ziels abgeleitet, bei jedem Zeichnen neu

Deklariert wird im Inspector des Start-/End-Nodes: + Input bzw. + Output, Art waehlbar
(Number, Boolean, Text, Object), Name direkt editierbar, × zum Entfernen — dieselbe Bauform
wie die Variablen eines Behaviors.

Der Node traegt den Namen des Behaviors, nicht "Run behavior", und die Palette listet
Behaviors ebenfalls namentlich. Ein Aufruf ist damit ein Griff statt Node einfuegen + Dropdown.

Die eine Entscheidung, die du mir ueberlassen hast

Ein eingebettetes Behavior hat kein Objekt in der Szene — also auch keinen Ort fuer seine
Variablen. Ich habe (A) reine Funktion gewaehlt: jeder Aufruf startet die Variablen auf den
Asset-Defaults, rein geht es ueber den Start-Node, raus ueber den End-Node.

Der Grund ist dein eigener naechster Schritt: du willst den Tetris-Graphen zerlegen. Dabei
sollen die Teile rechnen, waehrend der Zustand (grid, pieceX, …) im Haupt-Behavior
bleibt — genau das, was du mit "kann man ueber input/output loesen" gemeint hast. (A) ist
ausserdem die konservative Wahl: daraus laesst sich spaeter Zustand machen, umgekehrt nicht,
ohne bestehende Graphen zu brechen.

Die Tuer ist nicht zu: Zustand gibt es weiterhin ueber ein Behavior-Objekt in der Szene
(Object by name → Get/Set property) — der Weg, den dein Jump and Run fuer den Punktestand
schon geht. Lokale Variablen im eingebetteten Graphen funktionieren als Zwischenspeicher
innerhalb eines Aufrufs unveraendert; sie werden nur am Ende verworfen.

Was ich selbst entschieden habe

Ein Start, ein End ein zweiter machte "welche Ports hat dieses Behavior" mehrdeutig. Der Command lehnt ihn ab, die Palette bietet ihn nicht mehr an
Selbstaufruf = Fehler mit Namen im Log (Behavior "X" runs itself), statt still in die Tiefenbegrenzung zu laufen. Die Palette bietet das eigene Behavior gar nicht erst an
self/owner bleiben der eingebettete Graph sieht dasselbe Objekt wie der Aufrufer — sonst waeren Self/Owner darin wertlos
Toter Verweis ist still ein Node ohne gewaehltes Behavior (oder eines ohne Start-Node) tut nichts und laesst den Fluss weiterlaufen, statt den Lauf abzubrechen

Zwei Dinge, die der Bau erzwungen hat

Eigener Pulse-Cache pro Aufruf. Node-Ids sind nur pro Graph eindeutig. Liefe der
eingebettete Graph im Cache des Aufrufers, koennte er dessen Werte ueberschreiben. Der
Interpreter arbeitet deshalb auf Frames — der Aufruf schiebt einen neuen auf und nimmt ihn
danach wieder weg. Dafuer gibt es einen eigenen Test, der eine Id-Kollision erzwingt; ohne
Frames liefert er 99 statt 7.

Ports aufraeumen ueber Graphgrenzen. Einen Port umzubenennen oder zu entfernen betrifft
nicht nur den eigenen Graphen, sondern jeden anderen, der das Behavior aufruft. Beides
raeumt dort mit auf — und ein Rename nimmt den eingetippten Inline-Wert mit, sonst faellt er
still auf den Default zurueck. Das ist mir erst beim Gegenpruefen aufgefallen: der Test stand
auf 0 statt auf 6.

Live-Highlight: der Trace eines laufenden Behaviors deckt jetzt auch die Graphen ab, die es
als Node aufruft — sonst bliebe ein eingebettetes Behavior beim Zusehen dunkel.

Belegt

Zehn Unit-Tests (tests/core/nested-behavior.test.ts) und elf E2E-Checks in Modul 19.
Der staerkste ist der erste: das aufrufende Behavior rechnet nichts selbst — die
Geschwindigkeit, mit der die Kiste laeuft, ist der zurueckgegebene Wert.

PASS  a behavior used as a node runs, and what it returns is what drives the caller
      – moved 200.0 px in ~1 s – 2 × 100 means the returned value arrived, 0 would mean nothing came back
PASS  the calling node grows the ports the other behavior declares
PASS  and it is drawn under that behavior's name, not as "Run behavior" – title="Double"
PASS  adding an input declares it on the start node – [{"key":"in1",…},{"key":"in2",…}]
PASS  and the node in the other graph grew that input without being touched
PASS  undo removes it in both graphs – [{"key":"in1","kind":"number"}]
PASS  picking a behavior in the palette drops in a node already set to it
      – {"type":"RunBehavior","params":{"behavior":"nb-double"}}

200.0 statt 100 beweist, dass der verdoppelte Wert ankam, nicht bloss irgendeiner;
0 waere "nichts kam zurueck". Die drei Port-Checks in Folge sind der Nachweis, dass die
Portliste wirklich abgeleitet und nicht kopiert ist: deklariert wird im einen Graphen,
gewachsen ist der Node im anderen, ohne dass er angefasst wurde.

Gegenprobe gefahren: ohne die frische Variablenkopie und ohne die Selbstaufruf-Sperre fallen
zwei Unit-Tests; ohne getrennte Frames faellt der Kollisionstest (expected 99 to be 7) — der
war beim ersten Versuch faelschlich gruen und musste geschaerft werden, bevor er etwas taugte.

volle E2E-Suite, remote ALL PASSED – 379 Checks, 0 FAIL
npm run check lokal gruen, 524 Tests

Übernommen aus todo-notes/done/nested-behavior.md beim Umzug auf das Issue-System (2026-09-12).

  • Angelegt: 2026-09-09 · 64e034f — Note down two more wishes
  • Abgeschlossen: 2026-09-09 · 6702ff8 — Let a behavior be a node in another graph
  • Gemeldet von: admin
## Nested Behavior / Behavior als Node in Graph Was damit gemeint ist? => Ein Behavior Asset als Node innerhalb eines Graph Grundsätzlich soll so ein Behavior Node im Graf mit einem Execution Flow ausgestattet sein, sprich wir brauchen einen Start/End Node: - Start Node ist ähnlich wie ein Event Node mit exec output port und dort soll man auch ouput "variablen" festlegen können (quasi wie variablen) - End Node ähnlich, hat als Input ein exec port + inputs die als output gelten, wenn man es als node in einen anderen graphen eines behavior assets einbindet. wenn alles verstanden, direkt loslegen, ansonsten warte ich auf rückfragen :) --- ## Umgesetzt Verstanden war alles — gebaut ist genau die beschriebene Form. Drei neue Node-Typen: | | | |---|---| | **`Start`** | exec-Ausgang plus die Eingaenge, die das Behavior von aussen bekommt. Was hier deklariert wird, ist ein **Eingang des aufrufenden Nodes** | | **`End`** | exec-Eingang plus die Werte, die zurueckgehen. Was hier deklariert wird, ist ein **Ausgang des aufrufenden Nodes** | | **`Run behavior`** | der Aufruf selbst. Seine Ports gibt es nicht in seiner Typdefinition — sie werden aus Start- und End-Node des Ziels **abgeleitet**, bei jedem Zeichnen neu | Deklariert wird im Inspector des Start-/End-Nodes: `+ Input` bzw. `+ Output`, Art waehlbar (Number, Boolean, Text, Object), Name direkt editierbar, `×` zum Entfernen — dieselbe Bauform wie die Variablen eines Behaviors. **Der Node traegt den Namen des Behaviors**, nicht "Run behavior", und die Palette listet Behaviors ebenfalls namentlich. Ein Aufruf ist damit ein Griff statt Node einfuegen + Dropdown. ## Die eine Entscheidung, die du mir ueberlassen hast Ein eingebettetes Behavior hat kein Objekt in der Szene — also auch keinen Ort fuer seine Variablen. Ich habe **(A) reine Funktion** gewaehlt: jeder Aufruf startet die Variablen auf den Asset-Defaults, rein geht es ueber den Start-Node, raus ueber den End-Node. Der Grund ist dein eigener naechster Schritt: du willst den Tetris-Graphen zerlegen. Dabei sollen die Teile *rechnen*, waehrend der Zustand (`grid`, `pieceX`, …) im Haupt-Behavior bleibt — genau das, was du mit "kann man ueber input/output loesen" gemeint hast. (A) ist ausserdem die konservative Wahl: daraus laesst sich spaeter Zustand machen, umgekehrt nicht, ohne bestehende Graphen zu brechen. **Die Tuer ist nicht zu:** Zustand gibt es weiterhin ueber ein Behavior-Objekt in der Szene (`Object by name` → `Get/Set property`) — der Weg, den dein Jump and Run fuer den Punktestand schon geht. Lokale Variablen im eingebetteten Graphen funktionieren als Zwischenspeicher innerhalb eines Aufrufs unveraendert; sie werden nur am Ende verworfen. ## Was ich selbst entschieden habe | | | |---|---| | **Ein Start, ein End** | ein zweiter machte "welche Ports hat dieses Behavior" mehrdeutig. Der Command lehnt ihn ab, die Palette bietet ihn nicht mehr an | | **Selbstaufruf = Fehler** | mit Namen im Log (`Behavior "X" runs itself`), statt still in die Tiefenbegrenzung zu laufen. Die Palette bietet das eigene Behavior gar nicht erst an | | **`self`/`owner` bleiben** | der eingebettete Graph sieht dasselbe Objekt wie der Aufrufer — sonst waeren `Self`/`Owner` darin wertlos | | **Toter Verweis ist still** | ein Node ohne gewaehltes Behavior (oder eines ohne Start-Node) tut nichts und **laesst den Fluss weiterlaufen**, statt den Lauf abzubrechen | ## Zwei Dinge, die der Bau erzwungen hat **Eigener Pulse-Cache pro Aufruf.** Node-Ids sind nur *pro Graph* eindeutig. Liefe der eingebettete Graph im Cache des Aufrufers, koennte er dessen Werte ueberschreiben. Der Interpreter arbeitet deshalb auf **Frames** — der Aufruf schiebt einen neuen auf und nimmt ihn danach wieder weg. Dafuer gibt es einen eigenen Test, der eine Id-Kollision erzwingt; ohne Frames liefert er 99 statt 7. **Ports aufraeumen ueber Graphgrenzen.** Einen Port umzubenennen oder zu entfernen betrifft nicht nur den eigenen Graphen, sondern **jeden anderen, der das Behavior aufruft**. Beides raeumt dort mit auf — und ein Rename nimmt den eingetippten Inline-Wert mit, sonst faellt er still auf den Default zurueck. Das ist mir erst beim Gegenpruefen aufgefallen: der Test stand auf 0 statt auf 6. **Live-Highlight:** der Trace eines laufenden Behaviors deckt jetzt auch die Graphen ab, die es als Node aufruft — sonst bliebe ein eingebettetes Behavior beim Zusehen dunkel. ## Belegt **Zehn Unit-Tests** (`tests/core/nested-behavior.test.ts`) und **elf E2E-Checks** in Modul 19. Der staerkste ist der erste: das aufrufende Behavior rechnet **nichts** selbst — die Geschwindigkeit, mit der die Kiste laeuft, *ist* der zurueckgegebene Wert. ``` PASS a behavior used as a node runs, and what it returns is what drives the caller – moved 200.0 px in ~1 s – 2 × 100 means the returned value arrived, 0 would mean nothing came back PASS the calling node grows the ports the other behavior declares PASS and it is drawn under that behavior's name, not as "Run behavior" – title="Double" PASS adding an input declares it on the start node – [{"key":"in1",…},{"key":"in2",…}] PASS and the node in the other graph grew that input without being touched PASS undo removes it in both graphs – [{"key":"in1","kind":"number"}] PASS picking a behavior in the palette drops in a node already set to it – {"type":"RunBehavior","params":{"behavior":"nb-double"}} ``` `200.0` statt `100` beweist, dass der **verdoppelte** Wert ankam, nicht bloss irgendeiner; `0` waere "nichts kam zurueck". Die drei Port-Checks in Folge sind der Nachweis, dass die Portliste wirklich *abgeleitet* und nicht kopiert ist: deklariert wird im einen Graphen, gewachsen ist der Node im anderen, ohne dass er angefasst wurde. Gegenprobe gefahren: ohne die frische Variablenkopie und ohne die Selbstaufruf-Sperre fallen zwei Unit-Tests; ohne getrennte Frames faellt der Kollisionstest (`expected 99 to be 7`) — der war beim ersten Versuch faelschlich gruen und musste geschaerft werden, bevor er etwas taugte. | | | |---|---| | volle E2E-Suite, remote | **ALL PASSED – 379 Checks**, 0 FAIL | | `npm run check` lokal | gruen, **524 Tests** | --- *Übernommen aus `todo-notes/done/nested-behavior.md` beim Umzug auf das Issue-System (2026-09-12).* - **Angelegt:** 2026-09-09 · `64e034f` — Note down two more wishes - **Abgeschlossen:** 2026-09-09 · `6702ff8` — Let a behavior be a node in another graph - **Gemeldet von:** admin
admin added the todo-note label 2026-09-12 15:03:52 +02:00
admin closed this issue 2026-09-12 15:03:52 +02:00
admin added this to the Development project 2026-09-13 13:43:26 +02:00
admin moved this to Done in Development on 2026-09-13 13:44:29 +02:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: meleki/feuerwerk#16