Files
AR-Menu/.agents/skills/needle-engine/references/components.md
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

450 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Needle Engine — Built-in Components Reference
## Table of Contents
- [Physics](#physics)
- [Animation](#animation)
- [Audio](#audio)
- [Video](#video)
- [Lighting and Shadows](#lighting-and-shadows)
- [Post-Processing](#post-processing)
- [Camera](#camera)
- [Scene Switching](#scene-switching)
- [Interaction](#interaction)
- [Splines](#splines)
- [Debug Tools](#debug-tools)
- [Utilities](#utilities)
---
## Physics
See [physics.md](physics.md) for the full physics reference (colliders, Rigidbody API, raycasting, async loading).
Rapier initializes automatically — just add collider and rigidbody components. Use `SphereCollider` for balls, `CapsuleCollider` for characters/cylinders, not BoxCollider for everything. Use `applyImpulse` for one-shot actions, `applyForce` for continuous. Never access `rb._body` internals.
---
## Animation
### Animation (simple clip playback)
```ts
import { Animation } from "@needle-tools/engine";
const anim = this.gameObject.getComponent(Animation);
anim.play(); // play default clip
anim.play("Idle"); // play by clip name
anim.stop();
anim.loop = true; // loop playback (default: true)
anim.playAutomatically = true; // auto-play on enable (default: true)
```
### Animator (state machine — Unity Animator Controller)
```ts
import { Animator } from "@needle-tools/engine";
const anim = this.gameObject.getComponent(Animator);
anim.play("Run"); // play by state name
anim.setFloat("Speed", 1.5); // Animator parameters (match Unity parameter names)
anim.setBool("IsGrounded", true);
anim.setTrigger("Jump");
anim.speed = 0.5; // global playback speed multiplier
```
### PlayableDirector (Timeline)
```ts
import { PlayableDirector } from "@needle-tools/engine";
const director = this.gameObject.getComponent(PlayableDirector);
director.play(); // start playback
director.pause();
director.stop();
director.time = 2.5; // scrub to time (seconds)
director.evaluate(); // evaluate at current time (use after setting time)
director.isPlaying // check playback state
director.isPaused
director.duration // total duration in seconds
```
---
## Audio
### AudioSource
```ts
import { AudioSource } from "@needle-tools/engine";
const audio = this.gameObject.getComponent(AudioSource);
audio.clip = "sounds/music.mp3"; // URL to audio file
audio.volume = 0.8;
audio.loop = true;
audio.spatialBlend = 1; // 0 = 2D, 1 = full 3D positional
audio.play();
audio.pause();
audio.stop();
// Browser autoplay policy: audio won't play until user interaction
AudioSource.registerWaitForAllowAudio(() => {
audio.play();
});
```
Key properties: `clip` (string/MediaStream), `volume` (01), `loop`, `spatialBlend` (01), `playOnAwake`, `pitch`, `minDistance`, `maxDistance`, `isPlaying`, `time`, `duration`.
### AudioListener
Represents the "ears" in the scene. Attach to the camera (auto-added to main camera if missing). Only one should be active.
```ts
import { AudioListener } from "@needle-tools/engine";
this.context.mainCamera?.addComponent(AudioListener);
```
---
## Video
### VideoPlayer
```ts
import { VideoPlayer } from "@needle-tools/engine";
const vp = this.gameObject.addComponent(VideoPlayer);
vp.url = "videos/intro.mp4"; // mp4, webm, or m3u8 (HLS)
vp.isLooping = true;
vp.playOnAwake = true;
vp.play();
vp.pause();
vp.stop();
vp.currentTime = 10; // seek to 10 seconds
// Webcam / screen capture:
vp.setVideo(mediaStream);
// HLS livestreams: just set an m3u8 URL — hls.js loads automatically
vp.url = "https://stream.example.com/live.m3u8";
```
Key properties: `url`, `isLooping`, `playbackSpeed`, `muted`, `playInBackground`, `screenspace`, `isPlaying`, `videoElement`, `videoTexture`.
The video texture is applied to the object's material by default (MaterialOverride render mode). The object needs a `Renderer` component.
---
## Lighting and Shadows
### Light
```ts
import { Light } from "@needle-tools/engine";
const light = this.gameObject.getComponent(Light);
light.intensity = 1.5;
light.color.set(1, 0.95, 0.9); // warm white
light.shadows = 2; // 0=None, 1=Hard, 2=Soft
light.shadowResolution = 2048;
```
Light types (set in Unity/Blender, not changeable at runtime): Directional (1), Point (2), Spot (0). Spot lights have `spotAngle` and `innerSpotAngle`. Point/Spot lights have `range`.
### ContactShadows
Soft ground shadows based on proximity — no lights needed.
```ts
import { ContactShadows } from "@needle-tools/engine";
// Auto-create fitted to scene
const shadows = ContactShadows.auto(this.context);
shadows.opacity = 0.6;
shadows.blur = 5;
// Or via HTML attribute:
// <needle-engine contactshadows="0.7">
```
### ShadowCatcher
Catches real-time shadows from light sources onto a surface. Use for AR ground planes.
```ts
import { ShadowCatcher } from "@needle-tools/engine";
const catcher = obj.addComponent(ShadowCatcher);
catcher.mode = 0; // 0=ShadowMask, 1=Additive, 2=Occluder
```
ContactShadows = soft ambient-style, no lights needed, better performance. ShadowCatcher = accurate shadows from real lights, higher cost.
### ReflectionProbe
Provides per-object environment reflections using cubemap or HDR textures. Objects can reference a specific probe as their reflection source, producing more accurate localized reflections than a single global environment map.
```ts
import { ReflectionProbe } from "@needle-tools/engine";
// Typically set up in Unity/Blender: add ReflectionProbe to an object, assign a cubemap texture,
// then on Renderer components set the probe as "anchor override"
// Check if a material is using a reflection probe:
ReflectionProbe.isUsingReflectionProbe(material);
```
Debug: `?debugreflectionprobe` URL param. Disable all: `?noreflectionprobe`.
---
## Post-Processing
See [postprocessing.md](postprocessing.md) for the full post-processing reference (all effects, parameters, runtime changes).
Key points: Use `this.context.postprocessing.addEffect(effect)` / `.removeEffect(effect)`. Effects use `VolumeParameter` — set values with `.value`. Toggle with `effect.enabled`. Loads async via `NEEDLE_ENGINE_MODULES.POSTPROCESSING`.
---
## Camera
```ts
// Access the main camera
this.context.mainCamera // THREE.Camera
this.context.mainCameraComponent // Needle Camera component
// Switch the active camera:
import { Camera } from "@needle-tools/engine";
const cam = targetObject.getComponent(Camera);
this.context.setCurrentCamera(cam); // make this the active camera
// Camera properties
cam.fieldOfView = 60;
cam.nearClipPlane = 0.1;
cam.farClipPlane = 1000;
cam.orthographic = false;
// Screen to world
const ray = cam.screenPointToRay(screenX, screenY);
```
Key properties: `fieldOfView`, `nearClipPlane`, `farClipPlane`, `backgroundColor`, `orthographic`, `orthographicSize`, `clearFlags`, `targetTexture`.
### Custom camera control (first-person, etc.)
For code-only scenes where you want full camera control (first-person, fly cam, etc.):
1. Use `<needle-engine camera-controls="0">` to prevent auto-added OrbitControls
2. Remove any existing OrbitControls — they override camera rotation every frame:
```ts
import { OrbitControls } from "@needle-tools/engine";
onStart(ctx => {
// Remove OrbitControls so they don't fight your custom camera logic
const cam = ctx.mainCamera;
const orbit = cam?.getComponent(OrbitControls);
if (orbit) orbit.destroy();
});
```
3. Write a `Behaviour` component for camera control — use `update()` and the engine's input system (`this.context.input`), not raw DOM events or `requestAnimationFrame`
4. See the [FirstPersonCharacter sample](https://github.com/needle-tools/needle-engine-samples/blob/main/package/Runtime/FirstPersonController/Scripts/FirstPersonController~/FirstPersonCharacter.ts) for a working example
---
## Scene Switching
`SceneSwitcher` manages loading/unloading multiple GLB scenes — useful for multi-room apps, configurators, portfolios.
```ts
import { SceneSwitcher } from "@needle-tools/engine";
const switcher = this.gameObject.getComponent(SceneSwitcher);
await switcher.select(0); // by index
await switcher.select("myScene"); // by name/URI
await switcher.selectNext();
await switcher.selectPrev();
// Add scenes dynamically
switcher.addScene("assets/room2.glb");
// Events
switcher.addEventListener("loadscene-finished", (e) => {
console.log("Loaded:", e.detail.scene.url);
});
```
Key properties: `scenes` (AssetReference[]), `currentIndex`, `preloadNext`, `preloadPrevious`, `useHistory` (browser back/forward), `useKeyboard` (arrow keys), `useSwipe`, `queryParameterName` (URL param, default `"scene"`).
You can also implement scene switching yourself using `AssetReference` or `loadAsset()`:
```ts
import { AssetReference, loadAsset } from "@needle-tools/engine";
// With AssetReference (caches by URL):
const ref = AssetReference.getOrCreate(baseUrl, "assets/room2.glb");
const instance = await ref.instantiate({ parent: this.context.scene });
// With loadAsset (returns a model wrapper):
const model = await loadAsset("assets/room2.glb");
this.context.scene.add(model.scene);
```
---
## Interaction
### DragControls
Enables dragging objects in 3D. Automatically takes ownership in networked scenes.
```ts
import { DragControls, DragMode } from "@needle-tools/engine";
const drag = obj.addComponent(DragControls);
drag.dragMode = DragMode.XZPlane; // horizontal plane
// Modes: XZPlane, Attached, HitNormal, DynamicViewAngle (default), SnapToSurfaces, None
```
### Duplicatable
Add alongside `DragControls` — dragging creates a clone instead of moving the original.
```ts
import { Duplicatable } from "@needle-tools/engine";
obj.addComponent(Duplicatable);
```
### DropListener
Enables drag-and-drop of files from the desktop into the 3D scene (GLB, FBX, OBJ, USDZ, VRM, images).
```ts
import { DropListener } from "@needle-tools/engine";
const dl = myObject.addComponent(DropListener);
dl.fitIntoVolume = true; // auto-scale dropped objects
dl.useNetworking = true; // sync drops to other clients
// Or load programmatically:
const loaded = await dl.loadFromURL("https://example.com/model.glb");
```
### CharacterController
Capsule collider + rigidbody for character movement. Auto-creates physics components on enable.
```ts
import { CharacterController } from "@needle-tools/engine";
const cc = this.gameObject.getComponent(CharacterController);
cc.move(new Vector3(0, 0, 0.1)); // move forward
cc.isGrounded; // true when touching ground
// For jumping, use the rigidbody directly:
if (cc.isGrounded) cc.rigidbody.applyImpulse(new Vector3(0, 5, 0));
```
`CharacterControllerInput` provides a ready-made WASD + Space control scheme with double-jump and animator integration.
For a full first-person controller example, see the [FirstPersonCharacter sample](https://github.com/needle-tools/needle-engine-samples/blob/main/package/Runtime/FirstPersonController/Scripts/FirstPersonController~/FirstPersonCharacter.ts).
For clickable hotspot labels on 3D objects (common in product configurators), see the [Hotspot sample](https://github.com/needle-tools/needle-engine-samples/blob/main/package/Runtime/Hotspots/Scripts/Needle.Hotspots~/Hotspot.ts).
### needle-menu (built-in UI menu)
The `<needle-menu>` web component provides a built-in hamburger menu. Components like `SyncedRoom` and `Voip` add buttons to it automatically. Access via `this.context.menu`.
```ts
// Add a button using ButtonInfo object (recommended)
this.context.menu.appendChild({
label: "My Action",
icon: "settings", // Google Material Icons name
onClick: () => { /* ... */ },
priority: 50, // higher = further right, always visible
});
// Or add a raw HTML button
const button = document.createElement("button");
button.textContent = "Click me";
button.onclick = () => { /* ... */ };
this.context.menu.appendChild(button);
// Control visibility (hiding requires Needle Engine PRO license in production)
this.context.menu.setVisible(false);
// Hide the Needle logo (requires license)
this.context.menu.showNeedleLogo(false);
// Set button priority (controls ordering and which buttons stay visible when space is limited)
NeedleMenu.setElementPriority(button, 90);
```
---
## Splines
### SplineContainer
Defines curves/paths in the scene. Can be created in Unity/Blender or from code.
```ts
import { SplineContainer } from "@needle-tools/engine";
import { Vector3 } from "three";
const spline = obj.addComponent(SplineContainer);
spline.addKnot({ position: new Vector3(0, 0, 0) })
.addKnot({ position: new Vector3(5, 2, 5) })
.addKnot({ position: new Vector3(10, 0, 0) });
spline.closed = false;
// Sample the spline (t: 01)
const point = spline.getPointAt(0.5); // world-space position
const tangent = spline.getTangentAt(0.5); // world-space tangent
```
### SplineWalker
Moves an object along a spline path.
```ts
import { SplineWalker } from "@needle-tools/engine";
const walker = obj.addComponent(SplineWalker);
walker.spline = splineContainer;
walker.duration = 5; // seconds for full traversal
walker.autoRun = true;
walker.useLookAt = true; // face movement direction
```
---
## Debug Tools
### Gizmos
Static methods for runtime debug drawing — shapes auto-remove after a duration (0 = one frame).
```ts
import { Gizmos } from "@needle-tools/engine";
Gizmos.DrawLine(start, end, color, duration, depthTest);
Gizmos.DrawWireSphere(center, radius, color, duration);
Gizmos.DrawRay(origin, direction, color, duration);
Gizmos.DrawLabel(position, text, size, duration);
Gizmos.DrawArrow(start, end, color, duration);
Gizmos.DrawWireBox(center, size, color, duration);
```
---
## Utilities
### EventList (Unity Events)
`EventList` is how Unity Events are serialized and invoked at runtime. Declare with `@serializable(EventList)` and call `.invoke()`.
```ts
import { EventList, serializable } from "@needle-tools/engine";
@serializable(EventList) onClick?: EventList;
// Invoke from code:
this.onClick?.invoke();
// Subscribe from code:
const unsub = this.onClick?.addEventListener(() => console.log("Clicked!"));
unsub(); // unsubscribe
```
### Creating Objects from Code
`ObjectUtils` provides convenience methods for creating primitives and text. These are helpers — you can always use standard Three.js objects directly (`new Mesh(geometry, material)`).
```ts
import { ObjectUtils, PrimitiveType } from "@needle-tools/engine";
const cube = ObjectUtils.createPrimitive(PrimitiveType.Cube, {
color: 0xff0000,
parent: this.gameObject,
position: { x: 0, y: 1, z: 0 }
});
const text = ObjectUtils.createText("Hello World");
this.context.scene.add(text);
```
Available primitives: `Cube`, `Sphere`, `Quad`, `Cylinder`. For anything more complex, use Three.js geometry directly or load GLB models.
### ParticleSystem
Full particle system with emission, shape, velocity, color/size over lifetime modules. Currently best configured via Unity/Blender — difficult to set up from code only.
```ts
import { ParticleSystem } from "@needle-tools/engine";
const ps = this.gameObject.getComponent(ParticleSystem);
ps.play();
ps.stop();
ps.pause();
```