From 5f531117508f0912823c16b1566fece2969970fa Mon Sep 17 00:00:00 2001 From: meleki Date: Sat, 3 Oct 2026 10:09:58 +0200 Subject: [PATCH] Describe the slider, joint motors and Joint position (#154) Physics gets the slider with its rail and distance limits, the motor on hinge and slider, the weak hinge motor on a long arm and the node Joint position. - Behaviors lists Joint position, Concepts names the slider. - Development carries the e2e total of 1701. --- Behaviors.md | 2 +- Concepts.md | 2 +- Development.md | 2 +- Physics.md | 41 +++++++++++++++++++++++++++++++++++------ 4 files changed, 38 insertions(+), 9 deletions(-) diff --git a/Behaviors.md b/Behaviors.md index d7ab238..7ad3889 100644 --- a/Behaviors.md +++ b/Behaviors.md @@ -229,7 +229,7 @@ playing use the same two words. float noise, a relative 1e-9, so `time - late == 6` holds without a `Round`; `Equals` does the same for two numbers, and for a number against a typed text that is a number, like `130`), `And/Or/Not`, `Key is down`, `Key was pressed` and `Key was released` (true for the one frame in which the key went down or came up, the frame `On key down` and `On key up` fire in; a key that stays held is not pressed again), - `Mouse position`, `Mouse button`, `Is hovered`, `Is touching`, `Layer of` (the collision layer of an object by name and number, see [Colliders](Colliders)), `Animation playing` (whether an animation runs on an object, its progress and time left) and `Animation length` (see [Animations](Animations)), `Get velocity`, `Current state` (name and id), `Is in state` (one boolean per state) · + `Mouse position`, `Mouse button`, `Is hovered`, `Is touching`, `Layer of` (the collision layer of an object by name and number, see [Colliders](Colliders)), `Animation playing` (whether an animation runs on an object, its progress and time left) and `Animation length` (see [Animations](Animations)), `Get velocity`, `Joint position` (how far a hinge or a slider stands from how it was placed, see [Physics](Physics#joints)), `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; 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 input + x/y/z, optional parent → outputs the new object; the asset is picked on the node or arrives over an edge, from an `Asset` node, a variable or a list item. A pick saved while it was still a setting reads as that input's value, so older projects spawn as before; the same holds for the asset of `Is asset`), `Destroy` (removes the object with everything below it; with **keep children** on, only the object goes: its children move up to its parent (top level when it had none) and keep their place, rotation and size in the world, and the output `children` is the list of them, nearest level only. Its behaviors, its rigidbody and its joints go with it either way. In the editor run, children that are content of a placed container move up for the run only: their place belongs to the template, so the scene does not learn it), `Add joint` (ties an object to another while the game runs: the picked Joint asset is placed on *object* at x/y and connected to *connected*, the scene when nothing arrives; outputs the new joint, which `Destroy` lets go again, see [Physics](Physics#joints)), `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 diff --git a/Concepts.md b/Concepts.md index 93dfc59..db3ac12 100644 --- a/Concepts.md +++ b/Concepts.md @@ -20,7 +20,7 @@ | **Collider** | A shape (box or circle) for collision detection, placed as a **child** of the object it belongs to (its *body*). Drawn as a green outline in edit mode only; its area is tinted only while the collider itself is selected. | | **Collision layer** | One of up to 16 named layers of the project (**menu → Project settings…**). A collider or tilemap names the layer it is on and the layers it **collides with**; two of them meet only when each one names the layer of the other. A new project has one layer, *Default*, which every collider is on and meets — so nothing collides differently until a layer is named. See [Colliders](Colliders#collision-layers). | | **Rigidbody** | Placed as a **child** of an object, it hands that object to the physics engine while playing: it falls with the scene's gravity, bounces and rests on colliders, pushed by what it meets. At most one per object. See [Physics](Physics). | -| **Joint** | Placed as a **child** of an object with a rigidbody, it ties that object to another one or to the scene while playing: a hinge, a spring, a rope or a fixed joint. Where it sits is the point it holds. See [Physics](Physics#joints). | +| **Joint** | Placed as a **child** of an object with a rigidbody, it ties that object to another one or to the scene while playing: a hinge, a spring, a rope, a fixed joint or a slider. Where it sits is the point it holds. See [Physics](Physics#joints). | | **Override** | A per-object value that replaces the asset's value for one property. Only changed keys are stored; the inspector paints their label gold and offers a reset (↺) and an **apply** (↑) that writes the value into the asset instead. Both appear only where the value really differs from its base — an override carrying the asset's own value is none. | | **Default** | Every property has a schema default. Asset values that differ from it are marked gold in the inspector and can be reset (↺) the same way an override can. The asset's name in the **Assets** panel carries the same gold while any of them does (`assetDeviates`, `src/core/properties/deviation.ts` — the inspector marks the rows of that same schema, so the list cannot claim a deviation the inspector then fails to show). A behavior's variables are not in it: their default is declared on the variable itself, so the asset value cannot differ from it. | | **Instance** | An object resolved from a placed container's template. Its **values** can be edited — position, name, property overrides — and only what actually deviates is stored, as a sparse `instanceEdits` entry on the container object keyed by the template path. Everything else keeps following the template. Its **structure** cannot: it can not be removed, re-parented or given a collider, and nothing of the scene can be parented into it. ↺ ▾ resets to the nearest base, which inside a container is the **template as the containers in between leave it**; ↑ ▾ offers **Apply to container** (write the value into the template, where every other instance still at the old value picks it up) and/or **Apply to asset** (which drops the template's override as well). With one base left the menu collapses to a plain ↺ / ↑, with none the buttons disappear. Gold marks the same thing the buttons offer. | diff --git a/Development.md b/Development.md index 65c7dc4..d66ff48 100644 --- a/Development.md +++ b/Development.md @@ -18,7 +18,7 @@ php tests/php/accounts.test.php # the API's accoun # has and the web server's container has not (SKIP there) php tests/php/snapshots.test.php # the snapshot store (#231), the same way php tests/php/releases.test.php # the release store (#266), the same way -node tools/seed-e2e.mjs && node tools/e2e.mjs # 1699 editor checks, screenshots in /tmp/gm-e2e-*.png +node tools/seed-e2e.mjs && node tools/e2e.mjs # 1701 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 6b1698c..4f4174f 100644 --- a/Physics.md +++ b/Physics.md @@ -146,6 +146,7 @@ body's colliders, since the group sits on the collider itself. | **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. | +| **Joint position** | How far a hinge has turned or a slider has glided from how it was placed, and how fast; see [Joints](#joints). | | **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 @@ -174,15 +175,27 @@ Its rows: | Row | Default | What it does | |---|---|---| -| Type | Hinge | **Hinge**: the object turns round the point where the joint sits (a door, a pendulum, a wheel). **Spring**: pulled towards a length, swinging about it. **Rope**: free up to a length, held beyond it. **Fixed**: kept exactly as the two were placed, moving as one. | +| Type | Hinge | **Hinge**: the object turns round the point where the joint sits (a door, a pendulum, a wheel). **Spring**: pulled towards a length, swinging about it. **Rope**: free up to a length, held beyond it. **Fixed**: kept exactly as the two were placed, moving as one. **Slider**: the object glides along one line and does not turn against the other end (a lift, a piston, a drawer). | | Connected object | None | The other end, picked from the objects of the scene or with the eyedropper. None is a point of the scene that never moves. | | Connected anchor X / Y | 0, 0 | Spring and rope only: where the other end is, in the connected object's own units (0, 0 is its origin, and it turns and scales with it) — with no connected object, in the scene's. A hinge and a fixed joint have one point only, the one the joint sits on. | | Length (0 = as placed) | 0 | Spring and rope only: the length the spring rests at or the rope allows, in world units. 0 takes the distance between the two ends as they were placed. | | Stiffness | 50 | Spring only: the force per 100 units of stretch (N/m). A body of 1 kg on 40 swings once in 0.99 s. | | Damping | 1 | Spring only: how fast the swinging dies down; 0 swings on. | | Limit angle | off | Hinge only. On: **Min angle** and **Max angle** say how far the object turns either way, in degrees, counted from how it was placed. | +| Limit distance | off | Slider only. On: **Min distance** (default -100) and **Max distance** (100) say how far the object glides either way, in world units, counted from where it was placed. | +| Motor | Off | Hinge and slider only. **Speed**: turns or glides on at *Motor speed*. **Target**: goes to *Motor target* at no more than *Motor speed*, slows down for the last stretch and holds there. | +| Motor speed | 90 | Degrees a second for a hinge, world units a second for a slider. Positive turns the way a positive rotation does (clockwise on screen), or glides along the rail's direction. | +| Motor target | 0 | Target only: where to go, in degrees or world units counted from how the joint was placed, like the limits. | +| Max force (0 = no limit) | 0 | Speed and Target: the most the motor pushes with, in units · kg per second² as `Apply force` counts; for a hinge, pushing 100 units from its pin. Something heavier or in the way stops it. 0 is as strong as it takes. | | Collide with connected | off | Off: the two objects of the joint pass through each other. On: they collide like any two. | +A joint has **Scale, Rotation and Mirror X/Y** like a sprite, and the knob of the scene view turns +it. Only a slider uses them: **its rail is the joint's own x axis**, so rotation 0 is a level rail +and 90 an upright one whose positive direction points down. The joint turns with the object it sits +on, as every child does. A slider held to the scene, or to an object without a rigidbody, keeps its +object turned as it was placed. Between two rigidbodies the engine keeps the two at one angle, so +it turns the held object to match the other once, and the console says so. + **The object needs a rigidbody**, its own or one of an object above it. Without one the joint holds nothing and the console says so once: `Physics: "Lamp joint" sits on an object without a rigidbody, so it holds nothing.` @@ -202,19 +215,35 @@ In the **scene view** a line leads from the joint to what it holds on to: to the of a spring or a rope, to the connected object of a hinge or a fixed joint. The held point is ringed, since the icon stands aside when it shares the spot with a collider's. A selected spring or rope has a round **handle** on its far end: drag it to move the connected anchor (one undo step). +A slider also gets its **rail**, dashed through where the joint sits: between its limits, with a +tick at each, or 60 units either way without any. While the game is played the rail is drawn where +the joint is, so it travels along with the object. A joint that is switched off lets go, and holds again when it is switched back on. `Destroy` on a joint lets go for good. **`Add joint`** ties two objects while the game runs: it places the picked Joint asset on *object* at (x, y) of that object, connects it to *connected* (nothing there: the scene) and outputs the new joint. The asset is the template for the type and every value; -`Properties` with Type *Joint* changes them on a placed joint. +`Properties` with Type *Joint* changes them on a placed joint. Setting *Motor speed*, *Motor +target* or *Max force* that way changes the running motor, so a door opens by writing 90 into its +target. **`Joint position`** reads a hinge or a slider back: *value* is how far it has turned +(degrees, counting on past a full round) or glided (world units) from how it was placed, and +*speed* how fast that changes. It is 0 for the other types, for a joint that holds nothing, and in +the editor run. + +A motor pushes on the turn only, so a hinge drives a door well but is weak on a small object at the +end of a long arm. Measured: a door of 100 x 20 hinged at its end reaches -90 degrees and holds it +against gravity. A box of 20 on an arm of 100 at the same 180 degrees a second swings past a target +of 90 to 105 without gravity, and with gravity it does not lift itself. The engine works the turn +out over too few rounds for that. A wheel turning round its own middle has no such trouble. Joints hold while the game is **played**. The editor run simulates nothing (#148), so there they do nothing, and the inspector of a placed joint says so. Measured in the unit tests: a pendulum of 100 units crosses the middle after 0.50 s (2.007 s the whole period), a spring of 40 N/m on 1 kg turns round after 0.50 s, a rope of 150 lets a body fall -to 150 and no further, and a hinge limited to 30 degrees stops between 27 and 33. +to 150 and no further, and a hinge limited to 30 degrees stops between 27 and 33. A wheel with a +motor of 360 degrees a second has turned 880 to 905 degrees after 2.5 s, a lift driven to -200 +holds there, and a slider limited to 50 stops between 48 and 52. ## Play and the editor run @@ -234,6 +263,6 @@ are placed, because what the editor run moves is saved. The nodes above do nothi 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). -- A slider joint, and motors on a hinge (#154, second step). Raycasts (#153) with their layer - filter (#158), `Raycast all` with every hit on the line (#157), one-way platforms (#155) and the - collision layers themselves (#152) are there, see [Colliders](Colliders#raycast). +- A force that breaks a joint. Raycasts (#153) with their layer filter (#158), `Raycast all` with + every hit on the line (#157), one-way platforms (#155) and the collision layers themselves (#152) + are there, see [Colliders](Colliders#raycast).