1 parent
93a5641da7
commit
e2d7283feb
7 files changed
+105
-16
No files matched your search
+8
-5
@@ -67,7 +67,7 @@
|
||||
```
|
||||
web/index.html Vite source page (dev) index.html built page (generated, served)
|
||||
src/core/model/ Project (object holders), Asset + AssetTypes, SceneObject, Properties (value types), hierarchy, walk (generic parent/child walks), instance (container content resolved from its template), usages (where an asset is used), container (the cycle guard), placement (what may be dropped where), texture (frames, the slice grid, a frame's collision outlines), polygon (an outline checked, cut into convex pieces and clipped to a rectangle), tile (the three kinds and their block shapes, reading a tile's pictures, the animation step), autotile (which quarter tile a neighbourhood asks for, the 47/16 tiles a block produces, 1 for a plain tile), tilemap (the cells of a layer, which neighbours a cell is joined to, the cells a stroke or a rectangle covers), tilemapCollision (the solid cells of a map, drawn in or out by their tile's collision scale and merged into rectangles, the outlined cells as polygons – an autotile's outlines cut into its quarters and pieced together per cell like its picture –, and where one sits in the world), animation (which frame at time t, a row's value there, what a row may aim at, the players on an object; KeyRef and its lookups) + animationPreview (the root at a frame, written into copies), graphLookup (what a graph sees of the project: behaviors to run, state machines to read), Scope (where a state machine's current state lives: per instance or one for the run — variables are shared by living on a Variables asset instead)
|
||||
src/core/properties/ PropertySchema, kinds (one row per value kind), listItems (an item's kind, read off its value), resolve (the only override merge), deviation (what is gold), collider/camera/bounds geometry, convex (capsules and polygons: rounded convex pieces against each other), rigidbody (what the engine reads off a rigidbody, a collider's material and the scene), aspect, enabled, editorRun, assetRef/textureRef (how a reference reads), declaredRows (the row a Property node declares in a shader or particle graph, and the params that node has in both)
|
||||
src/core/properties/ PropertySchema, kinds (one row per value kind), listItems (an item's kind, read off its value), resolve (the only override merge), deviation (what is gold), collider/camera/bounds geometry (with the side a one-way collider blocks from), convex (capsules and polygons: rounded convex pieces against each other), ray (where a ray meets a shape, in double precision), rigidbody (what the engine reads off a rigidbody, a collider's material and the scene), aspect, enabled, editorRun, assetRef/textureRef (how a reference reads), declaredRows (the row a Property node declares in a shader or particle graph, and the params that node has in both)
|
||||
src/core/commands/ undoable commands, CommandStack, SetBagValueCommand base, RenameCommand base (undone by renaming back: variable, struct field, port), one import command shape for texture/font/sound, require.ts (validated lookups, and what an instance may not change), naming.ts
|
||||
src/core/graph/ graph model, NodeRegistry/NodeContext/values, interpreter, dropTarget, nested (behaviors as nodes),
|
||||
emitGraph (the walk both graph compilers share: each node once, in dependency order; cycles and property names reported),
|
||||
@@ -81,13 +81,16 @@ src/core/store/ serialize (untrusted JSON → Project), tilemapCells (the
|
||||
file collections) — TextureStore, FontStore and SoundStore are its type parameters, nothing else
|
||||
src/core/time.ts the global clock: one scaled step a frame, shared by the world, the particles, the shaders and the animation preview
|
||||
src/runtime/ World, Entity, System interface, Input tracker, CollisionSystem (the editor run's detection),
|
||||
solids (what `Move` collides with and how it slides, which body a collider belongs to, the
|
||||
CollisionSource both collision systems give the behaviors), AnimationSystem, BehaviorSystem,
|
||||
solids (what `Move` collides with and how it slides — one-way solids let it through unless
|
||||
it lands on them —, the first solid a ray meets, which body a collider belongs to, the
|
||||
CollisionSource both collision systems give the behaviors, the contact of a staying pair), AnimationSystem, BehaviorSystem,
|
||||
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 push a
|
||||
`Move` gives a dynamic body, and what the physics nodes reach through `NodeContext.physics`)
|
||||
the fixed step, the poses handed back, the contact pairs sorted into events and their
|
||||
normal, impulse and point; whether a dynamic body lands on or passes a one-way collider,
|
||||
decided before each step for the contact hook; 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;
|
||||
|
||||
+4
-2
@@ -97,7 +97,7 @@ shown on the node and editable in the inspector.
|
||||
deliberately not used: its delay and rate are an operating-system setting, not a game's. Both
|
||||
rows stay off the node body while the delay is 0, so a plain key node keeps its single line.
|
||||
- **Node set** (`src/core/graph/nodes/`, one file per node): events `On start`, `On enable`, `On update`, `On disable`, `On destroy` (see *Lifecycle*), `On key down/up/held`,
|
||||
`On timer`, `On collision enter/stay/exit`, `On click` (the mouse pressed on the owner — anywhere when it has none), `On animation finished`, `On state entered`, `On state exited` (one output per state, see *State machines*), `Start` · flow `Branch`, `Sequence`, `Repeat`, `Run behavior`, `End`, and the four that move or follow a state machine, `State branch`, `Set state`, `Set state by name`, `State transition` (see *State machines*) · data `Self`, `Owner`, `Parent`, `Object by name`, `Name of`,
|
||||
`On timer`, `On collision enter/stay/exit` (stay every frame, or with an interval; while playing enter and stay tell normal, impulse and point of the contact), `On click` (the mouse pressed on the owner — anywhere when it has none), `On animation finished`, `On state entered`, `On state exited` (one output per state, see *State machines*), `Start` · flow `Branch`, `Sequence`, `Repeat`, `Run behavior`, `End`, and the four that move or follow a state machine, `State branch`, `Set state`, `Set state by name`, `State transition` (see *State machines*) · data `Self`, `Owner`, `Parent`, `Object by name`, `Name of`,
|
||||
`Is asset` (is this object an instance of asset X — how you tell what you collided with),
|
||||
`Variables`, `Properties` (every property row of the target's type, the transform rows `scale`, `rotation`, `mirrorX`, `mirrorY` included — how a graph turns a sprite), `Get position` (local and world), `Is enabled`, literals `Number`/`Text`/`Boolean`/`Texture`/`State machine`/`Sound`/`Scene`, `Add/Subtract/Multiply/Divide`,
|
||||
`Min/Max/Abs/Sqrt/Clamp`, `Floor/Ceil/Round`, `Modulo`, `Random`, `Time`, `To text`, `To number`, `Join text`,
|
||||
@@ -115,7 +115,9 @@ shown on the node and editable in the inspector.
|
||||
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`,
|
||||
`Apply force`, `Apply impulse`, `Set velocity`, `Set angular velocity`, `Set gravity` (see [Physics](Physics)). A node type is one file exporting a `NodeDef`
|
||||
`Apply force`, `Apply impulse`, `Set velocity`, `Set angular velocity`, `Set gravity` (see [Physics](Physics)),
|
||||
`Raycast` (what a ray meets first: object, collider, point, normal, distance — see [Colliders](Colliders#raycast)),
|
||||
`Ignore one-way` (fall through one-way colliders and tiles for some seconds — see [Colliders](Colliders#one-way)). 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.
|
||||
|
||||
+47
-2
@@ -41,8 +41,11 @@ engine's work (`PhysicsSystem`, see [Physics](Physics)), which also simulates ri
|
||||
editor run tests all pairs itself (`CollisionSystem`) and pushes nothing. Without a rigidbody
|
||||
both come to the same answer. A behavior receives `On collision enter/stay/exit` for the colliders of its **owner** (its
|
||||
parent; itself when top-level): the outputs are `other` (the other body), `other collider`
|
||||
and `collider` (the own one); while playing, `On collision enter` also tells the direction and
|
||||
the strength of the contact (see [Physics](Physics)). `Is touching(a, b)` answers the same question on demand — an
|
||||
and `collider` (the own one); while playing, `On collision enter` and `On collision stay` also
|
||||
tell the direction, the strength and the point of the contact (see [Physics](Physics)).
|
||||
`On collision stay` fires every frame by default; with an **Interval (s)** above 0 it fires once each
|
||||
time that long has passed since the pair met, on a clock of its own per pair that starts over when
|
||||
they meet again (#149). `Is touching(a, b)` answers the same question on demand — an
|
||||
object stands for its collider children, a collider for itself. The geometry lives in
|
||||
`src/core/properties/collider.ts` so editor and runtime agree on it.
|
||||
|
||||
@@ -73,3 +76,45 @@ always leave. A move longer than the thinnest collider involved is split into su
|
||||
fast object cannot jump across a thin wall and end up on the other side. Colliders of the
|
||||
moving object and of anything parented under it travel with it and cannot
|
||||
block it. `Move` without the setting and `Set position` ignore colliders entirely.
|
||||
|
||||
## One-way
|
||||
|
||||
A collider with **One-way** on (#155) blocks only what comes from one side: **Blocks from** says
|
||||
which — *Above* by default, or *Below*, *Left*, *Right* — in the collider's own axes, so it turns
|
||||
and mirrors with its object. In the scene view that side is drawn thick. It is the platform a
|
||||
figure jumps through from below and lands on from above:
|
||||
|
||||
- It blocks a contact whose normal lies within **45 degrees** of that side; anything else passes -
|
||||
from below, and from the side as well, as in Unity and Godot.
|
||||
- What is already inside it passes on through for as long as it is inside, so a jump that did not
|
||||
clear the top falls back out below.
|
||||
- `Move` with *Collide with solids* reports `hit` only where it was stopped, and a fast fall is
|
||||
still caught: the move is swept in steps no thicker than the platform.
|
||||
- **Ignore one-way** (object, seconds, 0.25 by default) lets the object fall through all one-way
|
||||
colliders and tiles for that long — drop down with the down key. When the time is up it still
|
||||
passes what it is inside and lands on the next one it comes down on.
|
||||
- A dynamic body lands, passes and drops the same way (see [Physics](Physics#one-way-colliders-in-the-engine)).
|
||||
- A ray meets it only from the side it blocks from.
|
||||
|
||||
Tiles can be one-way as well (see [Tilemaps](Tilemaps#one-way-tiles)).
|
||||
|
||||
## Raycast
|
||||
|
||||
The **Raycast** node (#153) casts a ray from `from x/y` along `dir x/y` (any length, it is made a
|
||||
unit vector) for up to `length` world units, and says what it meets first: `hit`, the `object`
|
||||
(the body, like `other` of a collision), its `collider` (or the tilemap), the `point x/y`, the
|
||||
`normal x/y` of the face (pointing out of it) and the `distance`. For line of sight, a ground
|
||||
check, a laser, a hitscan shot.
|
||||
|
||||
- Met are solid colliders of every shape and the cells of a tilemap, outlined ones too. Triggers
|
||||
only with **Hit triggers** on. A one-way collider or tile only from the side it blocks from.
|
||||
- With **Ignore owner** on (the default) the behavior's owner and everything parented below it are
|
||||
not met. And a shape the ray *starts inside* is never met — so a ray cast from the middle of a
|
||||
figure finds what is around it; one starting on an outline and running in meets it at 0.
|
||||
- Grazing a corner counts as meeting it.
|
||||
- It is worked out in our own double-precision geometry (`core/properties/ray.ts`) over the same
|
||||
shapes `Move` collides with, so the editor run and Play answer alike, and always about where
|
||||
everything is right now — not about the last physics step.
|
||||
- In the editor run every ray cast shows in the scene view for a quarter of a second: green to
|
||||
where it met something, grey to its end when it met nothing.
|
||||
- Only the first hit for now; a list of all hits and a filter by collision layer (#152) come later.
|
||||
+1
-1
@@ -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 # 1068 editor checks, screenshots in /tmp/gm-e2e-*.png
|
||||
node tools/seed-e2e.mjs && node tools/e2e.mjs # 1073 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
|
||||
|
||||
+34
-6
@@ -67,10 +67,15 @@ engine.
|
||||
## Collision events
|
||||
|
||||
`On collision enter/stay/exit` and `Is touching` come from the engine's contact pairs.
|
||||
`On collision enter` has three more outputs: **normal x** and **normal y**, a unit vector pointing
|
||||
from the other collider towards the own one – (0, −1) for something landing on the ground – and
|
||||
**impulse**, how hard they met (kg · units per second). A trigger reports a normal of (0, 0) and no
|
||||
impulse. `Is touching` answers for the last step, not for a move made in the same frame.
|
||||
`On collision enter` and `On collision stay` have five more outputs: **normal x** and **normal y**,
|
||||
a unit vector pointing from the other collider towards the own one — (0, -1) for something landing
|
||||
on the ground — **impulse**, how hard they meet (kg · units per second), and **point x/y**, the middle
|
||||
of the contact points in the world. Enter tells how the pair met, stay how it touches at the moment
|
||||
it fires (#149) — which, with its **Interval (s)**, need not be every frame. The impulse is the
|
||||
engine's own number: it grows with the weight, but a crate at rest reports about 1.25 times
|
||||
mass · gravity · 1/60, not exactly that (measured). A trigger reports all of them as 0, and so does
|
||||
the editor run, which simulates nothing. `Is touching` answers for the last step, not for a move
|
||||
made in the same frame.
|
||||
|
||||
## Without a rigidbody
|
||||
|
||||
@@ -92,6 +97,28 @@ weighs what its rigidbody's **Mass** says, or else what its colliders weigh by t
|
||||
dynamic mover what the engine says. The mover stays flush in front of the crate and never gets
|
||||
into it.
|
||||
|
||||
## One-way colliders in the engine
|
||||
|
||||
A dynamic body lands on a one-way collider or tile (see [Colliders](Colliders#one-way)) and passes
|
||||
it from below and from the side, like `Move` does. The engine is told pair by pair whether to push
|
||||
the two apart (Rapier's `filterContactPair` hook). Its JavaScript version cannot touch the engine
|
||||
from inside the hook — even reading a body's position there throws, the throw is swallowed and the
|
||||
pair collides with nothing (found in the spike for #155) — so the answer for each dynamic body near a
|
||||
one-way collider is worked out **before every step**:
|
||||
|
||||
- It **lands** when it is on the blocking side or sunk in no deeper than 2 units (a landing at 60
|
||||
steps per second sinks in 1.9 before the engine pushes it out), the contact faces that side
|
||||
within 45 degrees, and it is not moving away from it.
|
||||
- Once landed it is **held** as long as the two overlap: a body falling 2000 units per second is
|
||||
13 units into an 8 units thick platform one step later, and is pushed back up instead of passing.
|
||||
- Otherwise it passes, and so does a body told to by `Ignore one-way`. A resting body is woken for
|
||||
that — a sleeping one would not fall by itself.
|
||||
|
||||
Measured in the spike at 60 steps per second: a landing sinks in 1.9 units and rests 0.009 deep, a
|
||||
body shot up from below comes out on top and lands, one sliding sideways goes through, and one
|
||||
falling at 2000 units per second lands with and without CCD. Collision events come on passing
|
||||
through as well: overlapping is colliding.
|
||||
|
||||
## Moving bodies from a graph
|
||||
|
||||
| Node | What it does |
|
||||
@@ -128,5 +155,6 @@ are placed, because what the editor run moves is saved. The nodes above do nothi
|
||||
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).
|
||||
- Collision layers, raycasts, joints, one-way platforms (#144).
|
||||
- The simulation in the editor run (#148).
|
||||
- Collision layers (#152) and joints (#154). Raycasts (#153) and one-way platforms (#155) are
|
||||
there, see [Colliders](Colliders#raycast).
|
||||
+9
@@ -201,6 +201,15 @@ In the running game the placed map is a **body of its own** in the collision sys
|
||||
|
||||
There are no trigger tiles yet: a tile blocks or it does not.
|
||||
|
||||
### One-way tiles
|
||||
|
||||
A solid tile with **One-way (from above)** on (#155) blocks only what comes down on it from above,
|
||||
like a one-way collider blocking from *Above* (see [Colliders](Colliders#one-way)): a figure jumps
|
||||
through from below and from the side, lands on top, and falls through with `Ignore one-way`. Above
|
||||
means the map's top, turned with it. One-way cells are merged into rectangles the same way, but only
|
||||
with each other — a platform next to a wall is two shapes, so the wall never blocks from below only.
|
||||
In the scene view they are outlined thinner, their top drawn thick.
|
||||
|
||||
## In the project file
|
||||
|
||||
While you edit, a layer holds its cells as a map from `"x,y"` to a tile. The project file stores
|
||||
|
||||
+2
@@ -60,6 +60,8 @@ asks for; a picture that does not fit is kept and named, never silently dropped.
|
||||
Ping-pong turns on the last picture, so 0 1 2 1 rather than 0 1 2 2 1.
|
||||
- **Solid** — off by default. A solid tile blocks movement wherever it is painted, on every map and
|
||||
every layer (#132); see [Tilemaps](Tilemaps#collision).
|
||||
- **One-way (from above)** — off by default. A solid tile with it on blocks only from above: a
|
||||
platform one jumps through from below (#155); see [Tilemaps](Tilemaps#one-way-tiles).
|
||||
- **Collision scale** — 1 by default. How big a solid cell blocks against its square, drawn in or
|
||||
out only on the sides without a blocking neighbour (#140); see
|
||||
[Tilemaps](Tilemaps#collision-scale).
|
||||
|
||||
Reference in new issue
Block a user