diff --git a/Architecture.md b/Architecture.md index fcd7db6..cf8558a 100644 --- a/Architecture.md +++ b/Architecture.md @@ -86,7 +86,8 @@ src/runtime/ World, Entity, System interface, Input tracker, Collision Audio (the AudioSink a run plays into — the counterpart of Input on the way out) src/runtime/physics/ engine (loads Rapier once, reports its state, converts between pixels and metres), PhysicsSystem (Play's collisions: one engine body per rigidbody, free collider and tilemap, - the fixed step, the poses handed back, the contact pairs sorted into events) + the fixed step, the poses handed back, the contact pairs sorted into events; the push a + `Move` gives a dynamic body, and what the physics nodes reach through `NodeContext.physics`) src/ui/ widget toolkit (generic, knows nothing about assets) src/viewport/ view (transforms), items (what to draw), symbols (how to draw it), FileLibrary (load once, never retry a failure, tell the listeners) ← TextureLibrary, FontLibrary; diff --git a/Behaviors.md b/Behaviors.md index c26ba9f..96b3196 100644 --- a/Behaviors.md +++ b/Behaviors.md @@ -107,14 +107,15 @@ shown on the node and editable in the inspector. `Join lists`, `Index of`, structs `Pack`/`Expand`, `Length`, `Distance`, `Normalize`, `Axis input` (four keys → a normalized direction, so diagonals are not faster), `Compare`, `And/Or/Not`, `Key is down`, - `Mouse position`, `Mouse button`, `Is hovered`, `Is touching`, `Current state` (name and id), `Is in state` (one boolean per state) · + `Mouse position`, `Mouse button`, `Is hovered`, `Is touching`, `Get velocity`, `Current state` (name and id), `Is in state` (one boolean per state) · actions `Move` (dx, dy, scale, dt — connect an event's dt for per-second movement; in the object's own axes, which a turned parent turns too), `Set position`, `Spawn` (asset setting + x/y/z, optional parent → outputs the new object), `Destroy`, `Emit` (starts a particle source over, see *Particles*), `Play sound` / `Stop sound` (see [Sounds](Sounds): the asset says how loud and whether it loops, the node's `volume` multiplies that and its `loop` either leaves it alone or overrules it in either direction; every play is a voice of its own, so two in a row overlap, and the voice ends with the behavior that started it unless `detached` says otherwise), - `Load scene` (swaps the running scene once the frame is over — see [Scenes](Scenes)), `Log`, `Stop game`, `Restart game`. A node type is one file exporting a `NodeDef` + `Load scene` (swaps the running scene once the frame is over — see [Scenes](Scenes)), `Log`, `Stop game`, `Restart game`, + `Apply force`, `Apply impulse`, `Set velocity`, `Set angular velocity`, `Set gravity` (see [Physics](Physics)). A node type is one file exporting a `NodeDef` (ports, settings, `run`), listed in `nodes/index.ts`; palette, ports, validation and inspector fields are generated from it. A port may declare a `default` for its inline value (e.g. `Move.scale = 1`), shown on the node and in the inspector. diff --git a/Colliders.md b/Colliders.md index 4a5f942..115c67a 100644 --- a/Colliders.md +++ b/Colliders.md @@ -28,7 +28,8 @@ object stands for its collider children, a collider for itself. The geometry liv parent turns as well — and, after each axis, pushes its *solid* colliders back along that axis to just touch any solid collider they ran into (`exitDistance` in `collider.ts`: the travel at which the shapes stop overlapping) — so the other axis still moves and the object slides along walls. Its `blocked x` / `blocked y` outputs tell which -axes hit (always `false` with the setting off). A collider with +axes hit (always `false` with the setting off). In Play a dynamic body that stops the move is pushed +along (see [Physics](Physics)). A collider with **Trigger (no blocking)** enabled (`isTrigger`, drawn amber instead of green) still produces collision events but never blocks. An overlap that already exists is never resolved (no jolt when objects start stacked or inside a wall) but also never deepened, so such an object can diff --git a/Development.md b/Development.md index 7d9a43c..cee4d47 100644 --- a/Development.md +++ b/Development.md @@ -12,7 +12,7 @@ npm run check # typecheck + lint + format:check + tests, the gate CI runs # code is lost (tail's is reported), so a red run reads as green npm run build # writes dist/ and regenerates index.html ./build.sh # the same under `set -o pipefail`, for callers that pipe the output -node tools/seed-e2e.mjs && node tools/e2e.mjs # 1052 editor checks, screenshots in /tmp/gm-e2e-*.png +node tools/seed-e2e.mjs && node tools/e2e.mjs # 1055 editor checks, screenshots in /tmp/gm-e2e-*.png GM_STEPS=20 node tools/e2e.mjs # or 1-12, or 6,20 — a subset while working on one step ./e2e.sh start [GM_STEPS] # the same run detached, see below ./e2e.sh status # live progress, or the last run's result diff --git a/Physics.md b/Physics.md index c12ee5a..b7c9b77 100644 --- a/Physics.md +++ b/Physics.md @@ -68,8 +68,35 @@ wherever the object is put, nothing pulls on them, and dynamic bodies bounce off Whether two of them touch is decided by the same overlap test as before – touching edges do not count, only overlapping does – the engine only proposes which pairs to test. A placed tilemap is static ground made of its merged solid cells (see [Tilemaps](Tilemaps)). `Move` with -*Collide with solids* works as described in [Colliders](Colliders); a dynamic body blocks it like a -wall. +*Collide with solids* works as described in [Colliders](Colliders): the object stops flush at what +is in its way and slides along it. + +A dynamic body that stops such a move gets **pushed** along the move, as one body meeting another +without bounce: the speed it is pushed with is the move spread over the frame, and how much of it +the body takes depends on the two masses – a mover as heavy as a crate shoves it along, a crate +forty times heavier hardly shifts (measured: 133 of 161 units in a second, and 0.2). The mover +weighs what its rigidbody's **Mass** says, or else what its colliders weigh by their density; a +dynamic mover what the engine says. The mover stays flush in front of the crate and never gets +into it. + +## Moving bodies from a graph + +| Node | What it does | +|---|---| +| **Apply force** | Pushes with a force (units × kg per second²) for the length of the frame, so from `On update` it pushes steadily at any frame rate. A heavier body speeds up less. | +| **Apply impulse** | A kick at once (units × kg per second): the velocity changes by impulse ÷ mass. A jump, a hit, an explosion. | +| **Set velocity** | Sets the speed in units per second, whatever the body weighs. For one direction only, feed the other from `Get velocity`. | +| **Set angular velocity** | Sets the turning speed in degrees per second, counted like the rotation. A body with *Fixed rotation* does not turn, and does not start to later either. | +| **Get velocity** | Speed (x, y) and turning speed (degrees/s); 0 for an object without a dynamic body. | +| **Set gravity** | The scene's gravity for the rest of the run; the next scene starts with its own, and the project keeps its value. | + +The object is the one the rigidbody sits on (the rigidbody itself works too). They act on a +**dynamic** body only; on anything else they do nothing, and the console says so once per object: +`Physics: "Player" has no dynamic rigidbody, so Apply force, Apply impulse and Set velocity do +nothing on it.` They work from `On start` on. + +`Move` and `Set position` on a dynamic body put it somewhere else and it keeps its velocity; the +first time the console points to the nodes above, which move it with the physics. ## Play and the editor run @@ -79,11 +106,13 @@ the console say why. There is no falling back to the old detection: a game would differently. A behavior with *Run in editor* sees collisions but no simulation: dynamic bodies stay where they -are placed, because what the editor run moves is saved. +are placed, because what the editor run moves is saved. The nodes above do nothing there. ## What is not there yet -- Forces, impulses and velocities from a graph (`Apply force`, `Apply impulse`, `Set velocity`, …) - and `Move` over the engine's character controller – slopes, steps, pushing boxes (#147). +- `Move` walking up slopes and over steps (#150). Rapier's character controller was measured + for it in #147 and not taken: it caught on the seams between boxes lying side by side (10 of 100 + steps along a floor of 12 crates) and kept a gap of up to 1.7 units, where `Move` catches on + nothing and stops flush. - The simulation in the editor run (#148), and `On collision stay` with contact data (#149). - Capsules and polygons, collision layers, raycasts, joints, one-way platforms (#144).