From e1e8bbdc987e11495cddb3b573744d2cac8e82e0 Mon Sep 17 00:00:00 2001 From: MelekTaus Date: Fri, 18 Sep 2026 21:52:11 +0200 Subject: [PATCH] Physics: Move slides along the contact normal with friction, outputs hit/normal in Behaviors, sliding and friction in Colliders and Physics, check total 1057 (#150) Co-Authored-By: Claude Opus 5 --- Behaviors.md | 2 +- Colliders.md | 23 +++++++++++++++++------ Development.md | 2 +- Physics.md | 15 +++++++++------ 4 files changed, 28 insertions(+), 14 deletions(-) diff --git a/Behaviors.md b/Behaviors.md index 96b3196..001b1c3 100644 --- a/Behaviors.md +++ b/Behaviors.md @@ -108,7 +108,7 @@ shown on the node and editable in the inspector. `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`, `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` + 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; with *Collide with solids* it slides along what it hits, less friction, and outputs `blocked x/y`, `hit` and the contact `normal x/y`, see [Colliders](Colliders)), `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 diff --git a/Colliders.md b/Colliders.md index 115c67a..7b78653 100644 --- a/Colliders.md +++ b/Colliders.md @@ -24,12 +24,23 @@ object stands for its collider children, a collider for itself. The geometry liv `src/core/properties/collider.ts` so editor and runtime agree on it. **Blocking** is opt-in per movement: the `Move` node has a **Collide with solids** setting -(default off). When enabled it moves an object one axis at a time — its own axes, which a turned -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). In Play a dynamic body that stops the move is pushed -along (see [Physics](Physics)). A collider with +(default off). When enabled the object's *solid* colliders move along (dx, dy) — in its own axes, +which a turned parent turns as well — until one runs into a solid collider; there the object stops +flush (`exitDistance` in `collider.ts`: the travel at which the shapes stop overlapping). What is left +of the move then goes on **along the surface**: the part into it is dropped, and the part along it +is shortened by **friction × the part into it** (Coulomb; the pair's friction is the average of both +colliders, a tile counts 0.5). The contact's normal decides which way "along" is, so a circle that +runs into an edge rolls up over it, an object pressed onto a slope slides down it unless the pair +is rough enough to hold it (friction ≥ tan of the slope), and one pressed against a wall slides +along it the slower the harder it presses. Up to four contacts are followed per move (a corner +between floor and wall is two). There is no bounce: `Move` sets a distance, not a speed – a +dynamic body bounces (see [Physics](Physics)). The outputs: `blocked x` / `blocked y` tell which of +the object's own axes something stood against (a floor blocks y, a wall x, a slope each axis the +move went along); +`hit` whether anything was met; `normal x` / `normal y` which way the first surface faces, in the +world, pointing away from it — enough to build a bounce or a wall jump in the graph. All are `false` +/ 0 with the setting off. An object without a solid collider of its own moves freely. In Play a +dynamic body that stops the move is pushed into the contact (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 cee4d47..7ec40f3 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 # 1055 editor checks, screenshots in /tmp/gm-e2e-*.png +node tools/seed-e2e.mjs && node tools/e2e.mjs # 1057 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 b7c9b77..41fbfc6 100644 --- a/Physics.md +++ b/Physics.md @@ -69,9 +69,11 @@ Whether two of them touch is decided by the same overlap test as before – touc 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): the object stops flush at what -is in its way and slides along it. +is in its way and goes on along its surface, less what friction takes – the same friction a +dynamic body feels, combined the same way (the average), and the same in Play and the editor run. -A dynamic body that stops such a move gets **pushed** along the move, as one body meeting another +A dynamic body that stops such a move gets **pushed** into the contact – along its normal, so a +crate hit at its corner is shoved away from the corner, not 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 @@ -110,9 +112,10 @@ are placed, because what the editor run moves is saved. The nodes above do nothi ## What is not there yet -- `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. +- `Move` stepping up onto a ledge, or a limit on how steep a slope it walks up – that is for the + graph, with `hit` and `normal` (#150). `Move` does not use Rapier's character controller: in #147 + it caught on the seams between boxes lying side by side (10 of 100 steps along a floor of 12 + crates), kept a gap of up to 1.7 units and knows no friction; `Move` works out its contacts itself, + in double precision, and slides over the same seams without catching (#150). - 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).