@series-inc/rundot-3d-engine 0.6.8 → 0.6.10

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.
Files changed (2) hide show
  1. package/SKILL.md +366 -0
  2. package/package.json +3 -2
package/SKILL.md ADDED
@@ -0,0 +1,366 @@
1
+ # Rundot 3D Engine
2
+
3
+ The Rundot 3D Engine (`@series-inc/rundot-3d-engine`) is a Three.js-based game engine with ECS architecture, Rapier physics, and StowKit asset integration.
4
+
5
+ ## Quick Start
6
+
7
+ ```typescript
8
+ import { VenusGame, GameObject, Component } from "@series-inc/rundot-3d-engine"
9
+ import { MeshRenderer, RigidBodyComponentThree, RigidBodyType, ColliderShape } from "@series-inc/rundot-3d-engine/systems"
10
+
11
+ class MyGame extends VenusGame {
12
+ protected async onStart(): Promise<void> {
13
+ // Load assets
14
+ const stowkit = StowKitSystem.getInstance()
15
+ const buildJson = (await import("../prefabs/build.json")).default
16
+ await stowkit.loadFromBuildJson(buildJson, {
17
+ fetchBlob: (path) => fetch(path).then(r => r.blob()),
18
+ })
19
+
20
+ // Create a player
21
+ const player = new GameObject("Player")
22
+ player.position.set(0, 1, 0)
23
+
24
+ // Add mesh
25
+ const meshObj = new GameObject("PlayerMesh")
26
+ meshObj.addComponent(new MeshRenderer("player_mesh"))
27
+ player.add(meshObj)
28
+
29
+ // Add physics
30
+ player.addComponent(new RigidBodyComponentThree({
31
+ type: RigidBodyType.DYNAMIC,
32
+ shape: ColliderShape.CAPSULE,
33
+ radius: 0.5,
34
+ height: 2,
35
+ lockRotationX: true,
36
+ lockRotationY: true,
37
+ lockRotationZ: true,
38
+ }))
39
+
40
+ // Add lighting
41
+ const light = new THREE.DirectionalLight(0xffffff, 1)
42
+ light.position.set(5, 10, 5)
43
+ this.scene.add(light)
44
+ }
45
+
46
+ protected preRender(deltaTime: number): void {
47
+ // Per-frame logic
48
+ }
49
+
50
+ protected async onDispose(): Promise<void> {
51
+ // Cleanup
52
+ }
53
+ }
54
+
55
+ MyGame.create()
56
+ ```
57
+
58
+ ## Core Architecture
59
+
60
+ ### VenusGame — Game Base Class
61
+
62
+ Manages renderer, scene, camera, and all engine systems.
63
+
64
+ ```typescript
65
+ class MyGame extends VenusGame {
66
+ protected getConfig(): VenusGameConfig {
67
+ return {
68
+ backgroundColor: 0x87CEEB,
69
+ antialias: true,
70
+ shadowMapEnabled: true,
71
+ shadowMapType: "vsm", // or "pcf_soft"
72
+ toneMapping: "aces", // "aces" | "linear" | "none"
73
+ toneMappingExposure: 1.0,
74
+ audioEnabled: true,
75
+ cameraType: "perspective", // or "orthographic"
76
+ orthoSize: 10, // half-height for ortho camera
77
+ }
78
+ }
79
+
80
+ protected async onStart(): Promise<void> { /* init game */ }
81
+ protected preRender(deltaTime: number): void { /* per-frame logic */ }
82
+ protected async onDispose(): Promise<void> { /* cleanup */ }
83
+ }
84
+ ```
85
+
86
+ **Static access** (after initialization):
87
+ - `VenusGame.scene` — Three.js scene
88
+ - `VenusGame.camera` — active camera
89
+ - `VenusGame.renderer` — WebGL renderer
90
+ - `VenusGame.instance` — game instance
91
+
92
+ **Lifecycle order:** Physics → Tweens → Components → `preRender()` → `render()`
93
+
94
+ **IMPORTANT:** Use the `deltaTime` parameter in `preRender()`. Never call `this.clock.getDelta()`.
95
+
96
+ ### GameObject — Entity Class
97
+
98
+ Extends `THREE.Object3D`. Auto-added to scene on creation.
99
+
100
+ ```typescript
101
+ const obj = new GameObject("MyObject")
102
+ obj.position.set(0, 1, 0)
103
+ obj.addComponent(new MyComponent())
104
+
105
+ // Hierarchy
106
+ const child = new GameObject("Child")
107
+ obj.add(child)
108
+
109
+ // Component access
110
+ const comp = obj.getComponent(MyComponent)
111
+ obj.hasComponent(MyComponent)
112
+ obj.removeComponent(MyComponent)
113
+
114
+ // Enable/disable
115
+ obj.setEnabled(false) // triggers onDisabled() on all components
116
+ obj.setEnabled(true) // triggers onEnabled()
117
+
118
+ // Cleanup
119
+ obj.dispose() // disposes all components and children
120
+ ```
121
+
122
+ ### Component — Behavior Base Class
123
+
124
+ ```typescript
125
+ class MyComponent extends Component {
126
+ protected onCreate(): void {
127
+ // Called when added to GameObject — init here
128
+ }
129
+
130
+ public update(deltaTime: number): void {
131
+ // Called every frame
132
+ this.gameObject.position.x += deltaTime
133
+ }
134
+
135
+ public lateUpdate(deltaTime: number): void {
136
+ // Called after all update() calls — camera follow, UI updates
137
+ }
138
+
139
+ public onEnabled(): void { /* GameObject enabled */ }
140
+ public onDisabled(): void { /* GameObject disabled */ }
141
+
142
+ protected onCleanup(): void {
143
+ // Called on removal or dispose — cleanup listeners, resources
144
+ }
145
+ }
146
+ ```
147
+
148
+ **Access from Component:**
149
+ - `this.gameObject` — the attached GameObject
150
+ - `this.scene` — the Three.js scene
151
+ - `this.getComponent(Type)` — get sibling component
152
+
153
+ ## Systems
154
+
155
+ ### MeshRenderer — Static Mesh Display
156
+
157
+ ```typescript
158
+ import { MeshRenderer } from "@series-inc/rundot-3d-engine/systems"
159
+
160
+ // ALWAYS use child GameObject pattern
161
+ const renderer = new MeshRenderer("asset_name", castShadow?, receiveShadow?, isStatic?, materialOverride?)
162
+ const meshObj = new GameObject("Mesh")
163
+ meshObj.addComponent(renderer)
164
+ parentGameObject.add(meshObj)
165
+
166
+ // Check if loaded
167
+ renderer.isLoaded()
168
+ renderer.getBounds()
169
+ renderer.setVisible(false)
170
+ renderer.setMaterial(customMaterial)
171
+ ```
172
+
173
+ **Static meshes** (`isStatic: true`) skip per-frame matrix updates — use for non-moving objects.
174
+
175
+ ### SkeletalRenderer — Animated Characters
176
+
177
+ ```typescript
178
+ import { SkeletalRenderer } from "@series-inc/rundot-3d-engine/systems"
179
+
180
+ const skelRenderer = new SkeletalRenderer("character_mesh")
181
+ const meshObj = new GameObject("CharacterMesh")
182
+ meshObj.addComponent(skelRenderer)
183
+ character.add(meshObj)
184
+ ```
185
+
186
+ ### RigidBodyComponentThree — Physics
187
+
188
+ ```typescript
189
+ import { RigidBodyComponentThree, RigidBodyType, ColliderShape } from "@series-inc/rundot-3d-engine/systems"
190
+
191
+ // Dynamic (affected by physics)
192
+ new RigidBodyComponentThree({
193
+ type: RigidBodyType.DYNAMIC,
194
+ shape: ColliderShape.BOX, // BOX, SPHERE, CAPSULE
195
+ size: new THREE.Vector3(1, 1, 1), // box dimensions
196
+ radius: 0.5, // sphere/capsule radius
197
+ height: 2, // capsule height
198
+ mass: 1.0,
199
+ friction: 0.5,
200
+ restitution: 0.8, // bounciness
201
+ linearDamping: 0.5,
202
+ angularDamping: 0.5,
203
+ lockRotationX/Y/Z: true, // lock rotation axes
204
+ fitToMesh: true, // auto-size from mesh bounds
205
+ })
206
+
207
+ // Static (immovable)
208
+ new RigidBodyComponentThree({ type: RigidBodyType.STATIC, shape: ColliderShape.BOX, size: ... })
209
+
210
+ // Kinematic (script-controlled)
211
+ new RigidBodyComponentThree({ type: RigidBodyType.KINEMATIC, ... })
212
+
213
+ // Trigger (sensor, no physics response)
214
+ new RigidBodyComponentThree({ type: RigidBodyType.STATIC, shape: ColliderShape.BOX, isSensor: true })
215
+ rb.registerOnTriggerEnter((other) => console.log("entered", other.name))
216
+ rb.registerOnTriggerExit((other) => console.log("exited", other.name))
217
+
218
+ // Velocity & forces (dynamic only)
219
+ rb.setVelocity(new THREE.Vector3(0, 5, 0))
220
+ rb.applyImpulse(new THREE.Vector3(10, 0, 0))
221
+ rb.applyForce(new THREE.Vector3(0, -9.8, 0))
222
+ ```
223
+
224
+ ### AnimationGraphComponent — State Machine Animations
225
+
226
+ ```typescript
227
+ import { AnimationGraphComponent } from "@series-inc/rundot-3d-engine/systems"
228
+
229
+ const config = {
230
+ parameters: {
231
+ speed: { type: "float", default: 0 },
232
+ isGrounded: { type: "bool", default: true },
233
+ },
234
+ states: {
235
+ idle: { animation: "idle" },
236
+ walk: { animation: "walk" },
237
+ run: { animation: "run" },
238
+ locomotion: {
239
+ tree: {
240
+ parameter: "speed",
241
+ children: [
242
+ { animation: "idle", threshold: 0 },
243
+ { animation: "walk", threshold: 1 },
244
+ { animation: "run", threshold: 2 },
245
+ ],
246
+ },
247
+ },
248
+ },
249
+ transitions: [
250
+ { from: "idle", to: "walk", when: { speed: 1 } },
251
+ { from: "walk", to: "run", when: { speed: 2 } },
252
+ { from: "attack", to: "idle", exitTime: 1.0 }, // after anim finishes
253
+ ],
254
+ initialState: "idle",
255
+ }
256
+
257
+ const animGraph = new AnimationGraphComponent(model, config)
258
+ character.addComponent(animGraph)
259
+
260
+ // Drive transitions via parameters
261
+ animGraph.setParameter("speed", 2)
262
+ animGraph.setState("attack") // direct state change
263
+ animGraph.getCurrentState()
264
+ ```
265
+
266
+ ### StowKitSystem — Asset Loading
267
+
268
+ ```typescript
269
+ import { StowKitSystem } from "@series-inc/rundot-3d-engine/systems"
270
+
271
+ const stowkit = StowKitSystem.getInstance()
272
+
273
+ // Load from build.json
274
+ await stowkit.loadFromBuildJson(buildJson, {
275
+ fetchBlob: (path) => fetch(path).then(r => r.blob()),
276
+ })
277
+
278
+ // Access assets
279
+ const mesh = await stowkit.getMesh("name") // async
280
+ const mesh = stowkit.getMeshSync("name") // sync (null if not loaded)
281
+ const tex = await stowkit.getTexture("name")
282
+ const clip = await stowkit.getAnimation("walk", "character_mesh")
283
+ const audio = await stowkit.getAudio("sfx_click")
284
+ const skinned = await stowkit.getSkinnedMesh("character", 1.0)
285
+
286
+ // Clone with shadow settings
287
+ const clone = await stowkit.cloneMesh("name", castShadow, receiveShadow)
288
+
289
+ // GPU instancing
290
+ await stowkit.registerMeshForInstancing("coin_batch", "coin_mesh", true, true, 500)
291
+ ```
292
+
293
+ ### Other Systems
294
+
295
+ - **AudioSystem** — 2D/3D positional audio with AudioListener management
296
+ - **InputManager** — keyboard, mouse, touch, and gamepad input
297
+ - **LightingSystem** — directional, point, and spot lights with shadow management
298
+ - **NavigationSystem** — A* pathfinding on navigation meshes
299
+ - **ParticleSystem** — GPU particle effects
300
+ - **TweenSystem** — property animation and easing
301
+ - **UISystem** — HTML-based UI overlay system
302
+ - **PrefabSystem** — load and instantiate prefab hierarchies from JSON
303
+ - **SplineSystem** — Catmull-Rom spline paths for cameras, movement, etc.
304
+
305
+ ## Patterns
306
+
307
+ ### Separation of Concerns
308
+
309
+ ```typescript
310
+ // Logic/physics on parent
311
+ const character = new GameObject("Character")
312
+ character.addComponent(new CharacterController())
313
+ character.addComponent(new RigidBodyComponentThree({ type: RigidBodyType.DYNAMIC, shape: ColliderShape.CAPSULE }))
314
+
315
+ // Visual as child (can offset independently)
316
+ const visual = new GameObject("Visual")
317
+ visual.addComponent(new SkeletalRenderer("character_mesh"))
318
+ character.add(visual)
319
+ visual.position.y = -1
320
+ ```
321
+
322
+ ### Cache Component References
323
+
324
+ ```typescript
325
+ class MyComponent extends Component {
326
+ private meshRenderer?: MeshRenderer
327
+
328
+ protected onCreate(): void {
329
+ this.meshRenderer = this.getComponent(MeshRenderer) // cache in onCreate
330
+ }
331
+
332
+ public update(deltaTime: number): void {
333
+ // use cached ref — don't search every frame
334
+ }
335
+ }
336
+ ```
337
+
338
+ ### Factory Pattern
339
+
340
+ ```typescript
341
+ class EnemyFactory {
342
+ static create(type: string): GameObject {
343
+ const enemy = new GameObject(`Enemy_${type}`)
344
+ enemy.addComponent(new EnemyAI())
345
+ const meshObj = new GameObject("Mesh")
346
+ meshObj.addComponent(new MeshRenderer(`enemy_${type}`))
347
+ enemy.add(meshObj)
348
+ return enemy
349
+ }
350
+ }
351
+ ```
352
+
353
+ ## Common Mistakes
354
+
355
+ - **Don't create GameObjects in `update()`** — causes memory leaks. Create once, reuse or pool.
356
+ - **Don't access `this.gameObject` in constructor** — use `onCreate()` instead.
357
+ - **Don't forget `dispose()`** — always dispose GameObjects when done.
358
+ - **Don't add multiple components of same type** to one GameObject — use child GameObjects.
359
+ - **Don't call `this.clock.getDelta()`** — use the `deltaTime` parameter.
360
+ - **Don't bypass MeshRenderer** by loading meshes directly from StowKitSystem — use the component pattern.
361
+
362
+ ## Dependencies
363
+
364
+ - `three` (peer, >=0.180.0)
365
+ - `@dimforge/rapier3d` — Rapier physics engine
366
+ - `@series-inc/stowkit-reader` + `@series-inc/stowkit-three-loader` — asset loading
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@series-inc/rundot-3d-engine",
3
- "version": "0.6.8",
3
+ "version": "0.6.10",
4
4
  "type": "module",
5
5
  "types": "./dist/index.d.ts",
6
6
  "scripts": {
@@ -90,6 +90,7 @@
90
90
  "files": [
91
91
  "dist",
92
92
  "docs",
93
- "scripts"
93
+ "scripts",
94
+ "SKILL.md"
94
95
  ]
95
96
  }