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
98 lines
4.7 KiB
Markdown
98 lines
4.7 KiB
Markdown
# 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.
|
|
|
|
```ts
|
|
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
|
|
```ts
|
|
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):
|
|
```ts
|
|
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
|
|
```ts
|
|
// 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);
|
|
```
|