Files
pelpanagiotis a7c53a08a0 Initial commit: Unity Needle AR Menu project with MenuScene and SampleScene web apps
Add root .gitignore for Unity Library/Temp/Logs, IDE folders, and node_modules.
Include Assets, Needle TypeScript (MenuController, asset picker, WebXR), and project configuration.

Made-with: Cursor
2026-04-19 22:41:05 +03:00

4.7 KiB

Needle Engine — Physics Reference

Needle Engine uses Rapier (WASM) for physics. The Rapier physics backend is registered automatically at engine startup — no manual initialization needed. The WASM binary loads lazily on first use (when a collider or rigidbody component is created), so there's no upfront cost if physics aren't used.

NEEDLE_ENGINE_MODULES.RAPIER_PHYSICS.load() and .ready() exist for advanced use cases (e.g. accessing the raw Rapier API directly) but are not required for normal physics usage — just add Rigidbody and collider components and they work.

Colliders

Pick the shape that best fits the object — don't default to BoxCollider for everything.

import { BoxCollider, SphereCollider, CapsuleCollider, MeshCollider } from "@needle-tools/engine";

// Quick setup — auto-fits to mesh bounds, optionally adds rigidbody:
BoxCollider.add(myMesh, { rigidbody: true });
SphereCollider.add(myMesh, { rigidbody: true });

// Or add manually and configure:
const box = myObject.addComponent(BoxCollider);
// box.size, box.center

const sphere = myObject.addComponent(SphereCollider);
// sphere.radius (default: 0.5), sphere.center

const capsule = myObject.addComponent(CapsuleCollider);
// capsule.radius (default: 0.5), capsule.height (default: 2) — use for characters, poles, bottles

const mesh = myObject.addComponent(MeshCollider);
// mesh.convex = true for dynamic objects (required with Rigidbody)
// mesh.convex = false for static concave geometry (walls, terrain)

Use SphereCollider for balls, CapsuleCollider for characters/cylinders, MeshCollider for complex static geometry. Set isTrigger = true for trigger volumes.

Rigidbody

import { Rigidbody } from "@needle-tools/engine";

const rb = myObject.getComponent(Rigidbody);
rb.useGravity = true;
rb.mass = 2.0;
rb.isKinematic = false;        // true = not affected by forces

// Forces and impulses
rb.applyForce(new Vector3(0, 10, 0));     // continuous force (acceleration, applied over time)
rb.setForce(new Vector3(0, 10, 0));       // reset + apply new force in one call
rb.applyImpulse(new Vector3(5, 0, 0));    // instant velocity change (use for jumps, hits, explosions)

// Velocity — read and write directly (ALWAYS use these instead of accessing internals)
const vel = rb.getVelocity();              // current linear velocity (Vector3)
rb.setVelocity(new Vector3(0, 0, 0));     // set linear velocity directly
rb.setVelocity(0, 0, 0);                  // also accepts x, y, z args
const angVel = rb.getAngularVelocity();    // current angular velocity
rb.setAngularVelocity(new Vector3(0, 0, 0));
rb.smoothedVelocity;                       // averaged over ~10 frames (useful for UI/predictions)

// Stopping / resetting motion
rb.resetVelocities();                      // zero out both linear and angular velocity
rb.resetForces();                          // cancel all applied forces
rb.resetTorques();                         // cancel all applied torques
rb.resetForcesAndTorques();                // cancel both forces and torques

// Positioning
rb.teleport({ x: 0, y: 5, z: 0 });       // move without physics (resets velocities/forces)

// Sleep state
rb.wakeUp();                               // wake a sleeping body
rb.isSleeping;                             // check if body is asleep

Force vs Impulse: applyForce() is for continuous effects (thrusters, wind) — call every frame. applyImpulse() is for instant one-shot velocity changes (jumps, hits, button press) — call once.

Never access rb._body or internal Rapier handles directly. All velocity and force control is available through the public methods above. For example, to brake a rolling ball on key release, use rb.getVelocity() + rb.setVelocity() — not (rb as any)._body.linvel().

Key properties: mass, autoMass, useGravity, gravityScale (multiplier, 0 = no gravity), drag (linear damping), angularDrag, isKinematic, lockPositionX/Y/Z, lockRotationX/Y/Z, sleepThreshold, dominanceGroup, collisionDetectionMode (Discrete or Continuous).

API reference: https://engine.needle.tools/docs/api/Rigidbody

Physics callbacks

Defined on components (require a Collider on the same GameObject):

onCollisionEnter(col: Collision) { /* hit something */ }
onCollisionStay(col: Collision)  { /* still touching */ }
onCollisionExit(col: Collision)  { /* separated */ }
onTriggerEnter(col: Collision)   { /* entered trigger */ }
onTriggerStay(col: Collision)
onTriggerExit(col: Collision)

Raycasting

// Visual raycast (hits any visible geometry, no collider needed, BVH-accelerated)
const hits = this.context.physics.raycast();

// Physics engine raycast (hits Rapier colliders only)
const hit = this.context.physics.engine?.raycast(origin, direction);