-
Notifications
You must be signed in to change notification settings - Fork 0
Physics
🌐 English · Русский
Nova has a real rigid-body physics layer — a unified impulse-based constraint solver (sequential impulse / PGS over a contact manifold, warm-started, with friction, rolling friction, restitution, split-impulse positional correction and sleeping islands). It gives believable gravity with angle-of-incidence, inertia, rolling, bouncing, spin, solid stacks and emergent tumbling — not a scripted fall.
Two world-level master switches, both set when you create a world (and editable in the JSON — see World Format):
The master switch for solid objects.
- The player is a vertical capsule with real body height — pushed out of the floor and any collider, so you can't walk or fly through them, can't slip under a low overhang, and can't poke your head through a head-height bar (a point bubble used to). You still slide cleanly along walls.
- Each mesh can use a fast AABB box collider or an OBB oriented box that hugs a tilted shape (box-box contacts use a full 3D Separating-Axis test).
- Each object has its own Collide flag; when the world switch is off, every object's Collide is forced off and locked.
Turns on the simulation:
- The player becomes a walking character: gravity pulls the camera down, it stands on surfaces, Space jumps, and F1 toggles a free-fly mode for building.
- Any object can opt into Gravity — meshes, primitives, spheres, even a light or the platform fall and rest on whatever is beneath them.
- A falling ball collides with the real surface it lands on — the actual triangles of a pyramid / ramp / mesh, not its bounding box — so it rolls down a slope with real momentum (faster on steeper inclines), coasts to a stop on the flat, and bounces by its restitution. A falling mesh likewise rests on the real surface under it.
- Boxes rest, slide or topple on the real surface — never flung. A box dropped on a ramp settles flat on the slope and slides or tumbles down it; it is not ejected sideways (it collides with the mesh's real triangles, not its bounding box). Whether it slides or tumbles depends on the slope steepness and friction: a shallow slope just slides, a steep face / an off-balance box topples over its edge and rolls edge-to-edge down — all emergent from the solver.
- Everything collides with everything, and boxes STACK. Boxes and balls collide in every combination (box-box, ball-box, ball-ball), mass-weighted, so a tower of boxes stands solid (no jitter, no lean, no sink) and a ball fired into it scatters the tower — and the whole pile then settles and sleeps together (as one island).
- A custom mesh falls as its TRUE shape. A mesh object with Gravity + Collide simulates as its convex hull — it lands and rests on its own faces, tumbles, and stacks like its real silhouette rather than a bounding box, and it rests on the real triangles of a sloped mesh under it. (Box-like primitives — cube, ramp, pyramid… — keep their faster box collider, which already matches their shape. A concave mesh collides as its convex envelope.)
- Rolling friction — a rolling ball and a tumbling box slow down and come to rest instead of rolling forever, while still rolling down a slope (gravity beats the small rolling resistance). Tunable per object.
-
Restitution (Bounce) is set per-world (Create dialog) and per-object:
0= dead stop,1= perfectly elastic. On a contact the two surfaces' bounciness combine (geometric mean), so a springy ball on a dead floor differs from one on a "trampoline" wall. - Friction, mass and 3D rotation — objects shed speed to friction (Coulomb + rolling), a heavier object is shoved less (per-object Mass), and an off-centre hit makes a body tumble — full 3D spin with a proper inertia tensor and drift-free quaternion orientation.
- The simulation substeps internally, so it behaves the same whether the terminal renders at 60 FPS or only a few, with safety rails so nothing flies off.
Joints tie two objects together with a physical constraint, solved in the same solver as contacts — so a joint holds under gravity, collisions and stacking, never a separate physics path. Spawn one from the editor: cycle the spawn type to joint and place it (B); it appears as a line marker between its two anchor points, coloured by kind. Select it and cycle its Kind (N / M) — the Inspector shows the fields for that kind:
- Ball-socket (cyan) — pins the two anchor points together but leaves rotation free (a ball-and-socket). Good for pendulums, chains and ragdoll links.
-
Hinge (orange, with an axis stub) — a door / trapdoor: the bodies may rotate only about one shared axis (taken in body A's frame, so two differently-oriented bodies still turn about one clean axis). Optional angle limits — a stop range, shown in the Inspector as radians with degrees (e.g.
-1.57 (-90°)) — and a motor (a torque-limited drive toward a target speed) together make a servo. - Distance (green) — holds the two anchors a fixed rest length apart: a rigid rod by default, or a soft spring (set a frequency + damping) that oscillates toward the rest length.
Any object can be a joint side. An endpoint can be a dynamic body, the fixed world (a static anchor point, bodyId -1), or any other object — a light, a camera, a static prop, or the platform. A non-physical or static side acts as a fixed/kinematic anchor: drag it in the editor and whatever hangs from it follows. A joint whose two sides are both static is inactive (nothing to move), and the Inspector shows each joint's status — active, or inactive — <reason> (world gravity off, a body was deleted, or both sides static) — so a dead joint is never a mystery.
Collide (per joint, default off). A joint's two bodies do not collide with each other by default, so a centre-pinned pair can overlap without the pin and the contact fighting. Turn JCollide on for surface/edge-anchored joints — a real chain whose links should bump instead of folding through each other, for instance.
Joints snap together the moment you author them (or when a saved world loads), so they start assembled; a later runtime change (a gravity re-toggle, a retarget, a field edit) instead converges dynamically. They integrate with sleeping (a body being pulled by a joint won't freeze in mid-air, and a hung body still sleeps at rest and re-wakes when disturbed), accept live edits to their fields, and keep their anchor's world position when you retarget them onto another body.
Joints are saved in the world JSON and synced in multiplayer — a client that joins sees them, and live edits (spawn / change / delete) stream to peers.
By design (not bugs): a jointed colliding pair may overlap a little during a fast swing — the solver corrects it within a few steps. And if you drag static anchors so a distance chain can't reach (the anchors farther apart than the rods' rest lengths add up to), the rods visibly stretch until you restore a reachable layout — a rest length is a target, not a breaking strength.
Try all of this in the generated Test Lab world (maketestworld — see Getting Started): a pendulum, rod, spring, door, motor windmill, colliding chain, two-rod swing and trapdoor.
Each object carries its own tunables (edit them live in the Inspector, or in the world JSON):
- Collide — is this object solid (effective only when the world's Collision is on).
- Gravity — is this object pulled down (effective only when the world's Gravity is on; default off).
- Collider — a mesh's collider shape: AABB (fast world-axis box) or OBB (oriented box that rotates with the object, hugging tilted/elongated shapes).
-
Mass — impulse-solver mass (default
1); a heavier object is shoved less in a collision. -
Bounce (restitution) — 0–1;
-1inherits the world's default. On a contact the two surfaces' values combine. -
Friction — Coulomb friction coefficient (μ, default
0.5). -
RollFric — rolling-friction resistance (default
0.05) so a rolling ball / tumbling box comes to rest.
Object physics is simulated by the authority/solo and each moved body's full state — position, orientation and velocity — is streamed to clients, which dead-reckon and smoothly ease toward it, so everyone sees objects fall, roll and tumble in sync (not frozen between updates); the player's own gravity runs locally for every peer, so nothing desyncs. See Multiplayer.