{"id":46308,"plugin_id":"plugin_asdk_app_6acd235a70a48191812b3ba9c471b4ac","kind":"skill","collection_source":"plugin_package","comparison_source":null,"observed_at":"2026-10-24T13:29:11.536Z","digest":"40053ffc02f87ad0f3ae65962bd11de11df4d3edccb913bc6700c0a70a391dbb","against":null,"payload":{"description":"The physics simulation itself (no character needed): page Simulation switch + gravity, physics() in depth (positioned colliders vs dynamic bodies, friction/bounce/density/gravityScale/damping, convex vs trimesh colliders, fused vs separate group bodies, axis locks), kinematic movers (animated/dragged colliders that push dynamic bodies), spawning dynamic objects with the Create action, and falling/stacking/bouncing/floating recipes.","included_files":[],"name":"spline-3d-physics","skill_md_contents":"---\nname: spline-3d-physics\ndescription: \"The physics simulation itself (no character needed): page Simulation switch + gravity, physics() in depth (positioned colliders vs dynamic bodies, friction/bounce/density/gravityScale/damping, convex vs trimesh colliders, fused vs separate group bodies, axis locks), kinematic movers (animated/dragged colliders that push dynamic bodies), spawning dynamic objects with the Create action, and falling/stacking/bouncing/floating recipes.\"\n---\n\n# Physics (rigid-body simulation)\n\nSpline runs a rigid-body physics engine (Rapier) while the scene PLAYS —\nin play mode and in exports, never while editing. Objects fall, stack,\nslide, bounce and knock each other over; nothing simulated is written back\nto the scene (`stop()` returns everything to its authored layout).\n\nThis skill is the simulation reference. For a PLAYABLE CHARACTER standing\non and pushing this world with the built-in Game Controls feature\n(`GameControl`, colliders, key mapping, Collision/Trigger gameplay —\nonly when the user asked for it), load the `game-controls` skill; the\nDEFAULT game recipe (JS game loop + HTML UI, no engine physics) is the\n`games` skill; for the generic event/action machinery, load\n`interactivity`.\n\n## The simulation model\n\n- **Page switch** — physics is off per page until enabled. The first\n  `physics(...)` call flips it on automatically; there is no DSL call to\n  turn it back off (Simulation panel in the page settings).\n- **Gravity** — a page-wide setting (default `-10` m/s², UI slider -20..20;\n  POSITIVE values invert gravity so dynamic bodies fall UP). It is NOT\n  settable from the DSL — if a scene needs different global gravity, ask the\n  user to change it in the Simulation panel. Your per-object lever is\n  `gravityScale` (below).\n- **Scale** — 200 world units = 1 meter. A 200-unit cube behaves like a\n  1 m crate; a 2000-unit-tall scene is a 10 m drop, so very large scenes\n  look like they fall in slow motion (they're big, not slow). Keep play\n  scenes roughly real-world sized when the motion should feel snappy.\n- **Body roles**:\n  - `'positioned'` (the default) — an immovable collider: floors, walls,\n    ramps, furniture. Ignores gravity, blocks and is never pushed.\n  - `'dynamic'` — fully simulated: falls, rolls, gets pushed, knocks\n    other dynamic bodies around.\n  - **kinematic movers** — a positioned body that ANIMATES (see below)\n    follows its animation exactly and shoves dynamic bodies out of its way.\n    (The GameControl character is kinematic too — `game-controls` skill.)\n\n## `physics(props)` — the DSL call\n\nApplies to the SELECTED object(s), like other aliases. Unlike\n`updateEvent`'s nested objects, `physics()` MERGES: pass only the fields\nyou change; repeated calls tweak incrementally. (The axis-lock triples are\nthe exception — each is replaced whole.)\n\n- `type`: `'positioned'` (default) | `'dynamic'`.\n- `friction` (0..1, default 0.5) — surface grip. The engine AVERAGES the\n  friction of the two touching surfaces, so an icy floor still slides a\n  grippy crate somewhat.\n- `bounce` (0..1, default 0.2) — restitution, also AVERAGED between the\n  two surfaces: a `bounce: 1` ball on a `bounce: 0.2` floor bounces at\n  ~0.6. For a properly bouncy ball, raise the floor's bounce too.\n- `density` (default 1; the UI calls it \"Weight\") — mass comes from\n  density x volume. It decides who pushes whom in contacts (a dense crate\n  plows through light ones); it does NOT change free-fall speed.\n- `gravityScale` (default 1) — per-object gravity multiplier: `0` floats\n  in place (still collidable — good for zero-g props), `0.2` feels\n  moon-like, a small negative value slowly RISES (balloon).\n- `damping` (default 0) — air resistance on movement AND spin: higher\n  values fall slower, stop rolling sooner, kill drift. The usual fix for\n  \"it slides/rolls forever\".\n- `colliderType`: `'convex'` (default — fast; wraps the mesh in a convex\n  hull, sealing any concavity shut) | `'trimesh'` (exact triangles —\n  required for bowls, arches, tunnels, ramps with holes; slower). Use\n  trimesh on positioned decor; keep dynamic bodies convex.\n- `fusedBody` (default true; UI \"Children: Fused/Separate\") — a group with\n  `fusedBody: true` is ONE rigid body built from all child meshes (a table\n  falls as a table). With `false` each child simulates independently —\n  select the children and give them their own physics props (a stack of\n  crates you grouped for tidiness but want to topple crate by crate).\n- `enabledTranslation` / `enabledRotation`: `[x, y, z]` booleans\n  (dynamic bodies only) — axis locks, replaced WHOLE. Lock what shouldn't\n  move: `enabledRotation: [false, false, false]` makes a crate slide\n  without tumbling; `enabledTranslation: [true, true, false]` +\n  `enabledRotation: [false, false, true]` confines a body to the XY plane\n  for 2D-style physics.\n- You may also pass the raw schema names `rigidBody` / `restitution`\n  (`type` / `bounce` are their friendly spellings).\n- `pointMass` exists in the schema but the runtime ignores it — don't use.\n\nEnable/disable semantics: an object's physics `enabled` defaults to\n`'visibility'`, so once the page simulation is on, every VISIBLE object\nalready participates as a positioned collider — and HIDING an object removes\nit from the simulation. Still call `physics()` explicitly on the objects\nthat matter so their type/friction/bounce are deliberate. The DSL can only\nenable and tune: `physics(null)` / `physics({ enabled: false })` do NOT\nremove physics from an object — to keep something out of the simulation,\nhide it, or ask the user to set Enabled: No in its Physics panel.\n\n## Kinematic movers — animated colliders that push things\n\nThis is THE carve-out from code-driven motion (# Motion & interactivity):\nper-frame bridge writes are teleports the simulation cannot see, so a mover\nthat must PUSH dynamic bodies is state-driven — the one motion that stays\non `animate()`. A POSITIONED object becomes a kinematic body when it has\nany state that\nanimates its transform (e.g. created with `animate()`), a `Follow` event,\nor a `DragDrop` event. It follows its animation exactly (never pushed\nback) and bulldozes dynamic bodies — the way to build moving platforms,\nsweepers, flippers and crushers without any force API:\n\n```\n// conveyor platform sliding back and forth, carrying/pushing dynamic props\nmesh('Cube', { width: 800, height: 60, depth: 400 });\nposition({ y: 200 }); rename('Mover');\nphysics({ type: 'positioned', friction: 0.9 });\nanimate({ position: { x: 1000 } },\n        { duration: 2500, easing: 'ease-in-out', loop: true, direction: 'alternate' });\n```\n\nA spinning sweeper is the same recipe with `rotation: { y: 360 }` and\n`easing: 'linear'`. A `DragDrop` event on a positioned object makes a\nhand-controlled shover — the user drags it around to knock dynamic bodies\nover (wire the event via the `interactivity` skill).\n\n## Spawning dynamic bodies — the Create action\n\nThe `'Create'` action instantiates copies of a template object at runtime,\noptionally as dynamic bodies with an initial velocity — the only way to\nlaunch something (there is no force/impulse API):\n\n```\n// Space bar fires a ball from above the scene at the stack\nconst e = addEvent('KeyDown');\nupdateEvent(e, { key: 'Space' });\nconst c = addAction(e, 'Create');\n// ballId: real id from get_scene/get_objects; velocity is world units/sec\nupdateAction(e, c.actionId, {\n\tobject: ballId,\n\tdynamic: true,\n\tposition: [-2000, 600, 0],\n\tvelocity: [3000, 200, 0],\n\tdestroy: 'Time', lifetime: 4,\n});\n```\n\n- `dynamic: true` (the default) spawns a simulated body (the template's\n  physics props apply — give the template `physics({ type: 'dynamic',\n  ... })` first); `false` spawns a prop that just glides along\n  `velocity`.\n- `position` / `rotation` — spawn transform; `coordinateSystem:\n  'world'` (default) or an object id to spawn relative to it (a moving\n  cannon's muzzle).\n- `destroy`: `'No'` | `'Time'` (+ `lifetime` seconds) | `'Collision'`\n  (vanish on first contact) | `'Quantity'` (+ `destroyAfterQuantity` — only\n  the N newest survive). Always bound continuous spawners — thousands of live\n  bodies will tank the frame rate.\n- `hideBase: 'Yes'` hides the template object during play — the RIGHT way\n  to keep the original out of sight: its copies still simulate. Do NOT\n  `hide()` the template instead — an authored-invisible template (with the\n  default `'visibility'` enabling) spawns copies WITHOUT physics bodies.\n- On press-and-hold events `creationSpeed` (default 10/sec) throttles the\n  stream — a confetti or ball-pit hose.\n\n## Reacting to contacts\n\n`'Collision'` events fire when the host object's collider touches another\nbody: `target: 'scene'` reacts to ANY physics body (a dynamic crate landing\non a pressure plate), `target: 'character'` (the default) to the\ngame-controlled character only. `'Trigger'` events fire on entering an\ninvisible zone without physical contact. Both need physics enabled on the\npage and the objects involved. The full wiring (actions, pickup recipe,\n80 ms start suppression, zone props) is in the `games` skill; the generic\nevent/action API is in `interactivity`.\n\n## Recipes\n\nFalling stack (dominoes / tower knock-over):\n```\nmesh('Plane', { width: 4000, height: 4000 });\nrotation({ x: -90 }); rename('Floor');\nphysics({ type: 'positioned', friction: 0.8 });\nfor (let i = 0; i < 5; i++) {\n\tmesh('Cube', { width: 200, height: 200, depth: 200 });\n\t// stack with a 10-unit air gap so colliders don't start overlapping\n\tposition({ y: 110 + i * 210, x: (i % 2) * 20 });\n\tphysics({ type: 'dynamic', friction: 0.6 });\n}\n// play(): the tower settles, a spawned ball (Create recipe) topples it\n```\n\nBouncy ball: `physics({ type: 'dynamic', bounce: 0.9 })` on the ball AND\n`bounce: 0.8` on the floor (remember: averaged), drop it from height.\n\nBalloon: `physics({ type: 'dynamic', gravityScale: -0.05, damping: 0.9 })`\n— rises slowly, drifts to a stop against the ceiling. `gravityScale: 0`\n+ `damping: 0` is zero-g billiards instead.\n\nNon-tumbling slider (2.5D feel): `physics({ type: 'dynamic',\nenabledRotation: [false, false, false], enabledTranslation: [true, true,\nfalse] })` — moves only in the XY plane and never falls over.\n\n## Verify\n\n`play()` starts the simulation (not undoable; `stop()` resets to the\nauthored layout). A screenshot right after `play()` catches bodies\nmid-fall; for settled results, describe what should happen and have the\nuser confirm. Common failures:\n\n- **Jumps/explodes at start** — colliders overlap in the authored layout.\n  Leave small air gaps between stacked dynamic bodies and the floor; also\n  remember a convex hull is FATTER than a concave mesh looks.\n- **Falls through the floor** — the floor is hidden (`'visibility'`\n  enabling), or paper-thin while the body moves fast (tunneling): give\n  floors real thickness (≥ 50–100 units).\n- **Rests tilted / rolls off flat ground** — convex hull on a concave\n  mesh; use `colliderType: 'trimesh'` on the positioned decor.\n- **Slides or spins forever** — raise `friction` on both surfaces and/or\n  `damping` on the body.\n- **Won't move at all** — it's still `'positioned'`; only `type:\n  'dynamic'` bodies are simulated.\n"},"changes":[],"summary":"First saved snapshot. No earlier version is available for comparison.","summary_kind":"deterministic","summary_metadata":{}}