From 2c0c5eb99fce330cee39ff917d22500b6c227a67 Mon Sep 17 00:00:00 2001 From: root Date: Sat, 12 Sep 2026 15:08:25 +0200 Subject: [PATCH] Move the todo notes into the Gitea issue tracker and drop the folder All 50 notes are now issues #1-#50 in meleki/game-maker, open ones open and finished ones closed. Every issue keeps the note verbatim and adds a footer with the commit it was filed in, the commit that finished it and who raised it; the 37 notes the user wrote are posted as `admin`, the 13 I raised as `meleki`. Label `todo-note` marks the whole batch. todo-notes/ is gone - the tracker is the backlog from here on. README says where they live now. Co-Authored-By: Claude Opus 5 --- README.md | 6 + todo-notes/README.md | 7 - todo-notes/console-log.md | 0 todo-notes/done/.gitkeep | 0 todo-notes/done/asset-doppelklick.md | 18 - todo-notes/done/asset-drag-and-drop.md | 30 -- todo-notes/done/build-chunks.md | 19 - .../done/execute-behavior-in-editor-mode.md | 92 ----- todo-notes/done/expand-und-pack-node.md | 21 - todo-notes/done/flaky-pixel-art-filter.md | 63 --- todo-notes/done/game-pause.md | 58 --- todo-notes/done/game-view-preview.md | 73 ---- todo-notes/done/graph-during-play-mode.md | 8 - ...ph-editor-multiselect-und-mausverhalten.md | 29 -- .../graph-flow-animation-during-play-mode.md | 5 - todo-notes/done/graph-node-collapse-width.md | 14 - todo-notes/done/graph-snap-grid.md | 19 - .../done/graph-ux-edge-drag-and-drop.md | 28 -- .../done/inspector-buttons-entfernen.md | 7 - todo-notes/done/mehrere-kameras.md | 46 --- todo-notes/done/merge-get-set-variable.md | 9 - todo-notes/done/nested-behavior.md | 104 ----- todo-notes/done/nexted-behavior-ergaenzung.md | 109 ------ todo-notes/done/node-collapse.md | 15 - .../done/nodes-mit-dynamischer-port-zahl.md | 360 ------------------ todo-notes/done/on-key-down-repeat.md | 83 ---- todo-notes/done/override-darstellung.md | 19 - todo-notes/done/overwrite-reset-nested.md | 30 -- .../overwrite-textfarbe-auch-bei-assets.md | 37 -- todo-notes/done/refactor-types.md | 23 -- todo-notes/done/reseed-because-of-list.md | 18 - .../done/show-gizmo-of-disabled-objects.md | 23 -- todo-notes/done/sprite-animation.md | 15 - todo-notes/done/sprite-size.md | 24 -- .../done/struct-und-listen-restposten.md | 77 ---- .../sub-behavior-breadcrumb-navigation.md | 10 - todo-notes/done/tetris-example-game.md | 95 ----- .../done/tetris-mit-structs-neu-bauen.md | 46 --- todo-notes/done/tetris-und-data-logik.md | 94 ----- todo-notes/done/texture-asset-to-sprite.md | 9 - todo-notes/done/unreferenced-image-files.md | 40 -- todo-notes/done/variables-node-title.md | 12 - todo-notes/flaky-pixel-art-filtering.md | 4 - todo-notes/frame-edge-bleeding.md | 20 - todo-notes/graph-comments-groups.md | 6 - todo-notes/load-scene-node.md | 7 - todo-notes/nginx-gzip.md | 5 - todo-notes/nine-slice.md | 5 - todo-notes/on-start-during-play.md | 10 - todo-notes/palette-exact-match.md | 3 - todo-notes/render-on-change.md | 5 - todo-notes/sound.md | 8 - todo-notes/template-changes-to-instances.md | 5 - 53 files changed, 6 insertions(+), 1867 deletions(-) delete mode 100644 todo-notes/README.md delete mode 100644 todo-notes/console-log.md delete mode 100644 todo-notes/done/.gitkeep delete mode 100644 todo-notes/done/asset-doppelklick.md delete mode 100644 todo-notes/done/asset-drag-and-drop.md delete mode 100644 todo-notes/done/build-chunks.md delete mode 100644 todo-notes/done/execute-behavior-in-editor-mode.md delete mode 100644 todo-notes/done/expand-und-pack-node.md delete mode 100644 todo-notes/done/flaky-pixel-art-filter.md delete mode 100644 todo-notes/done/game-pause.md delete mode 100644 todo-notes/done/game-view-preview.md delete mode 100644 todo-notes/done/graph-during-play-mode.md delete mode 100644 todo-notes/done/graph-editor-multiselect-und-mausverhalten.md delete mode 100644 todo-notes/done/graph-flow-animation-during-play-mode.md delete mode 100644 todo-notes/done/graph-node-collapse-width.md delete mode 100644 todo-notes/done/graph-snap-grid.md delete mode 100644 todo-notes/done/graph-ux-edge-drag-and-drop.md delete mode 100644 todo-notes/done/inspector-buttons-entfernen.md delete mode 100644 todo-notes/done/mehrere-kameras.md delete mode 100644 todo-notes/done/merge-get-set-variable.md delete mode 100644 todo-notes/done/nested-behavior.md delete mode 100644 todo-notes/done/nexted-behavior-ergaenzung.md delete mode 100644 todo-notes/done/node-collapse.md delete mode 100644 todo-notes/done/nodes-mit-dynamischer-port-zahl.md delete mode 100644 todo-notes/done/on-key-down-repeat.md delete mode 100644 todo-notes/done/override-darstellung.md delete mode 100644 todo-notes/done/overwrite-reset-nested.md delete mode 100644 todo-notes/done/overwrite-textfarbe-auch-bei-assets.md delete mode 100644 todo-notes/done/refactor-types.md delete mode 100644 todo-notes/done/reseed-because-of-list.md delete mode 100644 todo-notes/done/show-gizmo-of-disabled-objects.md delete mode 100644 todo-notes/done/sprite-animation.md delete mode 100644 todo-notes/done/sprite-size.md delete mode 100644 todo-notes/done/struct-und-listen-restposten.md delete mode 100644 todo-notes/done/sub-behavior-breadcrumb-navigation.md delete mode 100644 todo-notes/done/tetris-example-game.md delete mode 100644 todo-notes/done/tetris-mit-structs-neu-bauen.md delete mode 100644 todo-notes/done/tetris-und-data-logik.md delete mode 100644 todo-notes/done/texture-asset-to-sprite.md delete mode 100644 todo-notes/done/unreferenced-image-files.md delete mode 100644 todo-notes/done/variables-node-title.md delete mode 100644 todo-notes/flaky-pixel-art-filtering.md delete mode 100644 todo-notes/frame-edge-bleeding.md delete mode 100644 todo-notes/graph-comments-groups.md delete mode 100644 todo-notes/load-scene-node.md delete mode 100644 todo-notes/nginx-gzip.md delete mode 100644 todo-notes/nine-slice.md delete mode 100644 todo-notes/on-start-during-play.md delete mode 100644 todo-notes/palette-exact-match.md delete mode 100644 todo-notes/render-on-change.md delete mode 100644 todo-notes/sound.md delete mode 100644 todo-notes/template-changes-to-instances.md diff --git a/README.md b/README.md index cf29acb8..90b60f36 100644 --- a/README.md +++ b/README.md @@ -885,6 +885,12 @@ he walks on the spot – a clock that ticks without the picture following would The workspace folder itself is what nginx serves, so `npm run build` is the deployment. +### Ideas, gaps and bugs + +They live in the issue tracker of the Gitea repo: . +The folder `todo-notes/` that held them as files until 2026-09-12 is gone; all 50 notes were moved +into issues #1–#50, closed ones included, each carrying the commits it was filed and finished with. + ## API All routes go through `api/index.php?r=…` (the site's nginx does not pass `PATH_INFO`). diff --git a/todo-notes/README.md b/todo-notes/README.md deleted file mode 100644 index 07af2275..00000000 --- a/todo-notes/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# todo-notes - -A drop box for ideas that come up while work is already in progress, so they are not lost and not -squeezed into the running task. One file per topic, free form, any language. - -Anything in here is fair game to pick up at any time. When a note is done, move the file to `done/` -rather than deleting it – the note says what was wanted, the commit says what was built. diff --git a/todo-notes/console-log.md b/todo-notes/console-log.md deleted file mode 100644 index e69de29b..00000000 diff --git a/todo-notes/done/.gitkeep b/todo-notes/done/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/todo-notes/done/asset-doppelklick.md b/todo-notes/done/asset-doppelklick.md deleted file mode 100644 index 31552689..00000000 --- a/todo-notes/done/asset-doppelklick.md +++ /dev/null @@ -1,18 +0,0 @@ -Angeblich kann man Assets per Doppelklick in die Scene/Container platzieren. Das funktioniert aber nicht. -Falls es irgendwie implementiert ist gehört es sowieso raus => Das Doppelklick Feature brauchen wir nicht! - ---- - -**Umgesetzt (2026-09-09).** Raus, wie gewuenscht. - -Es *war* implementiert: `ListView` meldete einen Doppelklick (und Enter) als `onActivate`, und -`AssetPanel.activate` platzierte damit in die Mitte der sichtbaren Ansicht — der Weg, den ich in -`asset-drag-and-drop.md` noch als "bleibt bestehen" beschrieben hatte. Warum er bei dir nichts -tat, habe ich nicht weiter verfolgt: das Feature faellt ohnehin weg. - -Entfernt sind damit auch die Teile, die nur dafuer existierten: `AssetPanel.place`, das -`PlacementTarget`-Interface samt `Editor.placementTarget`, und die beiden `worldCenter()`-Methoden -in `ContainerEditor` und `SceneViewport`, die sonst tot im Baum stehengeblieben waeren. - -**Geblieben:** Doppelklick (oder Enter) auf ein **Szenen**-Asset setzt es weiterhin als aktive -Szene — das ist kein Platzieren, sondern derselbe Befehl wie im Kontextmenue. diff --git a/todo-notes/done/asset-drag-and-drop.md b/todo-notes/done/asset-drag-and-drop.md deleted file mode 100644 index 2e2024f0..00000000 --- a/todo-notes/done/asset-drag-and-drop.md +++ /dev/null @@ -1,30 +0,0 @@ -momentan funktioniert das platzieren eines asset in der scene/container per "Place in ..." über das kontextmenü. -zukünftig soll es per drag and drop passieren - ---- - -**Umgesetzt (2026-09-09).** Drag and Drop ersetzt den Menueintrag vollstaendig — auf deine -Ansage hin ist "Place in scene"/"Place in container" aus dem Kontextmenue verschwunden. -Doppelklick auf die Zeile platzierte zunaechst weiterhin in die Mitte der Ansicht — auf deine Notiz -`asset-doppelklick.md` hin ist auch das entfernt. - -- **Ziehen:** die Zeile im Assets-Panel wird nach 4 px Weg zum Drag; ein Ghost mit dem - Asset-Namen haengt am Cursor (`src/ui/drag.ts`). Die `ListView` erkennt die Geste nur und - gibt sie per `onDragOut` nach aussen ab — sie kennt kein Ziel ausserhalb ihrer selbst. -- **Vorschau:** ueber einer Ansicht zeigt ein Umriss die **Groesse**, die das Objekt haben wird, - plus Fadenkreuz auf dem Ursprung. Rot = der Drop wird abgelehnt. -- **Ziel:** die Ansicht unter dem Zeiger, nicht der aktive Tab. Scene-Tab → aktive Szene, - Container-Tab → offene Vorlage. `ContainerCanvas` erbt das von `SceneViewport`. -- **Regel:** `placementRejection` (`src/core/model/placement.ts`) — dieselbe Funktion, die - `PlaceAssetCommand` ausfuehrt. Die Vorschau kann also nichts versprechen, was der Drop - danach verweigert. Heute lehnt sie ab: Szenen und Container, die in sich selbst landen. - -**Zwei Fallen, die beim Bauen erst sichtbar wurden:** - -1. Ein Drag beginnt mit einem **Linksklick**, und der selektiert die Zeile — der Container-Tab - folgte der Selektion und haette sich mitten im Drag auf genau den Container umgeschaltet, - der gerade gezogen wird. Das Ziel waere unter dem Zeiger weggerutscht und jeder Drop haette - sich selbst verschachtelt. `followSelection` haelt jetzt still, solange ein Drag laeuft. - Vorher schuetzte davor der Rechtsklick, der nicht selektiert; mit Drag&Drop trug das nicht mehr. -2. Ein Drag endet oft dort, wo die Zielansicht **nichts davon mitbekommt** (ueber einem anderen - Panel, ausserhalb des Canvas). Ohne `onDragChange` waere die Vorschau stehengeblieben. diff --git a/todo-notes/done/build-chunks.md b/todo-notes/done/build-chunks.md deleted file mode 100644 index d21d557c..00000000 --- a/todo-notes/done/build-chunks.md +++ /dev/null @@ -1,19 +0,0 @@ -beim build kommt immer folgender output: -``` -../dist/assets/index-CPWPKF3g.js 578.97 kB │ gzip: 180.43 kB │ map: 2,777.11 kB - -(!) Some chunks are larger than 500 kB after minification. Consider: -- Using dynamic import() to code-split the application -- Use build.rollupOptions.output.manualChunks to improve chunking: https://rollupjs.org/configuration-options/#output-manualchunks -- Adjust chunk size limit for this warning via build.chunkSizeWarningLimit. -``` - -- was bedeutet das? -- kann man das "fixen"? - ---- -Antwort (User-Wahl: nichts ändern): -- Die Meldung ist nur ein Hinweis von Vite, kein Fehler. Sie erscheint bei jedem Chunk über 500 kB (minifiziert). -- Das Hauptbundle (590 kB) besteht zur Hälfte aus Pixi (300 kB: Grafik, Text, Container, Renderer, also fast nur Benutztes) und zur Hälfte aus eigenem Code (287 kB, davon Editor 130, Core 97, UI 40). Code-Splitting per `import()` bringt nichts, weil der Editor beim Start alles braucht. -- „Fixen“ ginge über einen eigenen Pixi-Chunk (`manualChunks`, beide Teile unter 500 kB, Pixi bleibt nach einem Deploy im Cache) oder über eine höhere Warngrenze (`chunkSizeWarningLimit`). An der Gesamtgröße ändert beides nichts. -- Größerer Hebel: nginx liefert JS unkomprimiert aus, weil `gzip` in der Konfiguration des Web-Stacks aus ist (590 kB statt 184 kB mit gzip, gemessen). Das Modul `gzip_static` ist im Container vorhanden. diff --git a/todo-notes/done/execute-behavior-in-editor-mode.md b/todo-notes/done/execute-behavior-in-editor-mode.md deleted file mode 100644 index 09b07bcf..00000000 --- a/todo-notes/done/execute-behavior-in-editor-mode.md +++ /dev/null @@ -1,92 +0,0 @@ -ein toggle bei einem behavior object in einer scene (passender name fehlt mir noch): -- wenn der toggle aktiviert ist, soll das behavior außerhalb des play modes trotzdem laufen (sprich immer) -- das behavior ist natürlich nicht aktiv wenn die scene nicht geladen ist wo sich das behavior befindet (sprich genauso wie im play mode, nur ohne play mode) - ---- - -**Umgesetzt (2026-09-09).** Der Schalter heisst **`Run in editor`** und steht im Inspector eines -Behavior-Objekts direkt unter `Enabled`. Ist er an, laeuft das Behavior weiter, waehrend das -Projekt *nicht* spielt (`src/editor/EditorRun.ts`, vom Editor neben der Play-Session getickt). - -## Die eine Entscheidung, die der Schalter erzwingt - -Play arbeitet auf einer **Wegwerf-Kopie** — deshalb ist Spielen fuer das gespeicherte Projekt -gefahrlos. Ausserhalb von Play gibt es diese Kopie nicht, und damit stand die Frage im Raum, was -ein laufendes Behavior ueberhaupt anfassen darf. Entschieden wurde (Rueckfrage an den User) fuer -die **Unity-Semantik** (`ExecuteInEditMode`): - -> Was das Behavior bewegt oder schreibt, ist eine **echte Aenderung am gespeicherten Modell**. -> Sie wird autosaved, die Panels zeigen sie — und **Undo holt sie nicht zurueck**. - -Das macht den Schalter maechtig (ein Behavior kann Inhalt bauen oder anordnen) und ist genau der -Grund, warum er **standardmaessig aus** ist. Der E2E-Check haelt beide Haelften fest: der Server -liefert die verschobene Position aus, und nach zwei Undos ist der Schalter weg, die Bewegung nicht. - -## Was der Editor-Lauf bewusst *nicht* bekommt - -| | | -|---|---| -| **Eingaben** | Tastatur und Maus bleiben beim Editor, sonst wuerde Tippen in einem Textfeld Behaviors ausloesen. `On key …`, `On click` und `Is key down` feuern hier nie; `On start`, `On update`, `On timer` und Kollisionen schon. | -| **Container-Inhalt** | `syncFromModel` schreibt den Inhalt platzierter Container als normale Objekte aus (*baking*) und loescht dabei deren `instanceEdits` — im Editor waere das eine stille, irreversible Aenderung. Der Lauf instanziiert deshalb mit `bakeContainers: false`. **Preis:** ein Behavior *innerhalb* eines platzierten Containers laeuft ausserhalb von Play nicht. | -| **ein zweites Leben** | Nur die Behaviors der **geladenen** Szene laufen, und nur im Edit-Modus. Play stoppt den Editor-Lauf — dort laeuft ohnehin jedes Behavior. Eine Exception im Graphen oder ein `Stop game`-Node beendet den Lauf und schreibt den Grund in die Notice-Zeile, statt ihn 60× pro Sekunde zu wiederholen. | - -Der zweite Punkt der Notiz faellt damit von selbst heraus: die Welt wird aus der aktiven Szene -gebaut, ein Behavior einer anderen Szene hat gar keine Entity. Beim Szenenwechsel wird der Lauf -neu aufgebaut oder verworfen. - -## Kein Save-Sturm - -Ein Behavior, das jeden Frame etwas verschiebt, macht das Modell jeden Frame schmutzig — 60× -pro Sekunde speichern waere die naheliegende Katastrophe. Zwei Dinge verhindern das: - -- `syncToModel()` meldet jetzt, **ob** ueberhaupt geschrieben wurde (`{structure, values}` statt - eines blossen `boolean`). Ein Behavior, das nur liest oder wartet, macht gar nichts schmutzig. -- `setSaveStatus('dirty')` emittiert nur bei **Wechsel**, der Autosave-Timer wird also nicht jeden - Frame neu gestellt. Gemessen: **2 Speicherungen in 2 Sekunden**, nicht ~120. - -## Ein Darstellungsfehler, den erst der Screenshot gezeigt hat - -`schemaForAsset` liefert fuer ein Behavior `COMMON_PROPS + Typschema + Variablen`. Der -Object-Inspector zeigte alles ausser `enabled` unter der Ueberschrift **VARIABLES** — der neue -Schalter landete also dort, wo nur steht, was das Behavior *deklariert*, und die Notiz „The -behavior declares no variables." verschwand fuer immer. Beides ist repariert: die Typschema-Zeilen -eines Behaviors stehen jetzt bei `Enabled`, die Sektion darunter fuehrt wieder nur die Variablen. -Aufgefallen ist es nur, weil ich mir den Screenshot angesehen habe — die Checks waren zufrieden. - -## Ein Messgeraet statt eines geratenen Offsets - -Der erste Anlauf klickte die Checkbox ueber `locate('Enabled').x + 55`. Das trifft nur Zeilen, -deren Label in die 64-px-Spalte passt — `Run in editor` ist breiter und schiebt seine Checkbox -11 px nach rechts, also ging der Klick daneben und fuenf Checks fielen um. Neu ist deshalb -`__gm.inspectorField(label)`: es liefert die Bildschirmmitte des **Controls** einer Inspector- -Zeile, gefunden ueber sein Label. Damit ist jeder kuenftige Inspector-Check unabhaengig von der -Labelbreite. - -## Belegt - -Acht Unit-Tests (`tests/editor/editor-run.test.ts`, plus einer in `container.test.ts` fuer das -Nicht-Backen). Gegengeprueft: dreht man den Filter auf „alle Behaviors" und die Szenen-Bedingung -weg, fallen **vier von sieben** um; mit `bakeContainers: true` faellt der Container-Test. -Dazu zehn E2E-Checks in Modul 17: - -``` -PASS a placed behavior does not run outside play mode until it is asked to - – {"running":false,"time":0,"behaviors":[]} x=0 -PASS the inspector offers the switch on a behavior object, with Enabled and above the variables - – {"enabledBox":{"x":1070,"y":221},"runBox":{"x":1081,"y":253},"variables":{…,"y":509.5}} -PASS switching it on is an ordinary override on the object – {"runInEditor":true} -PASS the behavior runs although Play was never pressed - – {"running":true,"time":0.46,"behaviors":["er-logic"]} mode=edit -PASS the owner drifts on, and the moved object is the stored one – x 48.33 → 108.32 -PASS what the behavior moved is saved, not just shown – stored x=308.31 (started at 0) -PASS saving stays debounced while it runs, instead of once per frame – 2 saves in 2 s -PASS taking the switch back stops the run and the object stands still – x=329.98 -PASS undo takes back the switch, never the movement it caused – overrides={} x=351.65, started at 0 -PASS the step leaves nothing of its own behind -``` - -Der Schritt baut sich seine eigene Kiste samt Behavior (`On update → Move(owner)`) statt den -Mover der Fixture zu nehmen — der laeuft ueber die Pfeiltasten, und die sieht der Editor-Lauf -absichtlich nie. Am Ende raeumt er alles wieder weg. - -Screenshot `36-editor-run`. diff --git a/todo-notes/done/expand-und-pack-node.md b/todo-notes/done/expand-und-pack-node.md deleted file mode 100644 index 9c4a99a3..00000000 --- a/todo-notes/done/expand-und-pack-node.md +++ /dev/null @@ -1,21 +0,0 @@ -beim expand und pack node habe ich mir eigentlich auch vorgestellt, dass man sich eine struct variable auswählt und automatisch die felder erzeugt werden. -wir können das so lassen wie es ist (eigene felder definieren), aber eine helfer auswahl von structs das die felder erstellt wäre gut. - ---- - -**Umgesetzt (2026-09-10).** `From variable ▾` steht in der Kopfzeile des Port-Editors neben -`+ Field ▾`; das Menü listet die Struct-Variablen des Behaviors und legt deren Felder als Ports an. - -Drei Entscheidungen, die die Notiz offen ließ: - -- **Es ergänzt nur, es schreibt nichts um.** Ein Feld, das der Node schon deklariert, bleibt stehen – - es könnte eine Kante tragen. Ist nichts mehr zu ergänzen, ist der Eintrag deaktiviert. -- **Eine Liste aus Structs zählt mit** (`shapes (item)`): sie deklariert die Form *jedes Items*, also - genau das Bündel, das ein `Pack` für sie baut. -- **Ein Color-Feld wird ein Zahlport.** `FIELD_KINDS` ist *keine* Teilmenge von `DECLARED_PORT_KINDS` – - der Graph kennt keine Farbe, im Wertemodell ist sie eine Zahl. - -Zwei Fallen unterwegs, beide gemessen: `AddPortCommand` legt genau **einen** Port an, „alle Felder auf -einmal" wäre sonst in fünf Undo-Schritte zerfallen – daher `AddPortsCommand`, das **alle** Schlüssel -prüft, bevor es einen anhängt (sonst bliebe nach einer Kollision die halbe Liste stehen). -Und ein Struct-Feld darf `exec` heißen, ein Port nicht; die Fehlermeldung sagt jetzt auch warum. diff --git a/todo-notes/done/flaky-pixel-art-filter.md b/todo-notes/done/flaky-pixel-art-filter.md deleted file mode 100644 index 4a83f080..00000000 --- a/todo-notes/done/flaky-pixel-art-filter.md +++ /dev/null @@ -1,63 +0,0 @@ -Der E2E-Check `pixel-art filtering keeps the border between two quadrants hard` faellt -**sporadisch** um — am 2026-09-09 zweimal in vier Laeufen, jeweils ohne dass eine Aenderung -Texturen beruehrt haette. - -- gruen: `["#e63c3c","#e63c3c","#3cc85a","#3cc85a","#3cc85a"]` — harte Kante, `nearest` greift -- rot: `["#c65641","#a67047","#868a4c","#66a552","#3cc85a"]` — weicher Verlauf, also `linear` - -## Erledigt: der Folge-Check deckt nichts mehr zu - -Der zweite Check (`switching the filter back to smooth …`) verglich nur `soft.some(c => !pure(c))` -und meldete damit auch dann gruen, wenn **beide** Filter denselben Verlauf zeigten — ein haengender -Wechsel liess genau einen Check fallen, der zweite verdeckte ihn. - -Er verlangt jetzt zusaetzlich, dass sich das Bild gegenueber `nearest` messbar geaendert hat -(Summe der Farbabstaende ueber die fuenf Punkte > 150). An den oben aufgezeichneten Reihen -gerechnet: echter Wechsel 425, haengender 0 — und 30 selbst dann, wenn jeder Kanal um zwei -danebenlaege. Beide Checks legen ausserdem `__gm.textureFilter(datei)` in ihren Fehlertext. - -## Das Messgeraet - -`__gm.textureFilter(datei)` liest **beides**: was das Modell will (`scaleMode`) und was der Treiber -tatsaechlich weiss (`gl.getTexParameter` an der hochgeladenen Textur). Ein FAIL beantwortet damit -von selbst, was bisher Deutungssache war: - -| Messung | Bedeutung | -|---|---| -| `scaleMode` linear, Bild weich | das Modell hat nie umgeschaltet | -| `scaleMode` nearest, `gpu` linear | der Wechsel erreichte den Sampler nicht — Produktfehler | -| beide nearest, Bild trotzdem weich | der Test misst falsch | - -Es bindet die Source ueber `renderer.texture.bindSource` statt per rohem `bindTexture` — sonst -verfaelscht die Messung Pixis Vorstellung davon, was gebunden ist, und stiftet selbst Flakiness. - -## Offen: die Ursache - -Vier Hypothesen sind gemessen und **widerlegt** — nicht erneut probieren: - -| Hypothese | Messung | Ergebnis | -|---|---|---| -| Der Wechsel erreicht den GPU-Sampler nicht | 30 Wechsel, Modell und `gl.getTexParameter` gelesen | 60/60 einig | -| Der Vorlauf der elf Module hinterlaesst etwas | 15 Runden nach echtem Vorlauf | alle gruen | -| Verlorener GPU-Prozess (Chrome meldete `exit_code=34`) | Kontext gezielt verloren und wiederhergestellt | `nearest` ueberlebt beides, auch mit Redraw | -| Genau **ein** Wechsel auf frischer Seite, wie im Test | 12 Runden, je neuer Seed und Seitenaufbau | alle gruen | - -Dass in ueber 60 gezielten Versuchen nichts faellt, waehrend der echte Lauf in ein bis zwei von -vier Laeufen umfaellt, ist der eigentliche Widerspruch. - -Naechste Spur bleibt die Umgebung: Chrome meldet beim Start auf der Windows-Maschine -`GPU process exited unexpectedly: exit_code=34`. Wie oft das passiert, ist allerdings **unbekannt** — -`harness.mjs` gibt die Browser-stderr nur bei einer Exception oder bei `RUN DIED` aus, in gruenen -Laeufen also nie. Solange das so ist, laesst sich gar nicht messen, ob der Absturz mit dem -Fehlschlag korreliert. Wer hier weitermacht, protokolliert die stderr zuerst bei jedem Lauf. - -Die Sonden dazu waren Wegwerfware und sind geloescht. Wer weitermacht, baut sie neu — beide -waren duenn: die eine fuhr den echten Modul-Vorlauf und danach eine Messschleife aus -Filterwechsel + `__gm.textureFilter`, die andere erzwang ueber `WEBGL_lose_context` einen -Kontextverlust und mass danach dasselbe. - -## Warum diese Notiz trotz offener Ursache in `done/` liegt - -Abgelegt am 2026-09-09 auf Anweisung. **Erledigt ist der Testmangel**, den die Notiz beschrieb, -plus das Messgeraet — die **Ursache der Sporadik nicht**. Faellt der Check wieder um, ist das hier -der Stand: vier widerlegte Hypothesen und die einzige offene Spur (stderr zuerst protokollieren). diff --git a/todo-notes/done/game-pause.md b/todo-notes/done/game-pause.md deleted file mode 100644 index 2e3674b2..00000000 --- a/todo-notes/done/game-pause.md +++ /dev/null @@ -1,58 +0,0 @@ -die möglichkeit das spiel zu pausieren und fortzusetzen - ---- - -**Umgesetzt (2026-09-09).** In der Toolbar steht neben *Play* ein zweiter Knopf, der zwischen -**Pause** und **Resume** wechselt; dasselbe macht **`Ctrl+Enter`** waehrend des Spielens. Gehalten -heisst: die Welt wird **nicht mehr getickt**. Damit stehen Behaviors, Timer und Kollisionen still -— und die Weltuhr steht mit ihnen, was fuer `On timer` der eigentliche Punkt ist. - -**Pause ist ein Unterzustand von Play, kein dritter Modus** (`EditorState.paused`). Alles, was -Play von Edit unterscheidet — die Laufzeitkopie, die eigene Undo-Historie, dass nichts gespeichert -wird —, gilt waehrend der Pause unveraendert; ein dritter `EditorMode` haette jede vorhandene -Abfrage `mode === 'play'` zu einer Fallunterscheidung gemacht. Der Zustand wird beim Verlassen von -Play, beim Betreten und beim Laden eines Projekts geloescht: **eine Pause kann ihren Lauf nie -ueberleben**, sonst stuende der Editor still und niemand wuesste warum. - -**Eingaben waehrend der Pause werden verworfen, nicht gesammelt.** Ein gehaltener Frame ruft -weiterhin `input.endFrame()` und `world.dropPointerEdges()` auf. Ohne das haetten sich die -Tastatur- und Klick-Flanken der ganzen Pause angestaut und waeren beim Fortsetzen in einem -einzigen Frame losgegangen — aus zehn Sekunden Pause waere ein Sprung geworden, den niemand -ausgeloest hat. - -**Warum `Ctrl+Enter` und kein Buchstabe:** waehrend des Spielens gehoert die Tastatur dem Spiel. -Ein `P` oder die Leertaste waere dem Spiel dauerhaft weggenommen. `Ctrl+Enter` startet Play schon -bisher, haelt es jetzt und laesst es weiterlaufen; `Esc` stoppt wie gehabt. - -**Belegt** — sieben Unit-Tests (`tests/editor/pause.test.ts`; ohne die Regeln fallen drei davon -um, gegengeprueft) und neun E2E-Checks in Modul 18. Der Test-Hook `__gm.playInfo()` nennt dafuer -neu die **Weltuhr**, sonst waere "steht wirklich still" nicht von "sieht gerade gleich aus" zu -unterscheiden: - -``` -PASS the box drifts on its own while playing, with nothing held down - – x 46.66 → 98.33, {"playing":true,"paused":false,"time":0.4666} -PASS pausing stops the world where it is: the box and the clock both stand still - – x 99.99 → 99.99, clock 1.00 → 1.00 over 1 s -PASS resuming lets it move on from where it stopped, without catching up the held time - – x 99.99 → 141.65, clock 1.00 → 1.40 -PASS a key pressed while paused is swallowed instead of firing on resume - – 0 hits before, 0 while held, 0 after resuming -PASS the same key does fire once the game is running again – 0 → 1 -PASS stopping clears the hold, so the next play runs - – after stop {"playing":false,"paused":false,"time":0}, x 96.66 → 138.33 -``` - -Der erste Check ist die Voraussetzung des zweiten: ohne nachgewiesene Bewegung waere "steht still" -trivial gruen. Der letzte Check derselbe Gedanke fuer die Tastatur — ohne ihn waere "keine Taste -hat gefeuert" auch dann gruen, wenn der Graph ueberhaupt nie feuert. Die Uhr im dritten Check ist -der Beweis gegen das Nachholen: sie laeuft bei **1.00** weiter, nicht bei den ~3.5, die seit dem -Anhalten vergangen sind. - -**Offen geblieben — bewusst.** Ein **`Pause game`-Node** fuer den Graphen (passend zu `Stop game` -und `Restart game`) waere der naechste Schritt, damit ein Spiel sich selbst anhalten kann -(Pausenmenue, Game Over). Ich habe dazu gefragt, aber keine Antwort bekommen, und einen -game-facing Node nicht im Alleingang eingefuehrt: ein Node ist dauerhafte API, und ohne einen -Weg zurueck **aus dem Spiel heraus** waere ein so pausiertes Spiel nur ueber die Toolbar wieder -zu starten. Beides — Node und ein fester Weiter-Weg im Spiel — laesst sich auf dem jetzigen -Stand billig nachziehen: `state.setPaused(true)` ist alles, was ein Node tun muesste. diff --git a/todo-notes/done/game-view-preview.md b/todo-notes/done/game-view-preview.md deleted file mode 100644 index 6bd6b22e..00000000 --- a/todo-notes/done/game-view-preview.md +++ /dev/null @@ -1,73 +0,0 @@ -In der Game View soll anstelle von "Press Play to run the scene here": -- das aktuelle frame von der kamera dargestellt werden -- gibt es keine aktive kamera, der text "No active camera" - ---- - -**Umgesetzt (2026-09-09).** Der Game-Tab zeigt jetzt auch ohne Play das Bild der aktiven Kamera, -und *"No active camera"*, wenn keine an ist. Der alte Hinweis *"Press ▶ Play to run the scene -here"* entfaellt ersatzlos — er stand genau da, wo jetzt das Bild ist. - -Der Tab hat damit zwei Quellen fuer dieselbe Darstellung: - -| | | -|---|---| -| **waehrend Play** | die laufende Welt, wie bisher | -| **sonst** | das Modell der aktiven Szene, als stehendes Bild | - -Beide werden durch **dieselbe** Kamera gerahmt (`activeCamera.ts`, siehe unten) und durch -dieselbe Zeichenroutine gezeichnet. Editor-Symbole (Kamera, Collider, Behavior, Container-Icon) -fallen in der Vorschau genauso weg wie beim Spielen — die Vorschau zeigt das Spiel, nicht den -Editor. - -## Zwei reine Funktionen statt einer Regel an zwei Stellen - -`src/viewport/activeCamera.ts` beantwortet **welche Kamera und wo**, fuer beide Quellen: - -- `cameraOfItems(items)` — die Modell-Kamera. `collectHolderItems` liefert bereits - Hierarchie-Reihenfolge mit aufgeloester Schalterkette, also ist "die erste eingeschaltete" - hier schlicht der erste Treffer. Damit ist es **dieselbe Regel** wie `World.activeCamera`, - nicht eine zweite Formulierung davon. -- `cameraOfWorld(world)` — die laufende Kamera, **an ihrer Weltposition**. - -## Ein Fehler, den erst die Vorschau sichtbar gemacht hat - -Der zweite Punkt ist kein Detail: `x`/`y` einer Entity sind **relativ zum Elternteil**. Der -Game-View hat sie bisher direkt als Kamerastandort benutzt. Eine Kamera, die unter einem Objekt -haengt (der uebliche Weg, sie einem Spieler folgen zu lassen), wurde deshalb an der falschen -Stelle gerahmt — der Elternversatz fehlte. Aufgefallen ist es, weil die Modellseite die -Weltposition liefert und die Vorschau damit ein **anderes** Bild gezeigt haette als Play. Der -Unit-Test haelt genau diesen Unterschied fest: die Entity meldet `{x: 20, y: 10}`, gerahmt wird -bei `{x: 320, y: 210}`. - -## Die Vorschau kostet nichts, solange man sie nicht sieht - -Sie erfordert ein Einsammeln der ganzen Szene. Ohne Bremse liefe das bei **jeder** Auswahl- -oder Modelaenderung mit, auch waehrend der Scene-Tab offen ist — vorher tat der Game-View in -dem Fall gar nichts. `redraw()` steigt deshalb aus, solange das Widget unsichtbar ist; das -Einschalten des Tabs laeuft ohnehin ueber ein `relayout`, das per `resize` neu zeichnet. - -## Belegt - -Vier Unit-Tests (`tests/viewport/active-camera.test.ts`) — drei davon fallen um, wenn man die -Schalter-Bedingung oder die Weltposition entfernt (gegengeprueft). Dazu sechs E2E-Checks in -Modul 03. Der staerkste vergleicht Vorschau und Play direkt, damit "es rendert irgendwas" nicht -als Vorschau durchgeht: - -``` -PASS the game tab previews the scene through the active camera, without playing - – {"rendering":true,"visibleItems":2,"camera":"o3"} -PASS and the crate is really drawn there, not left as letterbox – #e0553f -PASS and it is the picture Play produces: same camera, same items, same pixel on the crate - – {…,"camera":"o3"} vs {…,"camera":"o3"}, #e0553f vs #e0553f -PASS switching the camera off empties the preview and names the reason - – {"rendering":false,"visibleItems":0,"camera":""} -PASS and undo brings the preview back without a reload -``` - -Der Pixel-Vergleich ist der Kern: dieselbe Farbe an derselben Bildschirmstelle wie im laufenden -Spiel. Der vierte Check liest zusaetzlich ueber `__gm.locate('No active camera')` nach, dass der -Text wirklich auf dem Schirm steht — `rendering: false` allein waere auch bei einem schwarzen -Kasten ohne Text erfuellt. - -Screenshots `10c-game-preview` und `10d-no-active-camera`. diff --git a/todo-notes/done/graph-during-play-mode.md b/todo-notes/done/graph-during-play-mode.md deleted file mode 100644 index cb691c59..00000000 --- a/todo-notes/done/graph-during-play-mode.md +++ /dev/null @@ -1,8 +0,0 @@ -graph während dem playmode lässt sich nicht editieren. - -- abklären was zu machen wäre um das zu ermöglichen -- abklären ob es möglich wäre aktuelle werte anzuzeigen (nicht nur den wie der flow läuft sondern auch was) - ---- -Umgesetzt (User-Wahl): Änderungen im Play wirken ab dem nächsten Frame und werden beim Stop mit verworfen, wie alles im Play. Ein neuer `On start` feuert für ein schon gestartetes Behavior nicht mehr. -Werte: vor jedem Daten-Ausgang der zuletzt gelieferte Wert, 10× pro Sekunde erneuert; bei mehreren Instanzen zählt die mit dem neuesten Wert. diff --git a/todo-notes/done/graph-editor-multiselect-und-mausverhalten.md b/todo-notes/done/graph-editor-multiselect-und-mausverhalten.md deleted file mode 100644 index 1eee52ce..00000000 --- a/todo-notes/done/graph-editor-multiselect-und-mausverhalten.md +++ /dev/null @@ -1,29 +0,0 @@ -1. panning per linksklick drag soll raus, dafür beim mittelklick (aber egal ob man ins leere klick oder sich über einem node befindet) = wie bei scene view -2. mehrere nodes gleichzeitig auswählbar um dadurch mehrere zu bewegen oder zu löschen. auch mit rect-auswahl selektierbar = auch wie bei scene view -3. was anderes: wenn ich in der graph view auf "fit" klicke, dann fittet es nicht wirklich (es befinden sich nodes halb außerhalb) - ---- - -**Umgesetzt (2026-09-09), alle drei Punkte.** - -1. **Panning nur noch mit der mittleren Taste** — und zwar von ueberall, auch wenn der Zeiger - ueber einem Node steht: der NodeView laesst den Mittelklick bewusst durchblubbern, statt ihn - wie jeden anderen Klick zu schlucken. Shift+Links auf leerer Flaeche pannt zusaetzlich, genau - wie im Scene-View. Die linke Taste pannt gar nicht mehr. -2. **Mehrfachauswahl**: Ziehen auf leerer Flaeche spannt ein Gummiband auf, das jeden **beruehrten** - Node auswaehlt; Ctrl/Shift-Klick nimmt einzelne dazu oder raus; das Ziehen eines Nodes der - Auswahl bewegt alle; `Delete` (und das Kontextmenue) loescht alle — jeweils als **ein** - Undo-Schritt (`CompoundCommand`). - Bewusster Unterschied zum Scene-View: dort entscheidet die *Position* eines Objekts, hier die - *Ueberschneidung* mit dem Rechteck. Nodes sind grosse Kaesten — ein Band, das sichtbar quer - durch einen Node geht, muss ihn fangen. -3. **Fit fittet wirklich.** Die alte Fassung setzte den Zoom hart auf `1` und *zentrierte* nur — - war der Graph groesser als die Ansicht, hingen Nodes danach zwangslaeufig heraus. Ausserdem - rechnete sie mit einer *geschaetzten* Node-Hoehe (80) fuer alle, obwohl echte Nodes je nach - Portzahl deutlich hoeher sind. Jetzt wird jeder Node mit seiner **gezeichneten** Groesse - gemessen und der Zoom so gewaehlt, dass alles hineinpasst (nie ueber 1 hinaus vergroessert). - -Die Geometrie dahinter liegt als reine Funktion in `src/editor/graph/framing.ts` -(`boxesInBand`, `frameBoxes`), nicht im Panel — genau diese Rechnung war der Bug aus Punkt 3 und -war vorher von keinem Test erreichbar. Die Mehrfachauswahl selbst sitzt im Selektionsmodell -(`nodeSelection`/`toggleNode`, symmetrisch zu den Szenenobjekten), nicht im Canvas. diff --git a/todo-notes/done/graph-flow-animation-during-play-mode.md b/todo-notes/done/graph-flow-animation-during-play-mode.md deleted file mode 100644 index 150c1798..00000000 --- a/todo-notes/done/graph-flow-animation-during-play-mode.md +++ /dev/null @@ -1,5 +0,0 @@ -eine simple animation die den flow symbolisiert. vielleicht edge circles die durch fließen, jedes mal wen ein event ausgelöst wird oder so? -mach vorschläge wie das dargestellt werden könnte. - ---- -Umgesetzt (User-Wahl): Perlen auf Exec-Kanten, 0,3 s pro Durchlauf, höchstens eine alle 0,15 s je Kante. Laufen auf Spielzeit, stehen also in der Pause. diff --git a/todo-notes/done/graph-node-collapse-width.md b/todo-notes/done/graph-node-collapse-width.md deleted file mode 100644 index 415c6396..00000000 --- a/todo-notes/done/graph-node-collapse-width.md +++ /dev/null @@ -1,14 +0,0 @@ -man kann ja nun graph nodes zusammenklappen. -hier soll auch die breite veringert werden auf die titeltext breite wenn möglich? - ---- - -EDIT: - -speziell für "Get variable" node, "Set variable" node und der zukünftige "Variable" node: -Es soll in der titelbar vom node der variablennamen (falls gesetzt!) angezeigt werden (egal ob zusammengeklappt oder nicht, ist unabhängig davon) - -hier die beispiele: -- "Get dummyX" (beim node "Get variable") -- "Set dummyX" (beim node "Set variable") -- "dummyX" (beim node "Variable") diff --git a/todo-notes/done/graph-snap-grid.md b/todo-notes/done/graph-snap-grid.md deleted file mode 100644 index 7bcb0c76..00000000 --- a/todo-notes/done/graph-snap-grid.md +++ /dev/null @@ -1,19 +0,0 @@ -beim graph bräuchte ich einen toggle der grid snapping aktiviert. -alle vorhandenen nodes soll da bleiben wo sie sind, erst wenn man ein node bewegt soll es greifen -die grid-größe kannst du selbst entscheiden, am besten ein sinnvoller wert - ---- - -Gebaut. Zwei Dinge, die weder oben stehen noch aus dem Commit hervorgehen: - -**Die Rastergröße ist 24 px – dieselbe Zahl, die den Hintergrund zeichnet** (`GRID_STEP`). Es gibt -also keinen zweiten, unsichtbaren Raster: was einrastet, rastet auf einer Linie ein, die man sieht. -Bei starkem Herauszoomen blendet das Raster aus (`MIN_GRID_STEP`), der Snap-Schritt bleibt aber -derselbe – er hängt an Weltkoordinaten, nicht am Zoom. - -**Bei Mehrfachauswahl richtet sich jeder Node für sich aus** (Nachtrag des Users während der -Arbeit). Die erste Fassung verschob die Auswahl um *einen* gemeinsamen Versatz, was ihre alten, -krummen Abstände mitschleppte: nur der Ankerknoten landete auf einer Linie. Jetzt ist `dragOffset` -pro Node definiert – jeder bekommt seinen eigenen Versatz, die Auswahl fluchtet danach wirklich. -Der Preis ist bewusst: die relativen Abstände innerhalb der Auswahl können sich um bis zu einen -halben Schritt ändern. diff --git a/todo-notes/done/graph-ux-edge-drag-and-drop.md b/todo-notes/done/graph-ux-edge-drag-and-drop.md deleted file mode 100644 index 311bc7d0..00000000 --- a/todo-notes/done/graph-ux-edge-drag-and-drop.md +++ /dev/null @@ -1,28 +0,0 @@ -1. wenn ich gerade einen edge dragge und er würde nicht ins leere gehen (sprich ein passender input/ouput gehovered) wird, dann soll der input/ouput gehightlighted werden -2. wenn ich auf das node selbst droppe, soll er das erste freie passende bzw. wenn keines mehr frei ist einfach das letzt passende input/out verbinunden - auch hier gilt die prediction vom highlight (sodass man wärend dem draggen schon sieht welches input/output angeschlossen wird falls man droppt) - -EDIT: -3. Wenn ich ins leere droppe, soll sich das create node feld aufgehen und nach passenden nodes filtern die kompatibell sind (wenn von output weg, dann passende nodes vorschlagen die einen passenden input haben und umgekehrt) - ---- - -**Umgesetzt (2026-09-09), alle drei Punkte.** - -1. Der Port, auf dem die Kante landen wuerde, bekommt waehrend des Drags einen weissen Ring - (Theme-Token `portTarget`), und die gezogene Linie schnappt darauf — die Vorhersage ist damit - nicht nur farbig, sondern auch geometrisch eindeutig. -2. Drop auf den Node-Koerper nimmt den ersten **freien** passenden Port; ist keiner mehr frei, den - letzten passenden, dessen Kante dann ersetzt wird (genau wie ein Drop auf den Port selbst). - Der Ring zeigt waehrend des Drags schon genau diesen Port. -3. Drop ins Leere oeffnet die Palette, gefiltert auf die Node-Typen, die diese Kante ueberhaupt - erreichen kann (vom Output aus: passender Input, vom Input aus: passender Output; `exec` passt - nur zu `exec`, `any` zu jedem Datentyp). Der gewaehlte Node kommt fertig verdrahtet an. - -Die Entscheidung "was passt hier zusammen" sitzt komplett in `src/core/graph/dropTarget.ts` (rein, -getestet); der Canvas zeichnet sie nur. Zwei Dinge, die beim Bauen dazukamen: - -- **Node und Kante gehen als ein `CompoundCommand` rein.** Zwei getrennte Kommandos haetten fuer - eine Geste zwei Undos gebraucht. -- **`firstPortFor` statt `dropTargetPort` beim Palette-Weg**: der neue Node ist noch gar nicht im - Graph, `dropTargetPort` koennte ihn also nicht finden. Er hat noch keine Kanten, also ist der - erste passende Port genau der, den `dropTargetPort` spaeter waehlen wuerde. diff --git a/todo-notes/done/inspector-buttons-entfernen.md b/todo-notes/done/inspector-buttons-entfernen.md deleted file mode 100644 index 6ef94310..00000000 --- a/todo-notes/done/inspector-buttons-entfernen.md +++ /dev/null @@ -1,7 +0,0 @@ -- Scene: Add collider button aus insepctor entfernen -- Graph: Delete node button aus inspector entfernen - -Umgesetzt: beide Aktionen bleiben erreichbar, nur nicht mehr im Inspector. -- Collider: Rechtsklick auf das Objekt in **Scene objects** → *Add collider*. Das ist jetzt der - einzige Weg; seine Sperre (Template-Inhalt, platzierter Container) prüft e2e 09 über das Menü. -- Node: `Delete` oder Rechtsklick auf den Node → *Delete node*. diff --git a/todo-notes/done/mehrere-kameras.md b/todo-notes/done/mehrere-kameras.md deleted file mode 100644 index 8b978cc8..00000000 --- a/todo-notes/done/mehrere-kameras.md +++ /dev/null @@ -1,46 +0,0 @@ -1. kamera enabled auf false setzen bewirkt nichts, wird trotzdem gerendert -2. wenn mehrere kammeras in der scene sind, dann soll die erste gefundene die enabled ist die aktive kamera sein - ---- - -**Umgesetzt (2026-09-09).** Beide Punkte waren dieselbe Stelle: der Game-View nahm die Kamera -mit `world.firstOfType('camera')` — die **erste ueberhaupt**, ohne den Schalter anzusehen. Ein -ausgeschaltetes `enabled` konnte deshalb nichts bewirken (Punkt 1), und eine zweite Kamera kam -nie zum Zug, egal wie die Schalter standen (Punkt 2). - -An ihre Stelle tritt **`World.activeCamera()`** — die einzige Stelle, die entscheidet, durch -welche Kamera gespielt wird: - -- **geschaltet** zaehlt wie bei jedem Objekt die ganze Kette nach oben (`isHandleEnabled`): eine - Kamera unter einem ausgeschalteten Container ist ebenfalls aus. -- **"erste"** heisst in **Hierarchie-Reihenfolge**, also von oben nach unten im Hierarchie-Panel, - nicht in Speicherreihenfolge. Die beiden koennen auseinanderlaufen, sobald eine Kamera unter - einem Elternteil haengt, das spaeter gespeichert ist — dann gewinnt das, was der User sieht. -- **keine an → gar nichts wird gerendert**, wie bei einer Szene ohne Kamera. Der Hinweis beim - Start sagt jetzt in beiden Faellen dasselbe: *"No camera is switched on – nothing is rendered"*. - -Ausgewertet wird pro Frame, also wirkt ein Umschalten waehrend des Spielens sofort — auch das -per `Set enabled`-Node aus einem Graphen heraus. Das ist der Weg, wie eine Szene die Ansicht -von einer Kamera an die naechste uebergibt. - -`firstOfType` ist damit unbenutzt und geloescht. - -**Belegt** — vier Unit-Tests (`tests/core/camera.test.ts`, jeder faellt ohne den Fix um) und -sieben E2E-Checks in Modul 03. Der Test-Hook `__gm.gameInfo()` nennt dafuer neu die **objectId -der aktiven Kamera**, sonst waere "durch welche Kamera" nur zu erraten: - -``` -PASS a switched-off camera renders nothing although it is still in the scene - – {"rendering":false,"visibleItems":0,"camera":""} -PASS the switched-on camera takes over, and the game is seen through it - – {"rendering":true,"visibleItems":0,"camera":"m8ngyi2lkc"} -PASS with both switched on the first camera in hierarchy order is the active one again - – {"rendering":true,"visibleItems":2,"camera":"o3"} -``` - -Der mittlere Check ist der eigentliche Beweis fuer Punkt 2: die zweite Kamera steht bei -x = 5000, die beiden Kisten liegen am Ursprung. `visibleItems: 0` bei `rendering: true` heisst, -dass wirklich durch **ihren** Ausschnitt gesehen wird — der blosse Name der Kamera haette auch -dann gestimmt, wenn das Bild von der alten Kamera gekommen waere. - -Screenshot `10b-second-camera`. diff --git a/todo-notes/done/merge-get-set-variable.md b/todo-notes/done/merge-get-set-variable.md deleted file mode 100644 index f546bc10..00000000 --- a/todo-notes/done/merge-get-set-variable.md +++ /dev/null @@ -1,9 +0,0 @@ -Kann man einen quasi 2 in 1 node machen "Variable" - dieser soll Get/Set gleichzeichtig können -- kommt ein exec flow rein, dann wird gesetzt (vom input port - wert oder edge) -- get gibt es quasi immer, natürlich der neue wert falls er gesetzt wird - ---- - -Umgesetzt: der Ausgang liest die Variable im Moment des Abrufs, nicht beim Schreiben. -`Get variable` liest dagegen weiter einmal pro Puls — auf dieselbe Technik umstellen hiesse, -bestehende Spiele im Verhalten zu aendern (Tetris liest das Brett deshalb zweimal). diff --git a/todo-notes/done/nested-behavior.md b/todo-notes/done/nested-behavior.md deleted file mode 100644 index 2c58159b..00000000 --- a/todo-notes/done/nested-behavior.md +++ /dev/null @@ -1,104 +0,0 @@ -## 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** | diff --git a/todo-notes/done/nexted-behavior-ergaenzung.md b/todo-notes/done/nexted-behavior-ergaenzung.md deleted file mode 100644 index 5db0fb5e..00000000 --- a/todo-notes/done/nexted-behavior-ergaenzung.md +++ /dev/null @@ -1,109 +0,0 @@ -Ein paar Fragen zu behavior als node in einem graph: - -- wenn es keinen Start oder End node gibt, kann man trotztdem das behaviour in einem graph hinzufügen, aber wie läuft dann der flow? (vorallem wenn es keinen start node gibt) -- ohne end node: woher weiß der parent graph (behavior) dass das child (sub behavior) "fertig" ist? -- drag and drop vom asset inspector wäre auch gut als spwan im graph, ist das möglich? -- doppelklick auf node (type behavior) um von dem asset behavior den graph zu öffnen wäre noch gut - ---- - -## Beantwortet und umgesetzt - -Deine ersten zwei Punkte waren Fragen, die letzten zwei Wuensche. Die Antworten standen schon -im Code — der Mangel war, dass **du sie nicht sehen konntest**. Genau das ist jetzt behoben. - -### 1. Ohne Start-Node: wie laeuft der Flow? - -**Gar nicht — der Aufruf wird uebersprungen, und der Fluss laeuft am `exec`-Ausgang weiter.** -Ohne Start-Node gibt es keinen Eingang in den Sub-Graphen; der Interpreter bricht den Aufruf ab -(`if (!start) return {}`) und macht beim Aufrufer weiter. Dasselbe gilt, wenn gar kein Behavior -gewaehlt ist oder das gewaehlte geloescht wurde. Das ist Absicht: ein toter Verweis soll kein -Spiel abbrechen. - -**Das war aber still, und still ist hier falsch.** Es ist derselbe Fehlgriff wie damals beim -`Run in editor`-Schalter: der Node sah gesund aus und tat nichts. Ein Aufruf, der nichts tun -*kann*, sagt es jetzt selbst — rote Zeile auf dem Node, roter Rand, dieselbe Zeile im Inspector: - -| Lage | was der Node sagt | -|---|---| -| kein Behavior gewaehlt | `No behavior picked` | -| Behavior geloescht | `That behavior is gone` | -| Behavior ohne Start-Node | `No start node – does nothing` | -| Behavior zeigt auf sich selbst | `A behavior cannot run itself` | - -Einfuegen kann man ein Behavior ohne Start-Node weiterhin — das ist der normale Weg beim Bauen -(erst den Aufruf setzen, dann den Sub-Graphen fuellen). Es sieht nur nicht mehr fertig aus. - -### 2. Ohne End-Node: woher weiss der Parent, dass das Child fertig ist? - -**Die Frage stellt sich nicht: ein Aufruf ist immer synchron.** Der aufrufende Node kehrt erst -zurueck, wenn der Fluss im Sub-Graphen ausgelaufen ist — "fertig" ist kein Signal, sondern das -Ende der Ausfuehrung. Der End-Node hat damit nichts zu tun; er entscheidet nur, **welche Werte** -zurueckkommen. Ohne ihn kommt schlicht nichts zurueck, alles andere ist unveraendert. - -Deshalb wird ein fehlender End-Node auch **nicht** als Mangel angezeigt — das waere eine -Warnung vor etwas Erlaubtem. Ein eigener Test haelt das fest: der Sub-Graph loggt `sub`, der -Aufrufer danach `after`, und das Ergebnis ist `['sub', 'after']` — nicht umgekehrt und nicht -verschraenkt. - -### 3. Drag & Drop aus dem Asset-Panel in den Graphen - -**Gebaut.** Ein Behavior aus der Asset-Liste in den Graphen gezogen wird zu einem Aufruf-Node, -und zwar **dort, wo du losgelassen hast** (unter dem Zeiger zentriert, nicht rechts-unter ihm). -Waehrend des Ziehens zeigt ein Umriss die Landestelle. Ins **eigene** Behavior gezogen wird -abgelehnt — mit Meldung, statt einen Node zu erzeugen, der beim Laufen einen Fehler wirft. - -Das war billig, weil der Mechanismus schon existierte: `startDrag` feuert im Asset-Panel -laengst fuer jedes platzierbare Asset, und die Szenen-Ansicht ist bereits ein Drop-Ziel. Neu -ist nur, dass der Graph-Canvas jetzt auch eines ist. - -### 4. Doppelklick auf einen Behavior-Node oeffnet dessen Graph - -**Gebaut.** Doppelklick auf einen Aufruf-Node zeigt den Graphen des Behaviors, das er ausfuehrt. -Auch das nutzt vorhandene Mechanik (`pressCount`, dieselbe Erkennung wie in den Listen); der -Node waehlt einfach das Asset aus, und der Graph-Tab folgt der Selektion. - -Zusammen mit Punkt 1 schliesst sich damit ein Kreis: der Node sagt *"No start node"*, ein -Doppelklick bringt dich in den Graphen, und dort legst du den Start-Node an. - -## Belegt - -``` -PASS a behavior dragged from the asset panel becomes a call node -PASS and it lands under the pointer, not at some default spot -PASS a behavior without a start node can be added just the same -PASS and the node says why it does nothing, instead of failing silently – "No start node – does nothing" -PASS a healthy call carries no such warning -PASS dropping a behavior into its own graph is refused, out loud -PASS double-clicking a call node opens the behavior it runs -``` - -Gegenprobe gefahren: ohne die Diagnose fallen beide neuen Unit-Tests. Der zweite war beim -ersten Versuch wertlos — er wollte zeigen, dass der Node **neu gezeichnet** wird, wenn der -Mangel entsteht, prueft das aber an einem Behavior mit Ports, wo sich die Signatur ohnehin -aendert. Jetzt haengt er an einem Behavior ganz ohne Ports, wo sich wirklich nur der Mangel -aendert. - -| | | -|---|---| -| volle Suite, remote | **ALL PASSED – 387 Checks** (379 + 8 neue), 0 FAIL | -| `npm run check` lokal | gruen, **527 Tests** (+3) | - -## Ein Fehlschlag, der zweimal dasselbe war - -Der erste Drag klappte, die beiden folgenden nicht — und zwar **nur im vollen Lauf**, allein -war das Modul gruen. Ich habe zuerst die Drop-Punkte verdaechtigt (falsch: sie lagen in der -Flaeche) und dann die Reihenfolge der Module (falsch: die letzten drei reichten nicht zum -Reproduzieren). Erst eine Messung in der Check-Nachricht hat es gezeigt: die Asset-Zeile lag -bei **y = 449** statt 293 — nach 18 Modulen ist die Liste so lang, dass die zuletzt -angehaengten Behaviors **aus dem sichtbaren Bereich gescrollt** waren. Der Druck landete auf -dem Panel dahinter, es startete gar kein Drag, und das sah exakt aus wie eine Ablehnung. - -Die Testdaten liegen jetzt am **Anfang** der Liste. `__gm.dragPayload()` als neuer Hook -unterscheidet die beiden Faelle kuenftig direkt: *abgelehnt* hat einen Drag, *nie angekommen* -keinen. - -Ein Reparaturversuch am Test-Hook selbst (`rowGlobal` sollte `null` liefern, wenn der -Scroll-Container die Zeile abschneidet) habe ich **wieder zurueckgenommen** — meine Pruefung -ueber die Masken-Bounds lieferte `null` auch fuer klar sichtbare Zeilen und hat den Lauf -sofort im ersten Modul zerlegt. Lieber ein Hook, der schweigt, als einer, der falsch redet. diff --git a/todo-notes/done/node-collapse.md b/todo-notes/done/node-collapse.md deleted file mode 100644 index 091b814c..00000000 --- a/todo-notes/done/node-collapse.md +++ /dev/null @@ -1,15 +0,0 @@ -Möglichkeit Nodes zuklappen können, wichtig: soll auch gespeichert werden. - ---- - -Gebaut. Zwei Dinge, die weder oben stehen noch aus dem Commit hervorgehen: - -**Zugeklappt ist reine Darstellung, wie ein versteckter Link.** Der Graph bleibt derselbe, -`collapsed` am Node wird nur gespeichert, und ein aufgeklappter Node hinterlässt keine Spur in der -Datei. Weil ein zugeklappter Node keine Ports zum Greifen hat, verbindet ein darauf fallengelassener -Link nichts. Neue Kanten gibt es erst nach dem Aufklappen, statt einen Port zu raten. - -**Versteckte Links teilen sich am zugeklappten Node eine Marke pro Seite statt pro Port.** Alle -Ports einer Seite liegen dort auf einem Punkt. Mit einer Marke pro Port stapelten sich Stummel, -Punkt und Name auf denselben Pixeln (gemessen: zwei Marken mit je `links: 1`, identische -Koordinaten). diff --git a/todo-notes/done/nodes-mit-dynamischer-port-zahl.md b/todo-notes/done/nodes-mit-dynamischer-port-zahl.md deleted file mode 100644 index 19552682..00000000 --- a/todo-notes/done/nodes-mit-dynamischer-port-zahl.md +++ /dev/null @@ -1,360 +0,0 @@ -## Dynamische Portzahl bei Nodes ermöglichen - -Wenn das möglich ist, umsetzen. Folgende Dinge wären dafür praktisch: - -### 1. sequence node - -diese hat momentan fixe 3 outputs, diese könnten somit ebenfalls dynamisch sein - -### 2. eine variable vom typ struct - -sprich eine variable die mehreren properties (key mit allen bisher möglichen variablen typen + value). -dazu passende nodes um von einer variable alle properties zu expanden und umgekehrt (alle werte eines structs setzen) - -## variablen vom typ struct und list im inspector - -bei typ list: momentan steht es einfach nur als string geparsed in einem textfeld. -für die liste bräuchten wir eine view, sprich liste mit variablen. - -für typ struct: hier wäre natürlich auch eine passende view gut, wo man bei der variable type + key + default value angeben kann. - ---- - -lass uns das mal ausarbeiten. wie weit das sinnvoll bzw. möglich ist. - ---- - -**Umgesetzt (2026-09-10), Teil 1 und der Werttyp. Was offen bleibt, steht unten.** - -**Dynamische Ports gab es schon** — nur nicht aus Node-Einstellungen. Verschachtelte Behaviors -leben seit jeher davon: `GraphNode.ports` speichert eine Deklaration **pro Node**, `resolveDef` -loest den Def pro Node auf, `portSignature` ist der Ausloeser, an dem View und Inspector merken, -dass sich eine abgeleitete Portliste geaendert hat. Die Regel dafuer stand als Dreier-Switch auf -Node-Typen in `nested.ts` — die einzige Stelle im Projekt, die Node-Typen namentlich kannte. - -Der Switch ist jetzt **aufgeloest** in zwei Vertraege am `NodeDef`: - -- `declaresPorts: 'inputs' | 'outputs'` — der Node traegt seine eigene Portliste, editierbar im - Inspector. Das haben `Start`, `End` und neu `Pack`/`Expand`. -- `derivePorts(source)` — der Node *rechnet* seine Ports aus sich und dem Graphen aus, in dem er - sitzt. Das haben `Sequence` (aus den eigenen Kanten) und `Run behavior` (Ports des benannten - Behaviors). - -**Sequence** hatte zuerst eine Einstellung *Outputs* (2…26). Die ist **wieder weg** — siehe den -Nachtrag unten: die Ports wachsen jetzt mit den Kanten. Die Keys bleiben `a`, `b`, `c`, … statt -`out1..N`, weil im Jump-and-Run zwei Nodes an einer Sequence haengen und deren Kanten heil bleiben -sollen. - -**Die einzige echte Falle war das Schrumpfen.** `SetNodeParamCommand` war ein reiner Bag-Schreiber: -verschwand ein Port, blieb seine Kante liegen — unsichtbar (`drawEdge` steigt bei unbekanntem Port -still aus), aber sie ueberlebte jedes Speichern und waere beim Hochzaehlen wieder angesprungen. -Der Befehl ist jetzt port-bewusst und nimmt solche Kanten mit, Undo gibt sie zurueck. Das schliesst -**einen bestehenden Fehler mit**: derselbe Verwaisungs-Effekt trat schon immer auf, wenn man an -einem `Run behavior`-Node das Behavior wechselte. - -**Struct als echter Werttyp (Weg C).** `PropertyValue` ist jetzt -`number | string | boolean | ListValue | StructValue`. Felder sind Primitive oder Listen, **nie** -wieder ein Struct — aus demselben Grund, aus dem eine Liste flach ist. - -Zwei Dinge waren dafuer noetig, und beide sind der eigentliche Preis von C: - -1. **Ein Objekt auf einer Datenkante hiess bisher zwangslaeufig Entity.** An drei Stellen in - `values.ts` bedeutete „Objekt, kein Array" ein Entity-Handle. `EntityHandle` traegt jetzt eine - **Symbol-Marke** (`ENTITY_HANDLE`) — ein Symbol, damit kein Struct-Feldname sie imitieren kann, - und es serialisiert nie, was fuer etwas richtig ist, das nur waehrend des Spiels existiert. - Der Typechecker hat jede betroffene Stelle selbst benannt. -2. **`String(value)` haette einen Struct als `[object Object]` geschrieben** — an **39** Stellen. - Die gehen jetzt alle ueber `textOf()`. Das war kein Stilfehler, sondern eine echte Luecke, die - erst der neue Typ sichtbar gemacht hat. - -Dazu ein **gemessener, lebender Fehler**, der dabei aufflog und mitrepariert ist: der `List`-Node -verlor seinen Wert beim Speichern. `normalizeGraph` liess bei Node-Params nur Primitive durch, -Arrays fielen weg (`[3,1,4]` → `undefined` nach Round-Trip). Listen-*Variablen* waren nicht -betroffen — die haben ein Schema. Params haben keins, weil sie Einstellungen und Inline-Werte -mischen. - -**Belegt** (`tests/core/dynamic-ports.test.ts`): ein Bündel faehrt ueber **eine** Kante durch ein -verschachteltes Behavior — `Pack{x:30,y:12}` → `Run behavior`-Port vom Typ `struct` → `Expand` → -`Add` → 42. Genau die Form, die Tetris heute von Hand parallel fuehrt (`Next move`: 8 rein, -7 raus; 49 deklarierte Ports ueber 9 Behaviors). - -**Offen, bewusst nicht gebaut:** -- **Variablen vom Typ struct** (Teil 2 der Notiz). Dafuer braucht eine Variable eine editierbare - Feldliste im Inspector — die Deklaration liegt bisher nur am Node (`Pack`/`Expand`) und am - Behavior-Port. `writeTyped`/`mergeStruct` sind schon darauf vorbereitet: ein Struct-Slot behaelt - seine Felder, was ankommt fuellt die benannten, der Rest bleibt stehen. -- **Die Views im Inspector** (Teil 3). *[Die Listen-Haelfte ist inzwischen gebaut — siehe den - Nachtrag „der List-Tab steht". Der Struct-Teil ist weiter offen.]* Das Textfeld war nicht nur - Kosmetik: `['red, green', 'blue']` wurde als `"red, green, blue"` geschrieben und las sich als - **drei** Items zurueck — ein Komma in einem Text-Item war nicht darstellbar. Fuer den Struct ist - Zeile-pro-Feld richtig, das sind wenige. -- Ein unverbundener `list`-Eingang zeigt weiterhin ein **Zahlenfeld** (`inlineInputDef` hat keinen - `list`-Zweig), und eine Listen-Variable kann nur Zahlen-Items: `AddVariableCommand` nimmt `items` - gar nicht entgegen. - ---- - -EDIT: - -> Eine Liste braucht eher einen **eigenen Editor als Overlay** - -ok, eigener tab "List" - -dann machen wir es so: es gibt dann einfach statt dem textfeld ein button wo drinnen steht "42 items". -klickt man darauf switch zu Tab "List" und dort wird die liste aller items dargestellt die man bearbeiten kann. - -dort soll auch der selbe "renderer" verwendet werden wie beim inspector. - -wenn wir dann struct (teil 3) umsetzen, kann es auch eine liste aus struct variablen geben (bitte das bedenken) und -im tab "List" sollen variablen (egal welcher type) im prinzip genauso dargestellt werden wie im inspector - ---- - -## Antwort auf den Edit (2026-09-10): geht, mit drei Auflagen - -**Der Tab ist hier besser als das Overlay**, aus einem Grund, der erst beim Nachsehen auffiel: der -Inspector haengt **nicht** im Tab-Bereich, sondern als eigene Spalte daneben (`Editor.ts:156`). Er -bleibt in jedem Tab sichtbar, der Sprung verliert also keinen Kontext. - -**„Derselbe Renderer" ist fast geschenkt.** `createField` (`fieldFactory.ts:171`) braucht als -Kontext nur `{ project(): Project }` — keine Inspector-Instanz, keinen Command-Stack. Die Zeile -baut selbst kein Kommando, sie ruft `onInput`/`onChange`. `previewingHandlers` benutzt vom -`InspectorHost` ausschliesslich `host.state`; den Parametertyp zu verengen macht auch die -Undo-Mechanik wiederverwendbar. Ein Struct kommt spaeter an *einer* Stelle dazu: dem -`switch (def.kind)` in `fieldFactory.ts:177`. - -**Der Weg in den Tab muss ueber den Zustand laufen, nicht ueber `showTab`.** Kein Tab kennt den -`Editor`; Tab-Wechsel von innen gibt es nur selektionsgetrieben, gebuendelt in `Editor.ts:169-179`. - -### Woran der Tab haengt - -Die anderen Tabs haengen an einer Asset-Id. Eine Liste ist kein Asset, sondern ein *Slot*: -Asset-Property (`assetId+key`), Objekt-Override (`holderId+objectId+key`) oder Node-Param -(`assetId+nodeId+key`). Also eine explizite Adresse **mit Pfad**, die der Tab behaelt wie die -anderen ihre Asset-Id und die auf „nichts" zurueckfaellt, wenn das Ziel verschwindet -(`AssetTabEditor.refresh()` macht genau das). Der Drill-down spaeter ist derselbe Mechanismus -eine Ebene tiefer. - -### Zeile-pro-Item traegt — gemessen - -Zeilen echt ueber `createFieldRow` gebaut, mit GPU im Browser, dabei **jeden Frame gescrollt**: - -| Zeilen | Aufbau | Display-Objekte | fps | -|---|---|---|---| -| 0 (Grundlinie) | – | 11 | 58 | -| 200 | 15 ms | 2 011 | 51 | -| 300 | 17 ms | 3 011 | 46 | -| 3 000 | 122 ms | 30 011 | 18 | - -Dagegen die laengsten Listen im echten Bestand: **tetris 224** (`shapes`), **224** (eine -Variablen-Vorgabe), **200** (`grid`); jumpRun und fixture haben keine. Der reale Hoechstfall liegt -also bei 224 — **kein Windowing noetig**. Bei 3 000 Eintraegen faellt es auseinander, aber nichts -kommt dem nahe. - -Der eigentliche Fund steckt in der Aufbau-Spalte: 15 ms pro Rebuild bei 200 Zeilen. Deshalb kam -der Fokus-Schutz zuerst (siehe Nachtrag oben) — ein Rebuild pro Tastendruck waere dort die Regel. - -### Was „Liste aus Structs" kostet - -Gibt es heute **nicht**: `ListValue = readonly (number | string | boolean)[]` (`Properties.ts:5`), -`FieldValue` schliesst Struct aus (`:8`), `sameValue` verlaesst sich auf „recurses exactly one level -deep" (`:50`), `LIST_ITEM_KINDS` kennt kein `struct`, `coerceListItem` gibt ein Primitiv zurueck. -Das macht `ListValue` und `StructValue` **gegenseitig rekursiv** (beliebige Tiefe, echter -Deep-Compare) und die Items brauchen eine deklarierte Feldform — wieder „eine geteilte Struct-Form -hat keine Heimat" (~29 Dateien fuer einen neuen Asset-Typ). Deshalb: Tab jetzt fuer flache Listen, -aber um einen Pfad herum; die Typerweiterung kommt mit den Struct-Variablen. - -Beibehalten: eine **Liste ist homogen** (ein `ListItemKind`), der **Struct** ist das heterogene -Ding. Das Textfeld sollte als Massenbearbeitung im Tab bleiben, sonst geht das Einfuegen einer -Komma-Liste verloren. Der unverbundene `list`-Eingang (heute faelschlich ein Zahlenfeld) loest -sich mit dem Knopf mit. - ---- - -## Nachtrag (2026-09-10): Sequence waechst mit den Kanten, statt gezaehlt zu werden - -Das Zahlenfeld *Outputs* war **unbedienbar**: tippen ging nicht, Pfeil rauf/runter ging nicht, nur -das Ziehen mit der Maus. Die Ursache ist allgemein und nicht `NumberField` zuzuschreiben: - -`onInput` (jeder Tastendruck, auch jede Pfeiltaste) → `previewingHandlers` schreibt in -`node.params` → `state.preview()` → Event `project` → `Inspector.refreshValues()` → die -`nodeSignature` enthaelt die Portliste, hat sich also geaendert → **`rebuild()` wirft die Zeile -weg, in der gerade getippt wird.** Ziehen ueberlebt das, weil ein laufender Drag seine -`globalpointermove`-Events weiter am alten (zerstoerten) Widget bekommt; Tippen braucht den Fokus, -und den gibt es nach dem Rebuild nicht mehr. - -**Diese Falle ist inzwischen zu** (siehe „List-Tab" unten): `refreshValues()` verschiebt einen -faelligen Rebuild, solange der Fokus im Inspector sitzt, und holt ihn nach, sobald das Feld -verlassen wird (`FocusManager.changeHandlers` + `isWithin`). Werte sind waehrenddessen ohnehin -sicher — `TextField.setValue` ignoriert eine laufende Eingabe (`TextField.ts:92`). Damit war der -Fehler auch nie `NumberField` zuzuschreiben. - -Statt das Feld zu reparieren, ist es nach Wunsch des Users **ersetzt**: die Sequence haelt immer -**einen freien Ausgang unter dem letzten verbundenen**. Nichts verbunden = 1 Ausgang; den -Ersatz-Ausgang verbinden = ein neuer erscheint; die unterste Kante loesen = schrumpft wieder. -Gezaehlt wird der letzte *verbundene* Port, nicht die Anzahl der Kanten — so kann keine Kante -verschwinden, wenn in der Mitte eine geloest wird (dann bleibt eine Luecke stehen). - -Preis dafuer: `derivePorts` bekommt jetzt den **Graphen** statt nur der Params -(`resolveDef(node, graph, lookup)`, ~15 Aufrufstellen, alle hatten den Graphen schon zur Hand), -und `run` bekommt als viertes Argument den **aufgeloesten Def** — anders kann ein Node mit -abgeleiteten Ports nicht wissen, wie viele er hat. `SetNodeParamCommand` bleibt port-bewusst, das -braucht `Run behavior` weiterhin. - -Nebeneffekt: die Sequence braucht keinen Param mehr, also auch kein Aufraeumen von Kanten beim -Verkleinern — eine Kante zu loesen *ist* das Verkleinern. - ---- - -## Nachtrag (2026-09-10): der List-Tab steht - -**Gebaut wie im Edit gewuenscht:** eine Listen-Property ist im Inspector ein Knopf `N items`; -er schaltet auf den neuen sechsten Tab **List**, wo jedes Item eine Zeile mit *demselben* -Widget bekommt, das der Inspector fuer diese Item-Art baut (`createField` — Zahl, Text, Ja/Nein -oder Farbe). Der Inspector bleibt daneben stehen, der Kontext geht also nicht verloren. - -**Der Weg dorthin laeuft ueber den Zustand**, nicht ueber `showTab`: `EditorState.listSlot` + -`openList(slot)` + Event `list`; der Editor hoert darauf und schaltet um. Ein Slot ist eine -**Asset-Property, ein Objekt-Override oder eine Node-Einstellung** — `ListSlot` steht in -`state.ts` neben `Selection`, weil `state.ts` sonst in einen UI-Ordner importieren muesste. -`resolveList(state, slot)` in `src/editor/list/slot.ts` liefert Wert, Item-Art, Titel und die -zwei Schreibwege; die drei Faelle benutzen **exakt die Spec der passenden Inspector-Sektion**, -also dasselbe Kommando, dieselbe Beschichtung, denselben Undo-Schritt. - -**Abweichung vom eigenen Vorschlag:** kein `path` in der Adresse. Ein immer leeres Feld, das -niemand liest, ist Spekulation; die Naht, an der ein Pfad spaeter ansetzt, ist die **eine** -Funktion `resolveList` — das ist dann eine Signaturaenderung, kein Umbau. Ehrlicher so. - -**Zwei Dinge, die erst die Gegenprobe gezeigt hat:** -- Die Zeilen-Handler duerfen **nicht** auf einer Momentaufnahme der Liste sitzen. Sie ueberleben - einen Commit (der Tab baut nur bei geaenderter *Laenge* neu), also wuerde die zweite bearbeitete - Zeile die erste zuruecksetzen. Gemessen: mit Momentaufnahme faellt genau dieser Test. -- Die Beschichtung in `replace` war **redundant** — alle drei Kommandos laufen selbst durch - `coerceValue`. Raus. In `toModel` ist sie noetig: dort entsteht der Wert, der als *Vorschau* - am Kommando vorbei direkt ins Modell geschrieben wird. Auch das ist jetzt bewacht. - -**Nebenbei mitgenommen:** `showTab` schaltete fuenf `visible`-Zeilen von Hand — nicht -typgeschuetzt, ein sechster Tab waere dort still vergessen worden. Jetzt ein -`Record`, das jede Auslassung zum Typfehler macht. - -Der unverbundene `list`-Eingang zeigt weiterhin ein **Zahlenfeld**: `inlineInputDef` hat keinen -`list`-Zweig, und der Inline-Pfad kann gar keine Liste erzeugen. Der Knopf loest das nicht mit. - -**Zahlen stehen mit drei Nachkommastellen** (`30.000`). Das ist Hausentscheidung, kein Versehen: -`formatNumber` zeigt immer alle Stellen, „so a value can never look whole when it is not". - -**Weiterhin offen:** Variablen vom Typ struct, die Struct-View im Inspector, und „Liste aus -Structs" (macht `ListValue`/`StructValue` gegenseitig rekursiv — siehe oben). - ---- - -## Nachtrag (2026-09-10): drei Korrekturen am List-Tab - -**Kurze Liste baute sich nicht auf** (User: „muss die Fenstergroesse kurz aendern"). Die Ursache -lag **nicht** im Tab, sondern im `ScrollContainer` — und traf jeden seiner Benutzer, auch `Menu`: - -`rebuild()` uebergibt die Zeilen-`VStack` an `setContent()`, solange sie noch **leer** ist. Der -leere Inhalt wird ueber `Math.max(size.h, this.h)` auf die **Viewporthoehe** gesetzt. Danach kommen -die Zeilen hinein, und `relayoutContent()` rechnet fuer eine kurze Liste wieder dieselbe Hoehe aus -— `Widget.resize()` sieht keine Aenderung und **ueberspringt `layout()`** (`Widget.ts:41`). Alle -Zeilen bleiben auf dem Ursprung gestapelt. Eine *lange* Liste aendert die Hoehe und faellt deshalb -nie auf; die erste Oeffnung ueberhaupt auch nicht, weil der Tab dann noch `w=h=0` hat. - -Gemessen an Tetris, bevor etwas geaendert wurde: `colors` (7) nach `shapes` (224) → alle vier -Zeilen auf `{245,116}`; `grid` (200) → sauber gestapelt; `test` (16) → wieder kaputt. - -Fix in `ScrollContainer.relayoutContent()`: wenn die Groesse gleich bleibt, `layout()` selbst -rufen. Die Methode wird genau dann gerufen, wenn der Inhalt sich geaendert hat — sich auf die -Aenderungserkennung von `resize` zu verlassen ist dort grundsaetzlich falsch. - -**Die Kommazeile im Fuss ist raus** (User-Wunsch). Damit ist auch der Komma-Fallback in -`fieldFactory` unerreichbar geworden — jede Sektion, die eine Liste tragen kann, liefert -`openList` (`assetSection:135`, `nodeSection:134`, `objectSection:325`). Fallback, `listToText` -und `listFromText` sind geloescht; der Test dafuer ist auf `coerceValue` umgeschrieben statt -gestrichen, sonst haette `coerceListItem` seine Abdeckung verloren (Boolean-Schreibweise, Farbe). - -**JSON-Speicherung war bereits da.** Gemessen an der Projektdatei: `props.shapes` ist ein -JSON-Array mit 224 Zahlen, kein Komma-String; die PHP-API ist reines JSON. Das Komma war nur die -*Anzeige* im geloeschten Textfeld. Es bleibt genau eine Stelle, an der ein Komma-Format wirklich -persistiert wird: eine Liste, die in ein **Text**-Property rutscht, wird ueber `textOf()` zu -`"1, 2, 3"` (`Properties.ts:33`, via `coerceValue` kind `text`) — betrifft auch die Ausgabe des -`Log`-Nodes. Der User hat entschieden, das **so zu lassen**; falls eine Liste je verlustfrei durch -ein Text-Feld muss, ist das die Stelle. - ---- - -## Nachtrag (2026-09-10): Variablen vom Typ struct — Teil 2 und 3 sind zu - -**Der Werttyp war fertig, die Deklaration fehlte.** `StructValue`, `textOf`, `sameValue`, -`writeTyped`/`mergeStruct` und der Port-Typ `struct` gab es seit dem ersten Teil; `Get`/`Set -variable` haben `any`-Ports, ein Bündel fliesst also ohne Änderung durch den Graphen. Neu ist -allein, dass eine Variable eine **Feldliste** deklarieren kann: -`PropertyDef` bekommt `kind: 'struct'` mit `fields: StructFieldDef[]`, `VARIABLE_KINDS` das -fünfte Kind, `variableDef` einen fünften Parameter. - -**Ein deklariertes Feld ist ein Primitiv** (`ListItemKind` — Zahl, Text, Ja/Nein, Farbe), obwohl -`FieldValue` auch eine Liste zulässt. Grund: ein Listen-Feld bräuchte einen verschachtelten -Editor und damit den Pfad in der Slot-Adresse — genau der Schritt, auf den auch „Liste aus -Structs" wartet. Der Werttyp bleibt unverändert, die Erweiterung ist additiv. - -**`coerceValue` gibt jetzt `PropertyValue` statt `FieldValue` zurück** — der erste Werttyp, der -kein Feld ist. Gemessen: das bricht **keine** der fünf Aufrufstellen. - -### Die UI: Block statt Zeile — und *nicht* der Tab - -Der List-Tab entstand, weil hunderte Zeilen in keine Inspector-Zeile passen. Ein Struct ist der -Gegenfall: wenige Felder, die untereinander passen. Also kein siebter Tab, keine zweite -Navigation. Eine Struct-Variable ist ein **Block**: - -``` -⊞ [piece ] [+ Field ▾] [×] - # [x ] [ 30.000 ] [×] - Aa [tag ] [ tee ] [×] -``` - -Das ist der Port-Editor, den `Pack` schon hat (`portSection.ts`), eine Ebene tiefer und **mit -Wertspalte**: Typ, Key und Default-Wert an einer Stelle — genau der Wortlaut aus Teil 3 der -Notiz. Ein **platziertes Objekt** sieht dieselben Felder als reine Wertzeilen (`createField` -liefert für `struct` einen Stapel beschrifteter Zeilen): überschreiben ja, deklarieren nein. - -### Drei Dinge, die den Ausschlag gaben - -**Eine Feldänderung muss jeden Wert erreichen.** `AddStructFieldCommand` &co. fassen die -Deklaration, den Default, den Wert des Behaviors **und jeden Instanz-Override** in *einem* -Kommando an (`structSites`); sonst behält ein gespeicherter Wert eine Form, die seine -Deklaration nicht mehr verspricht. Gegenprobe: ohne die Override-Sites fallen 3 Unit-Tests. - -**Kein Retype-Knopf** — der Port-Editor hat auch keinen. Feld löschen und neu anlegen. - -**Die Handler dürfen nicht auf einer Momentaufnahme sitzen**, dieselbe Falle wie beim List-Tab: -ein Feld schreibt das *ganze* Bündel, muss es also im Moment der Änderung lesen. Gegenprobe im -Browser: mit Momentaufnahme wird `{"x":42,"tag":"ell"}` zu `{"x":30,"tag":"ell"}` — die zweite -bearbeitete Zeile belebt den alten Wert der ersten wieder. - -**Die Signatur trägt jetzt die Felder** (`defSignature`): Felder sind eigene Zeilen, ein neues -Feld muss die Sektion also neu bauen. Ohne das erscheint die Zeile erst beim nächsten Rebuild — -gemessen, genau ein Browser-Check fällt. - -**Bewusst nicht gebaut:** „Liste aus Structs", ein Struct-Feld vom Typ Liste und der unverbundene -`list`-Eingang. Die drei stehen als eigene Notiz in `todo-notes/struct-und-listen-restposten.md`. - -### Der teuerste Fund lag nicht im Struct - -Der Schritt lief **solo grün und in der vollen Suite mit drei Fehlschlägen** — identische Klick- -koordinaten, das Menü ging auf, `Boolean` lag genau unter dem Zeiger, und trotzdem passierte -nichts. Kein Notice, also war es kein `CommandError`, sondern eine **echte Ausnahme**, die das -Menü (`closeThen`: erst schliessen, dann ausfuehren) als „Klick auf den Hintergrund" aussehen -liess. - -Halbiert (`GM_STEPS=1-9,22` gruen, `1-10,22` rot) und dann im Fenster abgefangen: -`Cannot convert undefined or null to object`. Die Ursache ist ein **Fixture-Fehler**: Schritt 10 -schiebt sein Text-Objekt per `mutate` von Hand in die Szene — **ohne `instanceEdits`**, obwohl -`SceneObject` das Feld verlangt. `overrideSites` laeuft mit `Object.entries` darueber. Mein -`AddStructFieldCommand` ist die erste Stelle, die `overrideSites` aus diesem Zustand heraus ruft. - -Das Produkt bleibt unangetastet: geladen wird das Feld von `normalizeObjects` gefuellt, angelegt -von den Kommandos — ein Objekt ohne `instanceEdits` kann echt nicht entstehen. Repariert ist das -Objektliteral im Schritt. - -**Lehre fuer den naechsten Schritt:** ein Schritt, der solo gruen ist, ist nicht belegt. Nur der -volle Lauf sieht, was die Vorgaenger hinterlassen. diff --git a/todo-notes/done/on-key-down-repeat.md b/todo-notes/done/on-key-down-repeat.md deleted file mode 100644 index c76cd73d..00000000 --- a/todo-notes/done/on-key-down-repeat.md +++ /dev/null @@ -1,83 +0,0 @@ -das on key down event node bräuchte ein "repeat" -sprich wie wenn man auf der tastatur eine taste länger drückt: -- man drückt die taste, das event wir ganz normal gefeuert -- man hält länger gedrückt, nach einem bestimmten delay, feuert das event nochmal -- hält man weiterhin gedrückt, feuert das event öfter hintereinandert - -das ist ein beispiel szenario für tetris links/rechts shape bewegung - -ich denk wir brauchen beim "on key down" event zwei properties: -- repeat after (seconds) -- repeat interval (seconds, nachdem das erstmal repeat after aufgerufen wurde) - -ist repeat after auf 0 - kein effekt (alles wie bisher) -ist repeat interval auf 0 - jedes weitere frame aufrufen - ---- - -## Umgesetzt - -Genau die beschriebene Form: zwei Zahlen-Properties am `On key down`-Node. - -| | | -|---|---| -| **Repeat after (ms)** | Haltedauer bis zur ersten Wiederholung. **0 = kein Effekt**, alles wie bisher | -| **Repeat interval (ms)** | Abstand der weiteren Wiederholungen. **0 = jedes Frame** | - -Beide Felder sind **Millisekunden** — damit bleiben die ueblichen Werte ganze Zahlen (300, 50) -statt 0,3 und 0,05. Die Uhr dahinter rechnet weiter in Sekunden; umgerechnet wird beim Auslesen -der Node-Properties. - -Beide tragen `decimals: 0`: das Inspector-Zahlenfeld zeigt sonst immer drei Nachkommastellen -(`NUMBER_DECIMALS`), also stand dort `300.000`. Der Node-Koerper war davon nie betroffen — -`formatValue` gibt ganze Zahlen bar aus. - -Der erste Druck feuert immer sofort und unveraendert — die Wiederholung haengt nur hinten dran. - -### Warum das nicht der Auto-Repeat des Browsers ist - -Der Browser feuert bei gehaltener Taste selbst wiederholt `keydown`. Diese Ereignisse werden -seit jeher verworfen (`Input.press`: `if (!this.down.has(key)) ... // ignore auto-repeat`), und -das bleibt so: Verzoegerung und Rate sind eine **Betriebssystem-Einstellung des Spielers**, -kein Spielparameter. Die Wiederholung wird stattdessen aus der gemessenen Haltedauer gebaut — -damit ist sie auf jeder Maschine gleich und pro Node einstellbar. - -Die Regel selbst steht als reine Funktion in `src/core/graph/keyRepeat.ts`, nicht als Bedingung -im Runtime-Code — deshalb ist sie ohne Browser pruefbar. Die Haltedauer pro Node liegt in -`nodeState`, derselben Ablage, aus der `On timer` seine Uhr bezieht. - -### Eine Entscheidung, die ich getroffen habe - -Ein sehr kleines, positives Intervall wird auf **10 ms** angehoben — dieselbe Untergrenze, die -`On timer` schon hat. Sonst wuerde ein vertipptes `0,0001` bei einem langen Frame tausende -Aufrufe in einem Bild ausloesen. **0 bleibt davon unberuehrt** und behaelt seine eigene -Bedeutung ("jedes Frame"). - -### Der Node bleibt schmal - -Zwei Zeilen `Repeat after (ms): 0` an *jedem* Tastatur-Node waeren reiner Laerm gewesen — ich habe -mir das Bild angesehen, es war deutlich sichtbar. Beide Zeilen bleiben vom Node-Koerper weg, -solange die Verzoegerung 0 ist; der Inspector zeigt sie immer. Neu dafuer ist `NodeDef.hiddenParams`. -Sobald eine Verzoegerung gesetzt ist, erscheinen **beide** — auch ein Intervall von 0, denn das -ist dort eine Einstellung und keine Abwesenheit. - -### Der Beweis - -Zwei `On key down` auf **derselben** Taste, einer schlicht, einer wiederholend — ein einziges -Halten misst beide, damit "es hat wiederholt" nicht mit "die Taste kam oefter an" verwechselt -werden kann. - -``` -PASS a hold shorter than the delay fires once, repeat or not - – plain 0 → 1, repeating 0 → 1 over 120 ms with a 300 ms delay -PASS holding past the delay keeps the repeating node firing, while the plain one counts one press - – plain +1, repeating +12 over 800 ms (press + ~10 repeats expected) -PASS a node with no repeat set carries no repeat rows, the repeating one carries both - – plain ["Key: Right arrow"], repeating [… "Repeat after (ms): 300", "Repeat interval (ms): 50"] -PASS typing a new delay lands on the node – {"key":"ArrowRight","repeatAfter":750,"repeatInterval":50} -PASS the delay typed into the inspector is the one the run uses - – repeating 0 → 1 over 500 ms with the delay raised to 750 ms -``` - -Der letzte ist der wichtigste: derselbe Graph, nur die getippte Zahl geaendert — und der Node -schweigt. Damit haengt die Laufzeit nachweislich am Inspector-Wert und nicht an etwas anderem. diff --git a/todo-notes/done/override-darstellung.md b/todo-notes/done/override-darstellung.md deleted file mode 100644 index 527f131b..00000000 --- a/todo-notes/done/override-darstellung.md +++ /dev/null @@ -1,19 +0,0 @@ -override darstellung bei scene objects die werte von assets überschreiben: -- momentan wird es fett dargestellt => stattdessen das label in einer gelb/goldener schriftfarbe darstellen -- auch in der hierarchie der scene/container bitte den namen gelb/gold darstellen, falls es irgendein child/property gibt dass dirty ist - ---- - -**Umgesetzt (2026-09-09), beide Punkte.** - -- Inspector: das Label einer abweichenden Property ist jetzt gold (`#f2c84b`) statt fett. - Die Farbe steht als Theme-Token `override` an einer Stelle. -- Hierarchie: der Name wird gold, wenn das Objekt selbst abweicht **oder etwas darunter** — - eine zugeklappte Zeile soll nicht verbergen, dass im Teilbaum etwas geaendert wurde. - -Die Logik dazu sitzt in `src/core/properties/deviation.ts` (rein, getestet), nicht im Panel: -`objectDeviates` vergleicht gegen das, dem ein Objekt folgt — bei Container-Inhalt gegen das -**Template**, sonst gegen das **Asset** —, `deviationMarks` traegt die Marke auf alle Vorfahren -hoch. Wichtig dabei: `deviationMarks` bekommt den **gespeicherten** Holder, nicht den -aufgeloesten; in der aufgeloesten Sicht sieht Container-Inhalt wie ein normales Objekt aus und -jede Instanz waere faelschlich gold. diff --git a/todo-notes/done/overwrite-reset-nested.md b/todo-notes/done/overwrite-reset-nested.md deleted file mode 100644 index 31f982af..00000000 --- a/todo-notes/done/overwrite-reset-nested.md +++ /dev/null @@ -1,30 +0,0 @@ -folgende fehlverhalten, beispiel jump and run projekt: -- wenn ich IM container asset "level" von einem einzigen coin sprite die breite ändere von 24 => 8 -- wenn ich dann in der level scene bin und auf das geänderte coin sprite gehe und von 8 => 10 ändere funktioniert es noch -- sobald ich aber auf den original standard wert 24 ändere (oder auf den reset button klicke) dann springt es zurück auf 8 (ist korrekt bei reset, aber wenn ich explizit die ursprünglichen 24 haben will, geht das nicht, weil dieser wert nicht mehr als overwrite gilt) - ---- - -**Umgesetzt (2026-09-09).** Reproduziert im Browser am echten Jump-and-Run-Stand: nach dem Tippen -von 24 stand `{"ov":8}` da, genau wie gemeldet. Deine Deutung war richtig — der Wert galt nicht -mehr als Override und wurde weggeworfen. - -**Ursache.** Ein Instanz-Edit wurde geloescht, sobald der Wert dem Template-Objekt gleicht -(`instanceRef.setOverride`). Bei **verschachtelten** Containern ist das Template aber nicht die -Basis: `level` haelt auf seinem `c1`-Objekt einen Edit fuer das darunterliegende `coin-body` -(die 8), und der gewinnt gegen das Template im `coin`-Asset. Traegt dieses Template zufaellig -schon die 24 — was jede Ruecknahme auf den Asset-Wert in einem Container-Editor hinterlaesst, -weil ein gespeicherter Override nie geloescht wird — dann galten 24 als "gleich dem Template", -der Edit fiel weg und der Wert landete auf der Zwischenebene: **8**. - -**Fix.** `InstanceTarget` hat jetzt `base`: das Template mit den Edits **jedes Containers -dazwischen** eingewoben. Nur das ist ein Wert, auf den etwas zurueckfallen kann. Alles, was eine -Abweichung beurteilt, benutzt es: `setOverride`/`setName`/`setPosition`, die beiden -`Revert…ToTemplate`-Kommandos und die Gold-Markierung im Inspector. Das **Schreiben** von -"Apply to container" geht weiterhin ins echte Template — dort gehoert der Wert hin. - -Bei einer einzigen Ebene sind `base` und `template` identisch, das Verhalten aendert sich dort -nicht. Reset landet weiterhin auf 8 (deine Erwartung), 24 bleibt jetzt als Override stehen. - -Drei Unit-Tests in `tests/core/apply.test.ts` halten es fest; zwei davon schlagen ohne den Fix -fehl (gegengeprueft). diff --git a/todo-notes/done/overwrite-textfarbe-auch-bei-assets.md b/todo-notes/done/overwrite-textfarbe-auch-bei-assets.md deleted file mode 100644 index dbff8f01..00000000 --- a/todo-notes/done/overwrite-textfarbe-auch-bei-assets.md +++ /dev/null @@ -1,37 +0,0 @@ -die textfarbe beim asset soll auch in diesem gold dargestellt werden, falls ein property des assets einen override hat. -ist somit relativ simpel: im prinzip genau das checken was im inspector erscheint wenn man das asset auswählt => gibt es wo einen override (gold) dann auch den assetname links im panel gold darstellen. - ---- - -**Umgesetzt (2026-09-09).** Der Name einer Asset-Zeile im Assets-Panel ist gold, solange das -Asset irgendeine Property traegt, die der Inspector gold malen wuerde — dieselbe Farbe und -dasselbe Kriterium wie in der Hierarchie. - -Die Regel steht als reine Funktion in `src/core/properties/deviation.ts`, neben dem schon -vorhandenen `objectDeviates`: - -- `markedAssetSchema(asset)` — die Zeilen, die der Asset-Inspector als "modified" markiert. -- `assetDeviates(asset)` — ob davon eine von ihrem Schema-Default abweicht (`isModified`). - -**Panel und Inspector benutzen dieselbe Liste**, statt die Bedingung zweimal zu formulieren: -`buildAssetSection` baut seine Property-Zeilen jetzt aus `markedAssetSchema(asset)` (das ersetzt -die Fallunterscheidung Behavior/Rest, die dort vorher stand). Damit kann die Liste keine -Abweichung behaupten, die der Inspector danach nicht zeigt. - -**Ein Sonderfall, der eine Entscheidung brauchte: die Variablen eines Behaviors.** Sie sind Teil -von `schemaForAsset`, aber der Inspector markiert sie bewusst nie (`setModified` ist dort ein -No-op) — ihr Default ist auf der Variablen selbst deklariert, der Asset-Wert *ist* dieser -Default. Ein Behavior mit `speed = 200` ist deshalb nicht gold. Sonst waere im E2E-Fixture der -"Mover" gold gewesen, ohne dass der Inspector irgendwo etwas markiert haette — genau der -Widerspruch, den die Notiz ausschliesst. - -**Belegt** (E2E-Modul 04, sechs neue Checks ueber den Test-Hook `__gm.assetRowColor`, der die -tatsaechliche Label-Farbe der Zeile liest): - -- Crate (Hoehe 60 statt 100, eigene Farbe) → `#f2c84b`, Main camera (alles auf Default) → `#e6e8ee` -- Mover (Variable `speed` = 200 gegen deklarierten Default 0) → `#e6e8ee`, wie der Inspector -- ein ↺ zurueckgesetzt → bleibt gold, weil die zweite Abweichung noch steht -- die zweite auch zurueckgesetzt → wird schlicht, **ohne Reload**; Undo bringt die Marke zurueck - -Screenshot `15-asset-reset`: im Panel sind "Level 1" (Hintergrundfarbe), "Crate" und -"Crate collider" gold, "Main camera", "Mover", "Spawner" und das frisch angelegte "Sprite" weiss. diff --git a/todo-notes/done/refactor-types.md b/todo-notes/done/refactor-types.md deleted file mode 100644 index 818e0fa3..00000000 --- a/todo-notes/done/refactor-types.md +++ /dev/null @@ -1,23 +0,0 @@ -## Eine Refactoring-Runde speziell für Typen: - -Typen (ob jetzt "String" oder "Color" oder "Texture") werden vertreut eingesetzt: -Geh alles durch, und refactore so, dass -- wenn ein neuer Typ in Zukunft irgendwann hinzugefügt wird -- oder ein Typ geändert wird -- oder wieder wegkommt -dass es nicht ewig viele Stellen beachtet werden müssen. - -Reduziere dass auf ein Minimum, wir brauchen das so "modular" wie möglich. - -Falls es schon so passt wie es ist, dann braucht man natürlich auch nichts machen, aber ich will nur auf nummer sicher gehen. - -Falls dir bei der Arbeit noch andere Dinge auffallen die man besser/konstruktiver/schöner bauen kann, dann kannst du das natürlich gleich mitnehmen. - ---- - -Umgesetzt (Umfang "alles", inkl. Literal-Nodes). Jede Wert-Art ist jetzt eine Zeile in -`src/core/properties/kinds.ts`; die Art-Listen, Menues, Tags, Port-Felder und Literal-Nodes folgen -daraus. Ein Tag ist `'$' + kind`, also generisch. Eine neue Art ist die Zeile plus – wenn sie etwas -im Projekt benennt – ihr Picker (`fieldFactory.REFERENCES`, vom Typecheck erzwungen). -Nicht angefasst, weil kein Typ-Thema: die `type !== 'exec'`-Guards und das Zwillingspaar -`Variables.ts`/`Properties.ts`. diff --git a/todo-notes/done/reseed-because-of-list.md b/todo-notes/done/reseed-because-of-list.md deleted file mode 100644 index c699018c..00000000 --- a/todo-notes/done/reseed-because-of-list.md +++ /dev/null @@ -1,18 +0,0 @@ - -1. es gibt das Node "String", der typ heißt aber "Text", das ist inkonsistent -2. "Item at" oder "Set item" node: index hat ".000" nachkommastellen im inspector, ist aber ein integer -3. neuer typ "Texture" momentan nur in einer liste hinzufügbar => es gibt kein "Texture" node, man kann den typ auch nicht bei variablen aussuchen -4. alle project seeds überarbeiten, sodass keine migration mehr notwendig ist und reseeden. -5. checken ob es projects gibt (ohne seed) die auch noch eine migration erfordern. -6. migration code entfernen -7. unnötige comments auch wieder entfernen - ---- - -Umgesetzt. Entscheidungen dabei: -- Nur das Label des `String`-Nodes heisst "Text", Typ-Id und Porttyp bleiben. -- `Texture` (und `Color`) sind Variablen-Arten, Struct-Feld-Arten und Port-Arten; deklariert ist eine - Textur die blanke Id, der `$texture`-Tag gilt nur in Listen. Neuer Node `Texture` mit Picker. -- Jump-Run und Tetris wurden mit den aktuellen Seedern ueberschrieben; nur die fuenf Projekte auf - dem Server zaehlen. Danach sind `legacyNodes.ts`, `migrateDeclaredLists` und der - `ownerId`-Fallback samt Tests geloescht. diff --git a/todo-notes/done/show-gizmo-of-disabled-objects.md b/todo-notes/done/show-gizmo-of-disabled-objects.md deleted file mode 100644 index f66a8e43..00000000 --- a/todo-notes/done/show-gizmo-of-disabled-objects.md +++ /dev/null @@ -1,23 +0,0 @@ -objects die nicht enabled sind, sind komplett unsichtbar, das passt -aber: wenn ich sie im panel linksunten selektiere, sollte zumindest das "gizmo" (also der selektions rahmen) im scene viewport sichtbar sein - ---- - -Gebaut. Drei Dinge, die weder oben stehen noch aus dem Commit hervorgehen: - -**Der Rahmen ist gedimmt (45 %).** Ein ausgeschaltetes Objekt zeichnet nichts, ein Rahmen um leeren -Raum in voller Stärke liest sich sonst wie eine Zeichnung, die fehlgeschlagen ist. Die Eckgriffe des -primären Objekts kommen mit – sie sind reine Kennzeichnung, es gibt keinen Hit-Test daran, sie -versprechen also keine Geste, die ein ausgeschaltetes Objekt nicht kann. - -**Ziehen bleibt aus.** Der Rahmen sagt nur, wo das Objekt liegt; sein Node ist weiterhin unsichtbar -und `eventMode: 'none'`, weil `onScreen` in `WorldViewport` "aus" und "außerhalb des Sichtfelds" in -einem Ausdruck bündelt. Bewegen geht also nur über den Inspector – oder nach dem Einschalten. - -**Es greift auch bei geerbtem Aus.** `DrawItem.enabled` ist `enabledAlongChain`, also das Objekt -*und* alle seine Eltern. Ein Kind, das nur deshalb unsichtbar ist, weil sein Elternteil aus ist, -bekommt denselben gedimmten Rahmen. - -Nebenbei entstanden: `__gm.gizmos()` – vorher waren Gizmos überhaupt nicht prüfbar. Es meldet das -`alpha`, das wirklich in den Strich geht, nicht ein daneben abgeleitetes Flag; sonst hätte der Test -grün bleiben können, während gar nichts gedimmt wird (als Gegenprobe nachgestellt). diff --git a/todo-notes/done/sprite-animation.md b/todo-notes/done/sprite-animation.md deleted file mode 100644 index 643c629a..00000000 --- a/todo-notes/done/sprite-animation.md +++ /dev/null @@ -1,15 +0,0 @@ -Sprite-Animation: Frames einer Textur nacheinander abspielen. - -Stand: Texturen lassen sich schon in Frames schneiden (Slice-Dialog), aber ein Sprite zeigt immer genau einen Frame. Nichts spielt sie ab. - -Abzuklären: -- wo die Animation wohnt: eigenes Asset (Frame-Liste + fps + Loop) oder Properties direkt am Sprite -- wie man sie im Graph steuert (starten/stoppen, anderen Clip wählen, "fertig"-Event) -- Vorschau im Editor oder erst im Play - ---- -Entschieden (User-Wahl): -- Eigenes Animation-Asset (Bildliste + fps + Loop/Once/Ping-pong) mit eigenem Tab; das Sprite trägt es über `texture`. -- Kein eigener Node: gewechselt wird per `Properties` (Sprite, `texture` auf Set). Eine andere Animation startet von vorn, dieselbe läuft weiter. Dazu `On animation finished`. -- Scene/Container zeigen den ersten Frame, der Animation-Tab spielt ab, im Play läuft alles. -- State-Machine und ein Neustart-Node erst, wenn ein Spiel sie braucht. diff --git a/todo-notes/done/sprite-size.md b/todo-notes/done/sprite-size.md deleted file mode 100644 index 141ac93e..00000000 --- a/todo-notes/done/sprite-size.md +++ /dev/null @@ -1,24 +0,0 @@ -sprites haben das property width und height: - ist es hier auch sinnvoll auf integer umzuändern oder sollen wir .000 decimal dran lassen? - ---- - -**Antwort (2026-09-09): nein, bleibt fraktional — ganzzahlig waere hier schaedlich.** - -Sprite-Maße sind keine Zaehlwerte, sondern Geometrie. Zwei Stellen brauchen die Brueche: - -- Die Aspect-Ratio-Knoepfe rechnen `fitAxisToAspect` (`height * w/h`). Das liefert echte - Brueche — der Code nennt selbst `9.090909090909092` als Beispiel. Auf ganze Zahlen - gerundet stimmt die Proportion nicht mehr, und der Knopf meldet danach, dass das - Seitenverhaeltnis nicht passt (Vergleich laeuft mit Toleranz `1e-9`). -- Am Objekt sind width/height ziehbar (Scrub), genau wie Position und Collider-Maße. - -Ganzzahlig sind nur Felder, die ihrer Bedeutung nach ganzzahlig sind: Slice-Spalten/Zeilen, -Frame-Rect, Kamera-View, Font size. Die runden seit `1b16a55` auch wirklich (vorher war -`decimals` reine Kosmetik — getippte `640.5` wurden als `640.5` gespeichert und als `641` -angezeigt). - -Offen bleibt die **Anzeige**-Frage, die dahinter steckt: sollen ueberfluessige Nullen weg -(`100` statt `100.000`, `9.091` bleibt)? Das waere eine Zeile in `formatNumber` und wuerde -fuer alle Zahlenfelder gelten; der Graph macht es bereits so. Dafuer bei Bedarf eine eigene -Notiz anlegen. diff --git a/todo-notes/done/struct-und-listen-restposten.md b/todo-notes/done/struct-und-listen-restposten.md deleted file mode 100644 index 6d0999f1..00000000 --- a/todo-notes/done/struct-und-listen-restposten.md +++ /dev/null @@ -1,77 +0,0 @@ -# Restposten aus dynamischen Ports / Structs - -Drei Punkte, die beim Bau von List-Tab und Struct-Variablen bewusst liegen geblieben sind. Sie -standen am Ende von `done/nodes-mit-dynamischer-port-zahl.md` und sind hierher gezogen, damit sie -nicht mit der erledigten Notiz verschwinden. - -**Alle drei sind gebaut** — dazu der Nebenpunkt, der beim Bauen aufgefallen war. - -## 1. Der unverbundene Eingang zeigt das falsche Feld — ERLEDIGT - -`inlineInputDef` (`NodeRegistry.ts:131`) kannte nur `boolean`, `string`/`any` und *alles andere* — -und "alles andere" wurde ein **Zahlenfeld**. - -Beim Nachmessen war der Restposten **kleiner als die Notiz behauptete**: `object` und `struct` -kommen gar nicht mehr an `inlineInputDef` vorbei, `inlineInputs` siebt sie schon aus (mit der -Begründung, die die Notiz für `object` verlangt hat: ein Objekt ist ein lebendes Handle, ein -Struct wird aus Teilen gebaut). Offen war nur `list` — und dort ist es genau die Antwort geworden, -die die Notiz vorgezeichnet hat: der Knopf `n items` plus List-Tab. - -Zwei Dinge, die dabei zu entscheiden waren: - -- **Die Item-Art ist `number`.** Kein Port deklariert eine — und das ist derselbe Vorgabewert, den - `+ Variable ▸ List` setzt. Der Graph typisiert eine Liste ohnehin nur als Liste. -- **Ein verbundener Port löst nicht mehr auf.** Sobald eine Kante den Eingang speist, ist der - Inline-Wert tot; der Slot liefert `null`, statt etwas zu bearbeiten, das der Node nicht liest. - -## 2. Ein Struct-Feld vom Typ Liste — ERLEDIGT - -Es blieb bei einer Signaturänderung, kein Umbau — aber die Naht war nicht `resolveList` allein, -sondern der **Besitzer** dahinter. Die drei Fälle (Asset, Objekt, Node) liefern jetzt nur noch, -*was an `key` steht und wie man es liest und schreibt*; ob daraus die Liste selbst oder die Liste -eines Struct-Feldes wird, entscheidet ein Schritt danach. `ListSlot` trägt dafür ein optionales -`field`. - -**Die Tiefenregel steht wieder in der Deklaration, nicht in einem Kommentar:** `StructFieldKind` -(= Primitive + `list`) sagt, was ein Struct-**Feld** hält, und ein solches Listenfeld hält -ausschließlich **Primitive** (`ListFieldDef`). Genau diese eine Asymmetrie hält die speicherbaren -Formen endlich: - -``` -list → struct → primitiv -struct → list → primitiv -``` - -Tiefer geht nichts — und das ist buchstäblich das, was `asStoredValue` seit jeher durch einen -untypisierten Slot lässt. Ein Listenfeld in den Items einer Liste weist `AddStructFieldCommand` -deshalb ab. - -Geschrieben wird immer das **ganze Bündel**, neu gebaut aus dem, was in dem Moment im Besitzer -steht: nur die Liste zu schreiben würde ihre Nachbarfelder verlieren, ein Schnappschuss würde eine -zwischenzeitliche Änderung zurücknehmen. - -## 3. Liste aus Structs — ERLEDIGT - -Gebaut. Zwei Annahmen dieser Notiz waren dabei **falsch**, und beide haben es billiger gemacht: - -- Es wird **nichts gegenseitig rekursiv**. Die Tiefe steckt in der *Deklaration*, nicht in einer - Konvention (siehe Punkt 2). -- Es setzt **Punkt 2 nicht voraus**. Ein Struct-Item braucht keinen Pfad in der Adresse, weil es - **inline** gerendert wird — dieselbe Begründung, aus der ein Struct im Inspector ein Block ist - und keine eigene Navigation bekommt. `structFieldHandlers` trägt die Feld-Adresse. - -## Nebenpunkt: die Item-Art war im Editor nicht wählbar — ERLEDIGT - -`+ Variable ▸ List` legte immer `items: 'number'` an, und für `items: 'struct'` gab es keinen -Feld-Editor; eine Liste aus Structs konnte deshalb nur aus einer Projektdatei entstehen. - -- **Die Kennmarke ist der Wähler.** `≡#`, `≡Aa`, `≡✓`, `≡◧`, `≡⊞` — eine eigene Spalte gibt die - 300 px breite Leiste nicht her, und die Marke ist die eine Stelle, die es sowieso schon sagt. -- **Ein Wechsel zieht jeden Wert mit** (`SetListItemsCommand`): deklarierter Vorgabewert, eigener - Wert des Behaviors und **jede Instanz-Abweichung**, jeweils durch `coerceValue` gegen die neue - Deklaration — dieselbe Umwandlung, die ein im List-Tab bearbeiteter Posten durchläuft. -- **Eine Liste aus Structs deklariert ihre Item-Form mit demselben `+ Field`**, das eine - Struct-Variable hat — nur Namen und Arten, weil die Werte Zeile für Zeile im Tab bearbeitet - werden. Die Feld-Kommandos gelten dafür jetzt auch für den Listen-Besitzer und fassen **jedes - Item jedes Wertes** an, damit kein Wert eine Form behält, die seine Deklaration nicht mehr - verspricht. diff --git a/todo-notes/done/sub-behavior-breadcrumb-navigation.md b/todo-notes/done/sub-behavior-breadcrumb-navigation.md deleted file mode 100644 index 09e5682b..00000000 --- a/todo-notes/done/sub-behavior-breadcrumb-navigation.md +++ /dev/null @@ -1,10 +0,0 @@ -falls man im graph ein sub-behavior klick, wäre eine breadcrumb navigation gut, sodass man auch wieder zurückspringen kann. -sobald man in der hierachie (links im panel, assets/scene objects) wieder ein neues behavior auswählt bzw. über das dropdown im graph, soll das breadcrumb wieder verschwinden. -kurz gefasst: breadcrumb anzeigen sobald behaviour in graph geöffnet wird (doppelklick). -im breadcrump selbst, beispiel: "Game > Move > Other" - hier soll man im prinzip auf "Move" oder auch "Game" klicken können. in dem beispiel wenn man auf "Game" klickt, ist man wieder "Root" d.h. breadcrumb verschwindet wieder. klickt man auf "Move" wird aus dem Breadcrumb "Game > Move" - ---- -Umgesetzt (Regel in `src/editor/graph/trail.ts`): -- Oeffnet ein Doppelklick ein Behavior, das schon im Pfad steht, ist das ein Schritt zurueck dorthin statt einer Wiederholung. -- Wird ein Behavior im Pfad geloescht, verschwindet der Breadcrumb. Beim Umbenennen aendert sich nur der Eintrag. -- Der Breadcrumb erscheint vor dem Umschalten des Graphen, sonst schneidet er den Graph ab, der gerade auf die Flaeche eingepasst wurde. diff --git a/todo-notes/done/tetris-example-game.md b/todo-notes/done/tetris-example-game.md deleted file mode 100644 index 849fad36..00000000 --- a/todo-notes/done/tetris-example-game.md +++ /dev/null @@ -1,95 +0,0 @@ -## Nächstes spiel: Tetris - -Ganz simpel halten, also wirklich nur das nötigste: -- die möglichen shapes die es halt so gibt -- ein zufallspawn eines dieser shapes das langsam nach unten geht -- links/rechts pfeilstasten um zu bewegen, pfeil rauf um im uhrzeigsinn zu drehen, pfeil runter gedrückt halten um nach unten zu beschleunigen -- eine line gefüllte line solved -- obere grenze = game over, ansonsten ist es einfach ein endlosspiel -- counter ui der einfach nur die gesolved lines zählt - -was wir nicht brauchen: -- "levels", sprich dass shapes schneller werden beim runterfallen -- titlescreen oder gameover screen, wenn gameover einfach spiel stoppen -- animationen, spezielle texturen, oder sonst irgendwelchen schnick schnack -- e2e erweiterung zu dem projekt => dieses projekt braucht keine speziellen e2e tests die man immer wieder durchlaufen muss - -habe ich irgendwas vergessen? wenn ja, rückfragen, ansonsten einfach project seed anlegen und projekt generieren - -falls nodes fehlen, oder praktische nodes sinnvoll wären die noch fehlen, dürfen diese natürlich erstellt werden - ---- - -**Umgesetzt (2026-09-09).** Projekt *Tetris* (`2072b56583af`), Seed `tools/seed-tetris.mjs`, -gespielt von `tools/smoke-tetris.mjs`. Alles aus deiner Liste ist drin, nichts davon, was du -ausgeschlossen hast: keine Level, kein Titel- oder Game-Over-Screen (Game Over stoppt das Spiel -per `Stop game`), keine Animationen, keine Texturen, und **keine Erweiterung der E2E-Suite** — -der Smoke-Lauf ist wie beim Jump-and-Run ein eigenes Skript, das nur laeuft, wenn dieses Projekt -oder die Engine darunter sich aendert. - -**Das Brett ist ein Text.** 200 Zeichen, eins pro Zelle: `.` fuer leer, sonst die Ziffer der Form, -die dort liegengeblieben ist. Damit sind die beiden Operationen, auf denen Tetris steht, je ein -einziger Ausdruck statt einer Schleife: eine Zelle ist `Substring(grid, zeile·10 + spalte, 1)`, -und eine volle Reihe zu loesen ist eine leere Reihe, vor alles darueber und darunter gejoint. -Gezeichnet wird aus einem **Vorrat benannter Objekte** — `c0…c199` fuer das Brett, `p0…p3` fuer -den fallenden Stein. Waehrend des Spielens wird nichts gespawnt und nichts zerstoert. - -**Vier neue Nodes, alle allgemein und nicht Tetris-eigen** (danach gefragt, du hast (a) gewaehlt): - -| | | -|---|---| -| `Repeat` (flow) | `times` → `body` / `index` / `done`. Der Interpreter kann seitdem Schleifen: er verwirft den Datencache **pro Runde**, sonst rechnete jede Runde mit den Werten der ersten. | -| `Substring` | `count` Zeichen ab `start`; negatives `count` reicht bis zum Ende | -| `Find text` | wo ein Text zuerst vorkommt, `−1` wenn nicht — damit ist "Reihe voll" = *kein Punkt darin* | -| `To number` | die fehlende Gegenrichtung zu `To text` | - -Dazu eine Aenderung an einem vorhandenen Node: **`Object by name` nimmt den Namen jetzt als -Eingang** statt nur als Einstellung. Ohne das waeren 200 Zellen nicht adressierbar — die Zeichen- -schleife baut `"c" + index`. Alte Projekte sind unberuehrt: ein gespeicherter Name liegt in -`params.name`, und genau von dort liest ein unverbundener Eingang (eigener Test dafuer). - -**Der Kunstgriff, der den Graphen klein haelt:** jeder Frame versucht **genau einen** Zug — -Drehen schlaegt Seitwaerts schlaegt Fallen. Die Tasten bewegen nichts selbst, sie hinterlassen -einen Wunsch (`wantDx`, `wantRot`), den der naechste Frame einloest. Dadurch gibt es die -Pruefung "passen die vier Zellen dort hin?" **einmal** im ganzen Graphen statt einmal pro Taste. -Ein um einen Frame verzoegerter Seitwaertsschritt ist bei 60 fps unsichtbar; drei Kopien -derselben 30 Nodes waeren es nicht. - -**Wie das Landen ins Brett kommt:** der gelandete Stein wird nicht noch einmal aus der Formtabelle -ausgerechnet, sondern **von seinen vier Zellobjekten abgelesen** (`Get position` → Spalte/Zeile). -Sie stehen genau dort, wo der Stein ist — das Zeichnen ist die einzige Stelle, die sie anfasst. - -**Belegt** — `tools/smoke-tetris.mjs`, remote im echten Browser: **ALL PASSED, 17 Checks.** - -``` -PASS the piece falls on its own – row 1 → 4 -PASS the left arrow moves the piece one column – 4,5,4,5 → 3,4,3,4 -PASS the up arrow turns the piece – rot 0 → 1 -PASS the board draws exactly the cells the board text names – {"lit":8,"inText":8} -PASS a full line disappears and is counted – lines 1, bottom row "....44...." -PASS a piece that cannot fall at the very top stops the game – stopped -``` - -Der Brett-Check ist der wichtigste: er haelt den **Text** gegen die **Bildschirmzellen** — ohne ihn -koennte die Zeichenschleife irgendetwas anzeigen und alles andere trotzdem gruen sein. Fuer die -volle Reihe wird die unterste Reihe vorher gefuellt, damit jeder Stein sie vollmacht, egal welcher -kommt. Der Game-Over-Check wartet auf die Bedingung statt auf die Uhr — mit fester Wartezeit war -er in einem von zwei Laeufen rot, und ein Check, der wuerfelt, ist keiner. - -**Was ich selbst entschieden habe:** - -- **Kein Wall-Kick.** Eine Drehung, die nicht passt, findet nicht statt — sie wird nicht an der - Wand vorbeigeschoben. Das ist die simple Regel, und "ganz simpel" war die Vorgabe. -- **Farbe pro Form**, wie im Original. Kostet drei Nodes: die Ziffer im Bretttext sucht ihre - Farbe aus einem zweiten Text (`colors`), acht Ziffern je Farbe. -- **Drehachse** ist die Box der Form (I in 4×4, O in 2×2, Rest 3×3), nicht der Schwerpunkt. Ohne - Wall-Kick ist das das Verhalten, das am wenigsten ueberrascht. -- **Game Over = ein Stein kann ganz oben (`py ≤ 0`) nicht einmal fallen.** Kein zweiter Test beim - Erscheinen, damit die Pruefung an der einen Stelle bleibt. -- **Tempo:** eine Reihe pro halber Sekunde, gehaltene Pfeil-runter-Taste zehnmal so schnell. Beides - steht als Variable `stepTime` im Inspector. - -**Was du wissen solltest:** der Board-Container traegt **205 Objekte** (Wanne + 200 Zellen + 4 -Steinzellen). Im Editor sieht man davon nichts — die Szene zeigt acht Objekte, der Rest liegt -im Container. Wer ihn aber oeffnet, bekommt eine lange Liste. Das ist der Preis dafuer, dass -nichts gespawnt wird. diff --git a/todo-notes/done/tetris-mit-structs-neu-bauen.md b/todo-notes/done/tetris-mit-structs-neu-bauen.md deleted file mode 100644 index b46ac384..00000000 --- a/todo-notes/done/tetris-mit-structs-neu-bauen.md +++ /dev/null @@ -1,46 +0,0 @@ -Tetris Game mit Structs neu bauen -...und mit neuen `Get variables` / `Set variables` node - ---- - -**Gebaut (2026-09-10).** Beides, in zwei Durchgängen: erst die Structs, dann die Plural-Knoten. - -| | vorher | jetzt | -|---|---|---| -| `shapes` | 224 lose Zahlen | 112 `{x, y}`-Bündel | -| Zellindex | `((shape·4+rot)·8 + corner·2)`, zweimal gelesen | `((shape·4+rot)·4 + corner)`, einmal | -| deklarierte Behavior-Ports | 49 | 35 | -| Variablen im Master | 18 | 13 | -| Master-Graph | 84 Knoten / 94 Kanten | 44 / 64 | -| `next-move` | 44 Knoten | 30 | -| `clear-lines` | 29 Knoten | 23 | - -`piece` und der Kandidat `next` sind Structs aus shape/rot/x/y. `Next move` gibt einen ganzen -Kandidaten zurück, `Fits` beurteilt ihn, der Master übernimmt ihn mit *einem* `Set variable`. -Jeder Bewegungsstrang packt nur das Feld, das er ändert (`Pack{rot}`) — „dasselbe Teil, eine Reihe -tiefer" ist dadurch wörtlich ein Feld. - -## Zwei Verträge, an denen die Zusammenlegung hängt - -Beide gemessen, beide als Unit-Test gepinnt (`tests/core/variable-ports.test.ts`), beide mit -Gegenprobe abgesichert: - -- **`Set variables` schreibt je Port mit `writeTyped`**, genau wie der Einzelknoten. Ein - `Pack{rot}` wird deshalb weiterhin per `mergeStruct` eingemischt statt das Bündel zu - überschreiben. Ohne das hätte Drehen die anderen drei Felder gelöscht. -- **Ein Datenknoten wird einmal pro Puls ausgewertet und behält die Antwort** (`dataCache`, - geleert je Schleifenrunde). Ein `Get variables` darf deshalb keine Schreibung einer seiner - Variablen überspannen — sonst liest der spätere Verbraucher den Wert von davor. Deshalb sind - `grid`/`outgrid` in `clear-lines` weiterhin zwei Knoten, und `take`/`keep` bleiben getrennt. - -Die Cache-Regel ist im **Spiel** nicht messbar: eine absichtliche Verletzung ließ den Smoke grün, -weil `OnUpdate` jeden Frame neu liest und der Fehler sich einen Frame später selbst heilt. Der -Unit-Test fällt dagegen sauber (`py: 7` statt `1`). - -## Was bewusst nicht passiert ist - -Das **Brett bleibt eine flache Zahlenliste**. `-1`/Formnummer ist bereits die kompakte Kodierung; -ein `{shape}`-Struct pro Zelle wären 200 Bündel für ein einziges Feld. - -Eine **Listen-Variable kann ihre Item-Art im Editor nicht wählen** — eine Liste aus Structs -entsteht heute nur aus einer Projektdatei. Steht in `struct-und-listen-restposten.md`. diff --git a/todo-notes/done/tetris-und-data-logik.md b/todo-notes/done/tetris-und-data-logik.md deleted file mode 100644 index b0e43f74..00000000 --- a/todo-notes/done/tetris-und-data-logik.md +++ /dev/null @@ -1,94 +0,0 @@ -## Tetris Game verbessern - -das ist erst relevant wenn behavior als node funktionieren - -hier ein paar fragen vorweg: -- habe ich gesehen dass es die variablen `grid`, `shapes`, `colors` und `spawnX` gibt. diese sind sehr kryptisch. welche nodes fehlen, bzw. wie kann man das schöner gestalten. -- man könnte jetzt das ganze auch in mehrere behavior strukturell aufsplitten und diese in das `Tetris`-Behavior als Node einbetten - ---- - -## Beantwortet und umgesetzt - -Beide Punkte hatten dieselbe Ursache, und die lag nicht im Tetris-Projekt: **der Engine fehlte -ein Datentyp.** Eine Variable konnte nur Zahl, Text oder Ja/Nein sein (`PropertyValue`), also -musste jede Menge von Werten in einen String kodiert werden. Genau daher kamen deine vier -kryptischen Variablen — nicht aus einer schlechten Wahl beim Bauen. - -### 1. Die kryptischen Variablen: es fehlte die Liste - -`list` ist jetzt eine Variablen- und eine Port-Art. Sie hält Zahlen, Texte, Ja/Neins oder -**Farben** und wird im Inspector als eine kommagetrennte Zeile getippt. - -| Variable | vorher | jetzt | -|---|---|---| -| `grid` | 200-Zeichen-Text, `.` = leer | Liste aus 200 Zahlen, `-1` = leer | -| `shapes` | 224 Ziffern am Stück | Liste aus 224 Zahlen | -| `colors` | `"03066326048763714..."` – Farben als 8-stellig gepaddete Dezimalzahlen | Farbliste: `#2ec9d6, #f5c542, …` | -| `spawnX` | `"3433333"` | `3, 4, 3, 3, 3, 3, 3` | - -Dazu acht neue Nodes, alle allgemein statt Tetris-eigen: **`List`** (Konstante), **`Fill`** -(ein Wert wiederholt – so entsteht ein leeres Brett), **`Count`**, **`Item at`**, **`Set item`**, -**`Sub list`**, **`Join lists`**, **`Index of`**. - -Was das im Graphen ausmacht: eine Zelle lesen war `Substring(grid, i, 1)` + `Equals '.'`, jetzt -`Item at(grid, i) >= 0`. Eine Landung schreiben war **sieben** Nodes (zweimal `Substring`, -zweimal `Join`, `To text`, `Add`), jetzt **einer**: `Set item`. - -**Bewusst nicht gebaut:** eine Liste *von Objekten*. Eine Liste wird in die Projektdatei -geschrieben, ein Objekt ist ein Handle, das keinen Reload überlebt — eine Objektliste wäre die -erste Wertsorte, die beim nächsten Laden still verschwindet. Objekte gehen weiter über Namen -(`Repeat` + `Item at` + `Object by name`), und eine spätere Item-Art `reference` (stabile Id) -wäre reines Hinzufügen, kein Bruch. - -### 2. Aufgesplittet: aus einem Graphen sind zehn geworden - -Erst waren es fünf; auf deinen Einwurf hin habe ich auch die einzelnen **Schritte** eines Frames -zu Behaviors gemacht, die das Master-Behavior nur noch setzt. - -| Behavior | Aufgabe | Nodes | -|---|---|---| -| `Spawn piece` | welcher Stein als nächstes kommt und in welcher Spalte | 5 | -| `Next move` | welchen **einen** Zug dieser Frame versucht | 44 | -| `Fits` | passen die vier Zellen dorthin? | 18 | -| `Draw piece` | den fallenden Stein an seinen Platz stellen | 14 | -| `Lock piece` | die gelandeten vier Zellen ins Brett schreiben | 20 | -| `Clear lines` | volle Reihen entfernen und zählen | 29 | -| `Show lines` | den Zähler auf den Schirm schreiben | 5 | -| `Draw board` | das Brett zeichnen | 11 | -| `Piece cell` | wo eine der vier Zellen eines Steins sitzt | 12 | -| **`Tetris`** | **nur noch: in welcher Reihenfolge, und wohin die Antwort** | **84** (vorher 219) | - -`Piece cell` war der erste Gewinn: dieselben zwanzig Nodes standen **zweimal** im Graphen. Jetzt -einmal — und drei der anderen rufen es auf, Sub-Behaviors im Sub-Behavior. - -**Keins der neun hält Zustand**, genau dein „kann man über input/output lösen". `Next move` ist -das Beispiel dafür: es *bewegt* nichts, es schlägt einen Zug vor und gibt ihn zurück -(`nx, ny, nrot, kind, fall, wantDx, wantRot`). Ein eingelöster Tastenwunsch kommt als `0` -zurück — so wird ein Tastendruck genau einmal ausgegeben, ohne dass das Sub-Behavior die -Variablen des Aufrufers kennt. - -**Woran man misst, dass der Master wirklich nur noch dirigiert:** in seinen 84 Nodes steckt -**keine Schleife und kein Listenzugriff mehr** — kein `Repeat`, `Item at`, `Set item`, -`Get position`, `Set position`, `Set text`. Das prüft der Smoke-Lauf, damit die Arbeit nicht -unbemerkt zurückwandert: - -``` -PASS the master calls every step as a node, and does none of their work itself - calls: spawn-piece, next-move, fits, draw-piece, lock-piece, clear-lines, show-lines, draw-board - work: [] -``` - -`Draw piece`, `Draw board` und `Show lines` haben **keinen End-Node**: sie beantworten nichts, -sie zeichnen nur. - -### Ein Fehlschlag unterwegs - -Der Game-Over-Check fiel um — und die Messung nannte den Grund selbst: `lines: 20`. Meine -Vorlage für „Brunnen voll" bestand aus 19 **vollen** Reihen, die `Clear lines` wegräumte, bevor -der neue Stein überhaupt anstand. Der Check maß also das Aufräumen statt das Spielende. Jetzt -lässt jede Reihe ihre letzte Spalte frei: blockiert, aber nirgends vollständig. - -Und beim Gegenprüfen fiel auf, dass mein erster Persistenz-Test nichts bewies — er lief über den -schema-geführten Weg, während der Filter, den ich repariert hatte, für Container-Instanzen gilt. -Beide Wege sind jetzt einzeln abgedeckt. diff --git a/todo-notes/done/texture-asset-to-sprite.md b/todo-notes/done/texture-asset-to-sprite.md deleted file mode 100644 index aea67b7f..00000000 --- a/todo-notes/done/texture-asset-to-sprite.md +++ /dev/null @@ -1,9 +0,0 @@ -rechtsklick auf texture asset => menüeintrag: create sprite from texture -selbsterklärend oder? (es soll im prinzip ein sprite asset erstellt werden wo automatisch die ausgewählte textur zugewiesen ist) - ---- - -Umgesetzt: das Sprite bekommt die **Pixelgröße des Bildes** (Wahl des Users) und heißt wie die -Textur, nach der üblichen Regel durchnummeriert (`pasted` → `pasted 2`). Ist die Bildgröße noch -nicht bekannt, bleibt es bei 100 × 100. Die Start-Werte gehen beim Ausführen durch das Schema wie -jede andere Änderung; ein Undo nimmt das Asset in einem Schritt heraus. diff --git a/todo-notes/done/unreferenced-image-files.md b/todo-notes/done/unreferenced-image-files.md deleted file mode 100644 index 99c99773..00000000 --- a/todo-notes/done/unreferenced-image-files.md +++ /dev/null @@ -1,40 +0,0 @@ -Zitat aus der readme: "Unreferenced image files are never collected." - -Ich würde sagen dass soll vorerst auch so bleiben. - -Was passt: - drag and drop file in den editor => textur wird hochgeladen und passende asset wird erstellt => das soll so bleiben, das passt - -Was wir auch haben aber ändern: - klickt man auf das `+` => `Texture`, dann öffnet sich der File System Browser. - -Anstelle des file system browser => ein dialog mit allen images die am server existieren. - - auswählen => ok, dann wird es wiederverwendet => dialog schließt sich => texture asset mit ausgewählten image wird erstellt. - - oder hochladen button => file system browser => OK => dialog schließt sich => texture asset mit hochgeladenen image wird erstellt (im prinzip das was wir jetzt auch haben) - ---- - -**Umgesetzt (2026-09-09).** Der Weg ueber `+` → `Texture` oeffnet jetzt einen Bild-Dialog statt -des File-Browsers; Drag&Drop und Ctrl+V bleiben unveraendert, und gesammelt wird weiterhin nichts. - -**Der Dialog** (`src/editor/texture/ImagePicker.ts`) zeigt jede Datei, die der Server haelt, als -Kachel mit Vorschaubild. Auswaehlen und **Use image** legt ein Texture-Asset auf diese Datei an — -ohne Upload, die Bytes liegen ja schon da. **Upload…** oeffnet den File-Browser und muendet in -genau denselben Import wie ein Drop. Doppelklick auf eine Kachel ist die Kurzform von *Use image*. - -Kacheln, die das Projekt schon traegt, sind mit dem **Asset-Namen** beschriftet (statt mit der -Pixelgroesse); eine davon zu waehlen legt kein zweites Asset an, sondern selektiert das -vorhandene — zwei Assets auf einer Datei wuerden sich sonst ueber `filter` streiten. - -**Zwei Aenderungen, die dazu noetig waren:** -- `GET ?r=textures` liefert jetzt auch `width`/`height` (aus dem Datei-Header). Ein Texture-Asset - haelt die Pixelgroesse, und der Dialog haette sie sonst nur ueber das geladene Vorschaubild - gehabt — dann haenge die Richtigkeit des Assets am Ladezustand der GPU-Textur. -- `TextureLibrary.load(file)` neben `get(ref)`: laedt, **ohne** den `scaleMode` anzufassen. Ein - Vorschaubild darf den Filter eines Bildes nicht umstellen, das die Szene gerade zeichnet. - -**Belegt** (`tools/e2e/12-textures.mjs`, Block 19e): ein zweites Bild wird eingefuegt, das Asset -per Undo wieder entfernt — die Datei bleibt auf dem Server, das Projekt kennt sie nicht mehr. -Genau die taucht dann im Dialog auf, ist *nicht* als "im Projekt" markiert, und ueber *Use image* -entsteht daraus wieder ein Asset mit 24 × 24 aus dem Server-Datensatz. Zusaetzlich geprueft: der -Dialog zeigt exakt die Dateiliste von `GET ?r=textures`. diff --git a/todo-notes/done/variables-node-title.md b/todo-notes/done/variables-node-title.md deleted file mode 100644 index 2927735d..00000000 --- a/todo-notes/done/variables-node-title.md +++ /dev/null @@ -1,12 +0,0 @@ -ok, title bei variables node NUR wenn zusammengeklappt ändern: -- mehr als 1 set => "Set {n} variables" -- ansonsten bei mehr als 1 get => "Get {n} variables" -- ansonsten bei 1 set => "Set {name}" -- ansonsten bei 1 get => "Get {name}" -- ansonsten => "No variables" - -dasselbe bei properties node - ---- -Umgesetzt: aufgeklappt heissen die Nodes immer `Variables` / `Properties`. Der Typ des `Properties`-Nodes steht deshalb als Zeile unter dem Titel (`Type: Sprite`, bei Behavior zusaetzlich `Behavior: `). -„Get {n}“ zaehlt nur Ports ohne Set. diff --git a/todo-notes/flaky-pixel-art-filtering.md b/todo-notes/flaky-pixel-art-filtering.md deleted file mode 100644 index 70a20f21..00000000 --- a/todo-notes/flaky-pixel-art-filtering.md +++ /dev/null @@ -1,4 +0,0 @@ -e2e-Check `pixel-art filtering` in `12-textures` fällt gelegentlich, im Nachlauf grün. - -Stand: fällt mit `gpu:"none", uploaded:false`. Ausgeschlossen: der Filterpfad selbst, 30 Wechsel in einer Sitzung, Kontextverlust. Verdacht: Timing/Umgebung. -Messgerät: `__gm.textureFilter(datei)` (Modellwert und Treiberzustand). diff --git a/todo-notes/frame-edge-bleeding.md b/todo-notes/frame-edge-bleeding.md deleted file mode 100644 index 2aefbd90..00000000 --- a/todo-notes/frame-edge-bleeding.md +++ /dev/null @@ -1,20 +0,0 @@ -Ein Sprite, das ein Frame aus einem Sheet zeigt, zeichnet am Rand eine Pixelzeile, die zum -Nachbarframe gehört. Sichtbar im Projekt *Actor demo* als heller Streifen über dem Kopf. - -Gemessen (`__gm.pixelAt`, Spielansicht): Beim Stehen nach unten (Sheet-Zeile 0, deren oberste -Pixelzeile in der Datei komplett leer ist) liegt dort `#394349` – bläulich. Das passt zur -*untersten* Sheet-Zeile (Schuhe von Character 6, `(87,87,154)`) über den grünen Hintergrund -gemischt, die Kante bricht also auf die Gegenseite um. Beim Laufen nach rechts (Zeile 2) ist der -Streifen stattdessen hellgrau – die Füße aus Zeile 1 direkt darüber. - -Ursache: `frameFillMatrix` (`src/viewport/textureFill.ts`) bildet die **ganze** Textur auf die Shape -ab, weil Pixi-v8-Graphics-Fills `Texture.frame` ignorieren. Eine Fill-Matrix kann keine Grenze -ausdrücken, also sampelt die Randzeile in den Nachbarn, sobald die Skalierung nicht ganzzahlig ist -(die Spielansicht streckt 640×360 auf etwa 735×409, Faktor ~1,13). - -Nicht die Ursache: die Frame-Rechtecke. Die sind exakt ganzzahlig; den fraktionalen Fall behandelt -`normalizeFrameRect` bereits. - -Abzuklären: halber Pixel Einzug in der Matrix (kostet einen halben Pixel Bildinhalt, trifft jedes -Frame) oder ein echtes `Sprite` mit `new Texture({source, frame})` statt des Graphics-Fills (Pixi -klemmt dann selbst, ändert aber den Zeichenweg für alle Sprites). diff --git a/todo-notes/graph-comments-groups.md b/todo-notes/graph-comments-groups.md deleted file mode 100644 index 3ccb6142..00000000 --- a/todo-notes/graph-comments-groups.md +++ /dev/null @@ -1,6 +0,0 @@ -Kommentare und Gruppen im Graph: eine beschriftete Box um mehrere Nodes, damit grosse Graphen lesbar bleiben. - -Abzuklären: -- nur Box mit Titel, oder auch freier Kommentar-Text -- verschiebt die Box ihre Nodes mit -- zuklappbar wie ein Node? diff --git a/todo-notes/load-scene-node.md b/todo-notes/load-scene-node.md deleted file mode 100644 index 1fbaea9d..00000000 --- a/todo-notes/load-scene-node.md +++ /dev/null @@ -1,7 +0,0 @@ -Mehrere Szenen in einem Lauf: ein `Load scene`-Node. - -Stand: ein Lauf kennt genau eine Szene, Bildschirme sind heute Container, die man ein- und ausschaltet (Jump-Run: Title/Level/Win/Lose). - -Abzuklären: -- was beim Wechsel erhalten bleibt (globale Variablen? ein "persistentes" Objekt?) -- Startszene des Projekts festlegen diff --git a/todo-notes/nginx-gzip.md b/todo-notes/nginx-gzip.md deleted file mode 100644 index ce6a165d..00000000 --- a/todo-notes/nginx-gzip.md +++ /dev/null @@ -1,5 +0,0 @@ -gzip im nginx einschalten. - -Stand: das Bundle geht mit 589 827 Bytes unkomprimiert raus, obwohl der Browser gzip anbietet; komprimiert wären es 184 kB. `gzip` ist in `/_stacks/web/nginx.conf` aus. - -Achtung: die Datei gilt für alle Seiten im Web-Stack, nicht nur für den game-maker. Nur auf ausdrücklichen Auftrag anfassen. diff --git a/todo-notes/nine-slice.md b/todo-notes/nine-slice.md deleted file mode 100644 index 9c8e33de..00000000 --- a/todo-notes/nine-slice.md +++ /dev/null @@ -1,5 +0,0 @@ -Nine-Slice für Sprites: Rahmen und Knöpfe behalten beim Skalieren ihre Ecken, nur die Mitte und die Kanten werden gestreckt. - -Stand: ein Frame wird immer auf die Grösse des Sprites gestreckt (steht auch unter "Not yet" in der README). - -Abzuklären: die vier Ränder am Frame/an der Textur festlegen oder am Sprite. diff --git a/todo-notes/on-start-during-play.md b/todo-notes/on-start-during-play.md deleted file mode 100644 index 7a38dd74..00000000 --- a/todo-notes/on-start-during-play.md +++ /dev/null @@ -1,10 +0,0 @@ -Ein im Play neu angelegter `On start`-Node feuert nicht für Behaviors, die schon laufen. - -Stand: seit der Graph im Play bearbeitbar ist, wirken Änderungen ab dem nächsten Frame — nur `On start` nicht, weil der Start schon vorbei ist. - -Abzuklären: nachträglich feuern (einmal pro laufender Instanz) oder bewusst so lassen und im Editor darauf hinweisen. - -EDIT auch noch abzuklären: Wann feuert `On start`: -- nur beim ersten Frame? -- muss das behavior enabled sein? -- wenn ein behavior später enabled wird, feuert `On start` dann noch einmalig? diff --git a/todo-notes/palette-exact-match.md b/todo-notes/palette-exact-match.md deleted file mode 100644 index d3a556ba..00000000 --- a/todo-notes/palette-exact-match.md +++ /dev/null @@ -1,3 +0,0 @@ -Node-Palette: Treffer im Namen vor Treffern in der Beschreibung ziehen. - -Stand: sortiert wird nach Kategorie, dann alphabetisch; gesucht wird auch in Typ und Beschreibung. Suche "variable" + Enter landet deshalb auf `Run behavior` (Kategorie flow, Beschreibung enthält "variables"), nicht auf `Variables`. diff --git a/todo-notes/render-on-change.md b/todo-notes/render-on-change.md deleted file mode 100644 index 706697ef..00000000 --- a/todo-notes/render-on-change.md +++ /dev/null @@ -1,5 +0,0 @@ -Editor nur bei Änderungen neu zeichnen, im Play weiter durchgehend. - -Stand: ein stehendes Bild kostet im Editor gemessen 69 % eines Kerns (Lüfter, Akku). Seit dem Low-Latency-Modus (`desynchronized`) verlängert ein Frame auf Abruf die Reaktionszeit nicht mehr. - -Abzuklären: was alles ein Neuzeichnen auslösen muss (Eingabe, Hover, Laden einer Textur, Perlen/Live-Werte im Graph während Play). diff --git a/todo-notes/sound.md b/todo-notes/sound.md deleted file mode 100644 index d20f6617..00000000 --- a/todo-notes/sound.md +++ /dev/null @@ -1,8 +0,0 @@ -Sound: Audio-Assets und ein Node zum Abspielen. - -Stand: es gibt keinerlei Audio, weder Asset-Typ noch Node. - -Abzuklären: -- Import wie bei Texturen (Datei hochladen) und welche Formate -- Node: abspielen, stoppen, Lautstärke, Loop (Musik vs. Effekt) -- Browser spielen erst nach der ersten Nutzergeste Ton ab diff --git a/todo-notes/template-changes-to-instances.md b/todo-notes/template-changes-to-instances.md deleted file mode 100644 index 369a6467..00000000 --- a/todo-notes/template-changes-to-instances.md +++ /dev/null @@ -1,5 +0,0 @@ -Struktur-Änderungen an einem Container-Template erreichen bestehende Instanzen nicht. - -Stand: Objekt im Template hinzufügen, löschen oder umhängen wirkt nur bei der nächsten Platzierung. Schon platzierte Instanzen behalten die alte Struktur. - -Abzuklären: was mit Overrides passiert, die an einem gelöschten Template-Objekt hängen.