@needle-tools/engine 4.16.0-next.73c93c0 → 4.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +237 -0
- package/dist/{needle-engine.bundle-CqSR6UY7.umd.cjs → needle-engine.bundle-CXVjO2uF.umd.cjs} +3 -3
- package/dist/{needle-engine.bundle-CwvwWDWq.js → needle-engine.bundle-Cx8Qrpbp.js} +2 -2
- package/dist/{needle-engine.bundle-75BC4qb_.min.js → needle-engine.bundle-mHgaFC2W.min.js} +1 -1
- package/dist/needle-engine.js +2 -2
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/package.json +3 -2
- package/plugins/common/needle-engine-skill.md +106 -168
- package/plugins/vite/ai.js +1 -1
|
@@ -1,160 +1,122 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: needle-engine
|
|
3
3
|
description: Automatically provides Needle Engine context when working in a Needle Engine web project. Use this skill when editing TypeScript components, Vite config, GLB assets, or anything related to @needle-tools/engine.
|
|
4
|
-
metadata:
|
|
5
|
-
reviewed-against: "@needle-tools/engine@4.15.0"
|
|
6
|
-
last-reviewed: "2026-03"
|
|
7
4
|
---
|
|
8
5
|
|
|
9
6
|
# Needle Engine
|
|
10
7
|
|
|
11
|
-
You are an expert in Needle Engine — a web-first 3D engine built on Three.js with a
|
|
8
|
+
You are an expert in Needle Engine — a web-first 3D engine built on Three.js with a Unity/Blender-based workflow.
|
|
12
9
|
|
|
13
|
-
##
|
|
10
|
+
## Key concepts
|
|
14
11
|
|
|
15
|
-
**
|
|
16
|
-
- Editing TypeScript files that import from `@needle-tools/engine`
|
|
17
|
-
- Working on a project with `vite.config.ts` that uses `needlePlugins`
|
|
18
|
-
- Loading or debugging `.glb` files exported from Unity or Blender
|
|
19
|
-
- Using the Needle Engine Blender addon or Unity package
|
|
20
|
-
- Asking about component lifecycle, serialization, XR, networking, or deployment
|
|
21
|
-
|
|
22
|
-
**Do NOT use for:**
|
|
23
|
-
- Pure Three.js projects with no Needle Engine
|
|
24
|
-
- Non-web Unity/Blender work with no GLB export
|
|
25
|
-
|
|
26
|
-
---
|
|
27
|
-
|
|
28
|
-
## Quick Start
|
|
12
|
+
**Needle Engine** ships 3D scenes from Unity (or Blender) as GLB files and renders them in the browser using Three.js. TypeScript components attached to GameObjects in Unity are serialized into the GLB and re-hydrated at runtime in the browser.
|
|
29
13
|
|
|
14
|
+
### Embedding in HTML
|
|
30
15
|
```html
|
|
16
|
+
<!-- The <needle-engine> web component creates and manages a 3D context -->
|
|
31
17
|
<needle-engine src="assets/scene.glb"></needle-engine>
|
|
32
|
-
<script type="module">
|
|
33
|
-
import "@needle-tools/engine";
|
|
34
|
-
</script>
|
|
35
18
|
```
|
|
19
|
+
Access the context programmatically: `document.querySelector("needle-engine").context`
|
|
36
20
|
|
|
37
|
-
|
|
21
|
+
### Component lifecycle (mirrors Unity MonoBehaviour)
|
|
38
22
|
```ts
|
|
39
23
|
import { Behaviour, serializable, registerType } from "@needle-tools/engine";
|
|
40
24
|
|
|
41
25
|
@registerType
|
|
42
|
-
export class
|
|
43
|
-
@serializable()
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
26
|
+
export class MyComponent extends Behaviour {
|
|
27
|
+
@serializable() myValue: number = 1;
|
|
28
|
+
|
|
29
|
+
awake() {} // called once when instantiated
|
|
30
|
+
start() {} // called once on first frame
|
|
31
|
+
update() {} // called every frame
|
|
32
|
+
onEnable() {}
|
|
33
|
+
onDisable() {}
|
|
34
|
+
onDestroy() {}
|
|
35
|
+
onBeforeRender(_frame: XRFrame | null) {}
|
|
48
36
|
}
|
|
49
37
|
```
|
|
50
38
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
**Needle Engine** ships 3D scenes from Unity or Blender as GLB files and renders them in the browser using Three.js. TypeScript components attached to objects are serialized into the GLB and re-hydrated at runtime.
|
|
39
|
+
### Serialization
|
|
40
|
+
- `@registerType` — makes the class discoverable by the GLB deserializer
|
|
41
|
+
- `@serializable()` — marks a field for GLB deserialization (primitives)
|
|
42
|
+
- `@serializable(Object3D)` — for Three.js object references
|
|
43
|
+
- `@serializable(Texture)` — for textures (import Texture from "three")
|
|
44
|
+
- `@serializable(RGBAColor)` — for colors
|
|
58
45
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
46
|
+
### Accessing the scene
|
|
47
|
+
```ts
|
|
48
|
+
this.context.scene // THREE.Scene
|
|
49
|
+
this.context.mainCamera // active camera (THREE.Camera)
|
|
50
|
+
this.context.renderer // THREE.WebGLRenderer
|
|
51
|
+
this.context.time.frame // current frame number
|
|
52
|
+
this.context.time.deltaTime // seconds since last frame
|
|
53
|
+
this.gameObject // the THREE.Object3D this component is on
|
|
54
|
+
```
|
|
63
55
|
|
|
64
|
-
###
|
|
56
|
+
### Finding components
|
|
57
|
+
```ts
|
|
58
|
+
this.gameObject.getComponent(MyComponent)
|
|
59
|
+
this.gameObject.getComponentInChildren(MyComponent)
|
|
60
|
+
this.context.scene.getComponentInChildren(MyComponent)
|
|
61
|
+
|
|
62
|
+
// Global search (import as standalone functions from "@needle-tools/engine")
|
|
63
|
+
import { findObjectOfType, findObjectsOfType } from "@needle-tools/engine";
|
|
64
|
+
findObjectOfType(MyComponent, this.context)
|
|
65
|
+
findObjectsOfType(MyComponent, this.context)
|
|
66
|
+
```
|
|
65
67
|
|
|
66
|
-
|
|
68
|
+
### Input handling
|
|
69
|
+
```ts
|
|
70
|
+
// Polling
|
|
71
|
+
if (this.context.input.getPointerDown(0)) { /* pointer pressed */ }
|
|
72
|
+
if (this.context.input.getKeyDown("Space")) { /* space pressed */ }
|
|
67
73
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
src="assets/scene.glb"
|
|
71
|
-
camera-controls
|
|
72
|
-
auto-rotate
|
|
73
|
-
autoplay
|
|
74
|
-
background-color="#222"
|
|
75
|
-
environment-image="studio"
|
|
76
|
-
contactshadows
|
|
77
|
-
></needle-engine>
|
|
74
|
+
// Event-based (NEPointerEvent works across mouse, touch, and XR controllers)
|
|
75
|
+
this.gameObject.addEventListener("pointerdown", (e: NEPointerEvent) => { });
|
|
78
76
|
```
|
|
79
77
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
| `autoplay` | Auto-play animations in the loaded scene |
|
|
86
|
-
| `background-color` | Hex or RGB background color (e.g. `#ff0000`) |
|
|
87
|
-
| `background-image` | Skybox URL or preset: `studio`, `blurred-skybox`, `quicklook`, `quicklook-ar` |
|
|
88
|
-
| `background-blurriness` | Blur intensity for background (0–1) |
|
|
89
|
-
| `environment-image` | Environment lighting image URL or preset (same presets as `background-image`) |
|
|
90
|
-
| `contactshadows` | Enable contact shadows |
|
|
91
|
-
| `tone-mapping` | `none`, `linear`, `neutral`, `agx` |
|
|
92
|
-
| `poster` | Placeholder image URL shown while loading |
|
|
93
|
-
| `loadstart` / `progress` / `loadfinished` | Callback functions for loading lifecycle |
|
|
78
|
+
### Physics & raycasting
|
|
79
|
+
```ts
|
|
80
|
+
// Default raycasts hit visible geometry — no colliders needed
|
|
81
|
+
// Uses mesh BVH (bounding volume hierarchy) for accelerated raycasting, BVH is generated on a worker
|
|
82
|
+
const hits = this.context.physics.raycast();
|
|
94
83
|
|
|
95
|
-
|
|
84
|
+
// Physics-based raycasts (require colliders, uses Rapier physics engine)
|
|
85
|
+
const physicsHits = this.context.physics.raycastPhysics();
|
|
86
|
+
```
|
|
96
87
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
| Unity (C#) | Needle Engine (TypeScript) |
|
|
100
|
-
|---|---|
|
|
101
|
-
| `MonoBehaviour` | `Behaviour` |
|
|
102
|
-
| `[SerializeField]` / public field | `@serializable()` (required for all serialized fields) |
|
|
103
|
-
| `Instantiate(prefab)` | `instantiate(obj)` |
|
|
104
|
-
| `Destroy(obj)` | `destroy(obj)` |
|
|
105
|
-
| `GetComponent<T>()` | `this.gameObject.getComponent(T)` |
|
|
106
|
-
| `AddComponent<T>()` | `this.gameObject.addComponent(T)` |
|
|
107
|
-
| `FindObjectOfType<T>()` | `findObjectOfType(T, ctx)` |
|
|
108
|
-
| `transform.position` | `this.gameObject.worldPosition` (world) / `this.gameObject.position` (local) |
|
|
109
|
-
| `transform.rotation` | `this.gameObject.worldQuaternion` (world) / `this.gameObject.quaternion` (local) |
|
|
110
|
-
| `transform.localScale` | `this.gameObject.worldScale` (world) / `this.gameObject.scale` (local) |
|
|
111
|
-
| `Resources.Load<T>()` | No direct equivalent — use `@serializable(AssetReference)` to assign refs in editor, then `.instantiate()` or `.asset` at runtime |
|
|
112
|
-
| `StartCoroutine()` | `this.startCoroutine()` (in a component; unlike Unity, coroutines stop when the component is disabled) |
|
|
113
|
-
| `Time.deltaTime` | `this.context.time.deltaTime` |
|
|
114
|
-
| `Camera.main` | `this.context.mainCamera` (THREE.Camera) / `this.context.mainCameraComponent` (Needle Camera component) |
|
|
115
|
-
| `Debug.Log()` | `console.log()` |
|
|
116
|
-
| `OnCollisionEnter()` | `onCollisionEnter(col: Collision)` |
|
|
117
|
-
| `OnTriggerEnter()` | `onTriggerEnter(col: Collision)` |
|
|
88
|
+
### Networking & multiplayer
|
|
89
|
+
Needle Engine has built-in multiplayer. Add a `SyncedRoom` component to enable networking.
|
|
118
90
|
|
|
119
|
-
|
|
91
|
+
- `@syncField()` — automatically syncs a field across all connected clients
|
|
92
|
+
- Primitives (string, number, boolean) sync automatically on change
|
|
93
|
+
- Complex types (arrays/objects) require reassignment to trigger sync: `this.myArray = this.myArray`
|
|
94
|
+
- Key components: `SyncedRoom`, `SyncedTransform`, `PlayerSync`, `Voip`
|
|
95
|
+
- Uses WebSockets + optional WebRTC peer-to-peer connections
|
|
120
96
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
| Three.js | Needle Engine |
|
|
124
|
-
|---|---|
|
|
125
|
-
| `new Mesh(geo, mat)` | Created in Unity/Blender, exported as GLB; access via `Renderer.sharedMesh` / `Renderer.sharedMaterials` |
|
|
126
|
-
| `scene.add(obj)` | `this.gameObject.add(obj)` or `instantiate(prefab)` |
|
|
127
|
-
| `scene.remove(obj)` | `obj.removeFromParent()` (re-parent) or `destroy(obj)` (permanent) |
|
|
128
|
-
| `obj.position` | `obj.position` (local) / `obj.worldPosition` (world — Needle extension) |
|
|
129
|
-
| `obj.quaternion` | `obj.quaternion` (local) / `obj.worldQuaternion` (world — Needle extension) |
|
|
130
|
-
| `obj.scale` | `obj.scale` (local) / `obj.worldScale` (world — Needle extension) |
|
|
131
|
-
| `obj.getWorldPosition(v)` | `obj.worldPosition` (getter, no temp vec needed) |
|
|
132
|
-
| `obj.traverse(cb)` | `obj.traverse(cb)` (same — it's Three.js underneath) |
|
|
133
|
-
| `obj.children` | `obj.children` (same) |
|
|
134
|
-
| `obj.parent` | `obj.parent` (same) |
|
|
135
|
-
| `raycaster.intersectObjects()` | `this.context.physics.raycast()` (auto BVH, faster) |
|
|
136
|
-
| `renderer.setAnimationLoop(cb)` | `update() {}` in a component, or `onUpdate(cb)` hook |
|
|
137
|
-
| `clock.getDelta()` | `this.context.time.deltaTime` |
|
|
138
|
-
| `new GLTFLoader().load(url)` | `AssetReference.getOrCreate(base, url)` then `.instantiate()`, or `loadAsset(url)` |
|
|
139
|
-
|
|
140
|
-
Needle Engine extends `Object3D` with component methods (`getComponent`, `addComponent`, `worldPosition`, `worldQuaternion`, `worldScale`, `worldForward`, `worldRight`, `worldUp`, `contains`, etc.). `this.gameObject` is the `Object3D` a component is attached to. The underlying Three.js API still works directly.
|
|
97
|
+
### WebXR (VR & AR)
|
|
98
|
+
Needle Engine has built-in WebXR support for VR and AR across Meta Quest, Apple Vision Pro, and mobile AR.
|
|
141
99
|
|
|
142
|
-
|
|
100
|
+
- Add the `WebXR` component to enable VR/AR sessions
|
|
101
|
+
- Use `XRRig` to define the user's starting position — the user is parented to the rig during XR sessions
|
|
102
|
+
- Available components: `WebXRImageTracking`, `WebXRPlaneTracking`, `XRControllerModel`, `NeedleXRSession`
|
|
143
103
|
|
|
144
|
-
## Creating a
|
|
104
|
+
## Creating a new project
|
|
145
105
|
|
|
106
|
+
Use `create-needle` to scaffold a new Needle Engine project:
|
|
146
107
|
```bash
|
|
147
|
-
npm create needle my-app
|
|
148
|
-
npm create needle my-app -t react
|
|
149
|
-
npm create needle my-app -t vue
|
|
150
|
-
npm create needle my-app -t sveltekit # SvelteKit
|
|
151
|
-
npm create needle my-app -t nextjs # Next.js
|
|
152
|
-
npm create needle my-app -t react-three-fiber # R3F
|
|
108
|
+
npm create needle my-app # default Vite template
|
|
109
|
+
npm create needle my-app -t react # React template
|
|
110
|
+
npm create needle my-app -t vue # Vue.js template
|
|
153
111
|
```
|
|
154
112
|
|
|
155
|
-
|
|
113
|
+
Available templates: `vite` (default), `react`, `vue`, `sveltekit`, `svelte`, `nextjs`, `react-three-fiber`.
|
|
156
114
|
|
|
157
|
-
|
|
115
|
+
Use `npm create needle --list` to see all available templates.
|
|
116
|
+
|
|
117
|
+
## Vite plugin system
|
|
118
|
+
|
|
119
|
+
Needle Engine ships a set of Vite plugins via `needlePlugins(command, config, userSettings)`. Custom project plugins go in `vite.config.ts`.
|
|
158
120
|
|
|
159
121
|
```ts
|
|
160
122
|
import { defineConfig } from "vite";
|
|
@@ -167,71 +129,47 @@ export default defineConfig(async ({ command }) => ({
|
|
|
167
129
|
}));
|
|
168
130
|
```
|
|
169
131
|
|
|
170
|
-
---
|
|
171
|
-
|
|
172
132
|
## Deployment
|
|
173
133
|
|
|
174
|
-
|
|
175
|
-
- **
|
|
176
|
-
- **
|
|
177
|
-
- **
|
|
134
|
+
Projects can be deployed to:
|
|
135
|
+
- **Needle Cloud** — official hosting with automatic optimization (`npx needle-cloud deploy`)
|
|
136
|
+
- **Vercel** / **Netlify** — standard web hosting
|
|
137
|
+
- **itch.io** — for games and interactive experiences
|
|
138
|
+
- **Any static host** — Needle Engine projects are standard Vite web apps
|
|
178
139
|
|
|
179
|
-
From Unity, built-in deployment components (e.g. `DeployToNetlify`)
|
|
140
|
+
From Unity, use built-in deployment components (e.g. `DeployToNeedleCloud`, `DeployToNetlify`).
|
|
180
141
|
|
|
181
|
-
|
|
142
|
+
## Progressive loading (`@needle-tools/gltf-progressive`)
|
|
182
143
|
|
|
183
|
-
|
|
144
|
+
Needle Engine includes `@needle-tools/gltf-progressive` for progressive streaming of 3D models and textures. It creates a tiny initial file with embedded low-quality proxy geometry, then streams higher-quality LODs on demand. Results in ~90% smaller initial downloads with instant display.
|
|
184
145
|
|
|
146
|
+
Works standalone with any three.js project:
|
|
185
147
|
```ts
|
|
148
|
+
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
|
|
149
|
+
import { WebGLRenderer } from "three";
|
|
186
150
|
import { useNeedleProgressive } from "@needle-tools/gltf-progressive";
|
|
187
|
-
useNeedleProgressive(gltfLoader, renderer);
|
|
188
|
-
gltfLoader.load(url, (gltf) => scene.add(gltf.scene));
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
In Needle Engine projects this is built in — configure via **Compression & LOD Settings** in Unity.
|
|
192
151
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
## Searching the Documentation
|
|
152
|
+
const gltfLoader = new GLTFLoader();
|
|
153
|
+
const renderer = new WebGLRenderer();
|
|
196
154
|
|
|
197
|
-
|
|
155
|
+
// Register once — progressive loading happens automatically for all subsequent loads
|
|
156
|
+
useNeedleProgressive(gltfLoader, renderer);
|
|
198
157
|
|
|
158
|
+
gltfLoader.load(url, (gltf) => scene.add(gltf.scene));
|
|
199
159
|
```
|
|
200
|
-
needle_search("how to play animation clip from code")
|
|
201
|
-
needle_search("SyncedTransform multiplayer")
|
|
202
|
-
needle_search("deploy to Needle Cloud CI")
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
Use this *before* guessing at API details — the docs are the source of truth.
|
|
206
160
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
## Common Gotchas
|
|
210
|
-
|
|
211
|
-
- `@registerType` is required or the component won't be instantiated from GLB (Unity/Blender export adds this automatically, but hand-written components need it)
|
|
212
|
-
- GLB assets go in `assets/`, static files (fonts, images) in `public/` (configurable via `needle.config.json`)
|
|
213
|
-
- `useDefineForClassFields: false` must be set in `tsconfig.json` — otherwise decorators silently break field initialization
|
|
214
|
-
- `@syncField()` only triggers on reassignment — mutating an array/object in place won't sync; do `this.arr = this.arr`
|
|
215
|
-
- Physics callbacks (`onCollisionEnter` etc.) require a Needle `Collider` component on the GameObject
|
|
216
|
-
- `removeComponent()` does NOT call `onDestroy` — use `destroy(obj)` for full cleanup
|
|
217
|
-
- Prefer `instantiate()` and `destroy()` functions over `GameObject.instantiate()` / `GameObject.destroy()`
|
|
218
|
-
- `loadAsset()` returns a model wrapper (not an Object3D) — use `.scene` to get the root Object3D
|
|
219
|
-
- `AssetReference.getOrCreateFromUrl()` caches by URL — loading the same URL twice returns the same Object3D. Use `.instantiate()` or `loadAsset()` with `{ context }` for multiple copies
|
|
220
|
-
|
|
221
|
-
---
|
|
222
|
-
|
|
223
|
-
## References
|
|
224
|
-
|
|
225
|
-
For detailed API usage, read these reference files:
|
|
226
|
-
|
|
227
|
-
- [Full API Reference](https://raw.githubusercontent.com/needle-tools/ai/refs/heads/main/providers/claude/plugin/skills/needle-engine/references/api.md) — lifecycle, decorators, context API, animation, networking, XR, physics
|
|
228
|
-
- [Framework Integration](https://raw.githubusercontent.com/needle-tools/ai/refs/heads/main/providers/claude/plugin/skills/needle-engine/references/integration.md) — React, Svelte, Vue, vanilla JS examples + SSR patterns
|
|
229
|
-
- [Troubleshooting](https://raw.githubusercontent.com/needle-tools/ai/refs/heads/main/providers/claude/plugin/skills/needle-engine/references/troubleshooting.md) — common errors and fixes
|
|
230
|
-
- [Component Template](https://raw.githubusercontent.com/needle-tools/ai/refs/heads/main/providers/claude/plugin/skills/needle-engine/templates/my-component.ts) — annotated starter component
|
|
161
|
+
In Needle Engine projects, progressive loading is built in and can be configured via the **Compression & LOD Settings** component in Unity.
|
|
231
162
|
|
|
232
163
|
## Important URLs
|
|
233
|
-
|
|
234
164
|
- Docs: https://engine.needle.tools/docs/
|
|
235
165
|
- Samples: https://engine.needle.tools/samples/
|
|
236
166
|
- GitHub: https://github.com/needle-tools/needle-engine-support
|
|
237
167
|
- npm: https://www.npmjs.com/package/@needle-tools/engine
|
|
168
|
+
|
|
169
|
+
## Searching the documentation
|
|
170
|
+
|
|
171
|
+
Use the `needle_search` MCP tool to find relevant docs, forum posts, and community answers.
|
|
172
|
+
|
|
173
|
+
## Common gotchas
|
|
174
|
+
- Components must use `@registerType` or they won't be instantiated from GLB (this is handled automatically when exporting from Unity or Blender, but must be added manually for hand-written components)
|
|
175
|
+
- GLB assets are in `assets/`, static files in `include/` or `public/`
|
package/plugins/vite/ai.js
CHANGED
|
@@ -42,7 +42,7 @@ function writeSkill(claudeDir) {
|
|
|
42
42
|
mkdirSync(skillDir, { recursive: true });
|
|
43
43
|
}
|
|
44
44
|
const skillPath = join(skillDir, "SKILL.md");
|
|
45
|
-
const templatePath = join(__dirname, "
|
|
45
|
+
const templatePath = join(__dirname, "../../SKILL.md");
|
|
46
46
|
const content = readFileSync(templatePath, "utf8");
|
|
47
47
|
writeFileSync(skillPath, content, "utf8");
|
|
48
48
|
return skillPath;
|