@series-inc/rundot-3d-engine 0.3.0 → 0.4.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/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
- By downloading, copying, or using this template, you agree to the
2
- RUN.game Terms of Service:
3
-
4
- https://policy.run.game/eula.html
5
-
6
- © Series Inc. All rights reserved.
1
+ By downloading, copying, or using this template, you agree to the
2
+ RUN.game Terms of Service:
3
+
4
+ https://policy.run.game/eula.html
5
+
6
+ © Series Inc. All rights reserved.
@@ -1,321 +1,321 @@
1
- # Component
2
-
3
- Component is the base class for all behaviors that can be attached to GameObjects. It provides lifecycle hooks and automatic update registration.
4
-
5
- ## Quick Start
6
-
7
- ```typescript
8
- import { Component } from "@series-ai/rundot-3d-engine"
9
-
10
- class RotateComponent extends Component {
11
- private speed: number = 1
12
-
13
- constructor(speed: number = 1) {
14
- super()
15
- this.speed = speed
16
- }
17
-
18
- protected onCreate(): void {
19
- console.log("Component initialized")
20
- }
21
-
22
- public update(deltaTime: number): void {
23
- // Rotate GameObject each frame
24
- this.gameObject.rotation.y += this.speed * deltaTime
25
- }
26
-
27
- protected onCleanup(): void {
28
- console.log("Component cleaned up")
29
- }
30
- }
31
-
32
- // Use the component
33
- const box = new GameObject("RotatingBox")
34
- box.addComponent(new RotateComponent(2.0))
35
- ```
36
-
37
- ## Component Lifecycle
38
-
39
- Components have a well-defined lifecycle with automatic hook calls:
40
-
41
- ```typescript
42
- class LifecycleExample extends Component {
43
- protected onCreate(): void {
44
- // Called when component is added to GameObject
45
- // Use for initialization, getting other components
46
- }
47
-
48
- public onEnabled(): void {
49
- // Called when GameObject becomes enabled
50
- // Use to resume behavior, start effects
51
- }
52
-
53
- public update(deltaTime: number): void {
54
- // Called every frame while GameObject is enabled
55
- // Use for per-frame logic, movement, checks
56
- }
57
-
58
- public lateUpdate(deltaTime: number): void {
59
- // Called after all update() calls
60
- // Use for camera following, UI updates
61
- }
62
-
63
- public onDisabled(): void {
64
- // Called when GameObject becomes disabled
65
- // Use to pause behavior, hide effects
66
- }
67
-
68
- protected onCleanup(): void {
69
- // Called when component is removed or GameObject disposed
70
- // Use for cleanup, removing listeners, disposing resources
71
- }
72
- }
73
- ```
74
-
75
- ## Common Use Cases
76
-
77
- ### Accessing GameObject Properties
78
-
79
- ```typescript
80
- class MoveForward extends Component {
81
- private speed: number = 5
82
-
83
- public update(deltaTime: number): void {
84
- // Access GameObject's transform directly
85
- this.gameObject.position.z += this.speed * deltaTime
86
- }
87
- }
88
- ```
89
-
90
- ### Getting Other Components
91
-
92
- ```typescript
93
- class HealthDisplay extends Component {
94
- private meshRenderer?: MeshRenderer
95
-
96
- protected onCreate(): void {
97
- // Get component from same GameObject
98
- this.meshRenderer = this.getComponent(MeshRenderer)
99
-
100
- if (!this.meshRenderer) {
101
- console.warn("No MeshRenderer found!")
102
- }
103
- }
104
-
105
- public showDamage(): void {
106
- if (this.meshRenderer) {
107
- // Flash red or hide temporarily
108
- this.meshRenderer.setVisible(false)
109
- }
110
- }
111
- }
112
- ```
113
-
114
- ### Enable/Disable Behavior
115
-
116
- ```typescript
117
- class EnemyAI extends Component {
118
- private isActive: boolean = false
119
-
120
- public onEnabled(): void {
121
- this.isActive = true
122
- console.log("AI activated")
123
- }
124
-
125
- public onDisabled(): void {
126
- this.isActive = false
127
- console.log("AI deactivated")
128
- }
129
-
130
- public update(deltaTime: number): void {
131
- if (this.isActive) {
132
- // AI logic only runs when enabled
133
- this.findTarget()
134
- this.moveTowardsTarget(deltaTime)
135
- }
136
- }
137
- }
138
- ```
139
-
140
- ### Accessing Scene and Game
141
-
142
- ```typescript
143
- class SpawnManager extends Component {
144
- public spawnEnemy(): void {
145
- // Access the scene directly
146
- const scene = this.scene
147
-
148
- // Create new GameObjects
149
- const enemy = new GameObject("Enemy")
150
- enemy.position.set(10, 0, 10)
151
- }
152
- }
153
- ```
154
-
155
- ## API Overview
156
-
157
- ### Properties
158
-
159
- - `gameObject: GameObject` - The GameObject this component is attached to (read-only)
160
- - `scene: THREE.Scene` - The Three.js scene (convenience accessor)
161
-
162
- ### Methods
163
-
164
- - `getGameObject(): GameObject` - Get the attached GameObject
165
- - `getComponent<T>(type): T | undefined` - Get another component from same GameObject
166
- - `isAttached(): boolean` - Check if component is attached
167
-
168
- ### Lifecycle Hooks (Override These)
169
-
170
- - `onCreate(): void` - Initialization after attachment
171
- - `onEnabled(): void` - Called when GameObject becomes enabled
172
- - `onDisabled(): void` - Called when GameObject becomes disabled
173
- - `update(deltaTime: number): void` - Per-frame update
174
- - `lateUpdate(deltaTime: number): void` - Per-frame update after all update() calls
175
- - `onCleanup(): void` - Cleanup before removal
176
-
177
- ## Patterns & Best Practices
178
-
179
- ### Cache Component References
180
-
181
- ```typescript
182
- // Good - Cache in onCreate
183
- class Follower extends Component {
184
- private target?: Transform
185
-
186
- protected onCreate(): void {
187
- const targetObj = GameObject.find("Target")
188
- this.target = targetObj // Cache reference
189
- }
190
-
191
- public update(deltaTime: number): void {
192
- if (this.target) {
193
- // Use cached reference - fast!
194
- }
195
- }
196
- }
197
-
198
- // Bad - Search every frame
199
- class Follower extends Component {
200
- public update(deltaTime: number): void {
201
- const target = GameObject.find("Target") // Slow!
202
- if (target) {
203
- // ...
204
- }
205
- }
206
- }
207
- ```
208
-
209
- ### Use update() vs lateUpdate()
210
-
211
- ```typescript
212
- // Player movement in update()
213
- class PlayerController extends Component {
214
- public update(deltaTime: number): void {
215
- // Move player based on input
216
- this.movePlayer(deltaTime)
217
- }
218
- }
219
-
220
- // Camera follows player in lateUpdate()
221
- class CameraFollow extends Component {
222
- public lateUpdate(deltaTime: number): void {
223
- // Follow player AFTER they've moved
224
- this.followTarget(deltaTime)
225
- }
226
- }
227
- ```
228
-
229
- ### Proper Event Listener Cleanup
230
-
231
- ```typescript
232
- class ClickHandler extends Component {
233
- private boundOnClick: (e: MouseEvent) => void
234
-
235
- protected onCreate(): void {
236
- this.boundOnClick = this.onClick.bind(this)
237
- document.addEventListener("click", this.boundOnClick)
238
- }
239
-
240
- protected onCleanup(): void {
241
- // Always remove listeners!
242
- document.removeEventListener("click", this.boundOnClick)
243
- }
244
-
245
- private onClick(e: MouseEvent): void {
246
- console.log("Clicked!")
247
- }
248
- }
249
- ```
250
-
251
- ### Optional Update Methods
252
-
253
- ```typescript
254
- // Only implement update if you need it
255
- class StaticData extends Component {
256
- // No update() - won't be registered for updates (more efficient)
257
-
258
- protected onCreate(): void {
259
- // One-time initialization only
260
- }
261
- }
262
- ```
263
-
264
- ## Anti-Patterns
265
-
266
- ### Don't Access GameObject Before Attached
267
-
268
- ```typescript
269
- // Bad - gameObject doesn't exist yet!
270
- class BadComponent extends Component {
271
- private position = this.gameObject.position // ERROR!
272
-
273
- constructor() {
274
- super()
275
- }
276
- }
277
-
278
- // Good - Access in onCreate or later
279
- class GoodComponent extends Component {
280
- private position?: THREE.Vector3
281
-
282
- protected onCreate(): void {
283
- this.position = this.gameObject.position.clone()
284
- }
285
- }
286
- ```
287
-
288
- ### Don't Store References to Disposed GameObjects
289
-
290
- ```typescript
291
- // Bad - holding reference to disposed object
292
- class Spawner extends Component {
293
- private spawnedObjects: GameObject[] = []
294
-
295
- public spawnObject(): void {
296
- const obj = new GameObject("Spawned")
297
- this.spawnedObjects.push(obj)
298
- // If obj.dispose() is called elsewhere, array has dead reference
299
- }
300
- }
301
-
302
- // Good - Remove from array when disposing
303
- class Spawner extends Component {
304
- private spawnedObjects: GameObject[] = []
305
-
306
- public despawnObject(obj: GameObject): void {
307
- obj.dispose()
308
- const index = this.spawnedObjects.indexOf(obj)
309
- if (index > -1) {
310
- this.spawnedObjects.splice(index, 1)
311
- }
312
- }
313
- }
314
- ```
315
-
316
- ## Related Systems
317
-
318
- - [GameObject](GameObject.md) - Entity class that components attach to
319
- - [VenusGame](VenusGame.md) - Main game class with lifecycle
320
- - [ComponentUpdater](../../src/engine/core/ComponentUpdater.ts) - Internal update manager
321
-
1
+ # Component
2
+
3
+ Component is the base class for all behaviors that can be attached to GameObjects. It provides lifecycle hooks and automatic update registration.
4
+
5
+ ## Quick Start
6
+
7
+ ```typescript
8
+ import { Component } from "@series-ai/rundot-3d-engine"
9
+
10
+ class RotateComponent extends Component {
11
+ private speed: number = 1
12
+
13
+ constructor(speed: number = 1) {
14
+ super()
15
+ this.speed = speed
16
+ }
17
+
18
+ protected onCreate(): void {
19
+ console.log("Component initialized")
20
+ }
21
+
22
+ public update(deltaTime: number): void {
23
+ // Rotate GameObject each frame
24
+ this.gameObject.rotation.y += this.speed * deltaTime
25
+ }
26
+
27
+ protected onCleanup(): void {
28
+ console.log("Component cleaned up")
29
+ }
30
+ }
31
+
32
+ // Use the component
33
+ const box = new GameObject("RotatingBox")
34
+ box.addComponent(new RotateComponent(2.0))
35
+ ```
36
+
37
+ ## Component Lifecycle
38
+
39
+ Components have a well-defined lifecycle with automatic hook calls:
40
+
41
+ ```typescript
42
+ class LifecycleExample extends Component {
43
+ protected onCreate(): void {
44
+ // Called when component is added to GameObject
45
+ // Use for initialization, getting other components
46
+ }
47
+
48
+ public onEnabled(): void {
49
+ // Called when GameObject becomes enabled
50
+ // Use to resume behavior, start effects
51
+ }
52
+
53
+ public update(deltaTime: number): void {
54
+ // Called every frame while GameObject is enabled
55
+ // Use for per-frame logic, movement, checks
56
+ }
57
+
58
+ public lateUpdate(deltaTime: number): void {
59
+ // Called after all update() calls
60
+ // Use for camera following, UI updates
61
+ }
62
+
63
+ public onDisabled(): void {
64
+ // Called when GameObject becomes disabled
65
+ // Use to pause behavior, hide effects
66
+ }
67
+
68
+ protected onCleanup(): void {
69
+ // Called when component is removed or GameObject disposed
70
+ // Use for cleanup, removing listeners, disposing resources
71
+ }
72
+ }
73
+ ```
74
+
75
+ ## Common Use Cases
76
+
77
+ ### Accessing GameObject Properties
78
+
79
+ ```typescript
80
+ class MoveForward extends Component {
81
+ private speed: number = 5
82
+
83
+ public update(deltaTime: number): void {
84
+ // Access GameObject's transform directly
85
+ this.gameObject.position.z += this.speed * deltaTime
86
+ }
87
+ }
88
+ ```
89
+
90
+ ### Getting Other Components
91
+
92
+ ```typescript
93
+ class HealthDisplay extends Component {
94
+ private meshRenderer?: MeshRenderer
95
+
96
+ protected onCreate(): void {
97
+ // Get component from same GameObject
98
+ this.meshRenderer = this.getComponent(MeshRenderer)
99
+
100
+ if (!this.meshRenderer) {
101
+ console.warn("No MeshRenderer found!")
102
+ }
103
+ }
104
+
105
+ public showDamage(): void {
106
+ if (this.meshRenderer) {
107
+ // Flash red or hide temporarily
108
+ this.meshRenderer.setVisible(false)
109
+ }
110
+ }
111
+ }
112
+ ```
113
+
114
+ ### Enable/Disable Behavior
115
+
116
+ ```typescript
117
+ class EnemyAI extends Component {
118
+ private isActive: boolean = false
119
+
120
+ public onEnabled(): void {
121
+ this.isActive = true
122
+ console.log("AI activated")
123
+ }
124
+
125
+ public onDisabled(): void {
126
+ this.isActive = false
127
+ console.log("AI deactivated")
128
+ }
129
+
130
+ public update(deltaTime: number): void {
131
+ if (this.isActive) {
132
+ // AI logic only runs when enabled
133
+ this.findTarget()
134
+ this.moveTowardsTarget(deltaTime)
135
+ }
136
+ }
137
+ }
138
+ ```
139
+
140
+ ### Accessing Scene and Game
141
+
142
+ ```typescript
143
+ class SpawnManager extends Component {
144
+ public spawnEnemy(): void {
145
+ // Access the scene directly
146
+ const scene = this.scene
147
+
148
+ // Create new GameObjects
149
+ const enemy = new GameObject("Enemy")
150
+ enemy.position.set(10, 0, 10)
151
+ }
152
+ }
153
+ ```
154
+
155
+ ## API Overview
156
+
157
+ ### Properties
158
+
159
+ - `gameObject: GameObject` - The GameObject this component is attached to (read-only)
160
+ - `scene: THREE.Scene` - The Three.js scene (convenience accessor)
161
+
162
+ ### Methods
163
+
164
+ - `getGameObject(): GameObject` - Get the attached GameObject
165
+ - `getComponent<T>(type): T | undefined` - Get another component from same GameObject
166
+ - `isAttached(): boolean` - Check if component is attached
167
+
168
+ ### Lifecycle Hooks (Override These)
169
+
170
+ - `onCreate(): void` - Initialization after attachment
171
+ - `onEnabled(): void` - Called when GameObject becomes enabled
172
+ - `onDisabled(): void` - Called when GameObject becomes disabled
173
+ - `update(deltaTime: number): void` - Per-frame update
174
+ - `lateUpdate(deltaTime: number): void` - Per-frame update after all update() calls
175
+ - `onCleanup(): void` - Cleanup before removal
176
+
177
+ ## Patterns & Best Practices
178
+
179
+ ### Cache Component References
180
+
181
+ ```typescript
182
+ // Good - Cache in onCreate
183
+ class Follower extends Component {
184
+ private target?: Transform
185
+
186
+ protected onCreate(): void {
187
+ const targetObj = GameObject.find("Target")
188
+ this.target = targetObj // Cache reference
189
+ }
190
+
191
+ public update(deltaTime: number): void {
192
+ if (this.target) {
193
+ // Use cached reference - fast!
194
+ }
195
+ }
196
+ }
197
+
198
+ // Bad - Search every frame
199
+ class Follower extends Component {
200
+ public update(deltaTime: number): void {
201
+ const target = GameObject.find("Target") // Slow!
202
+ if (target) {
203
+ // ...
204
+ }
205
+ }
206
+ }
207
+ ```
208
+
209
+ ### Use update() vs lateUpdate()
210
+
211
+ ```typescript
212
+ // Player movement in update()
213
+ class PlayerController extends Component {
214
+ public update(deltaTime: number): void {
215
+ // Move player based on input
216
+ this.movePlayer(deltaTime)
217
+ }
218
+ }
219
+
220
+ // Camera follows player in lateUpdate()
221
+ class CameraFollow extends Component {
222
+ public lateUpdate(deltaTime: number): void {
223
+ // Follow player AFTER they've moved
224
+ this.followTarget(deltaTime)
225
+ }
226
+ }
227
+ ```
228
+
229
+ ### Proper Event Listener Cleanup
230
+
231
+ ```typescript
232
+ class ClickHandler extends Component {
233
+ private boundOnClick: (e: MouseEvent) => void
234
+
235
+ protected onCreate(): void {
236
+ this.boundOnClick = this.onClick.bind(this)
237
+ document.addEventListener("click", this.boundOnClick)
238
+ }
239
+
240
+ protected onCleanup(): void {
241
+ // Always remove listeners!
242
+ document.removeEventListener("click", this.boundOnClick)
243
+ }
244
+
245
+ private onClick(e: MouseEvent): void {
246
+ console.log("Clicked!")
247
+ }
248
+ }
249
+ ```
250
+
251
+ ### Optional Update Methods
252
+
253
+ ```typescript
254
+ // Only implement update if you need it
255
+ class StaticData extends Component {
256
+ // No update() - won't be registered for updates (more efficient)
257
+
258
+ protected onCreate(): void {
259
+ // One-time initialization only
260
+ }
261
+ }
262
+ ```
263
+
264
+ ## Anti-Patterns
265
+
266
+ ### Don't Access GameObject Before Attached
267
+
268
+ ```typescript
269
+ // Bad - gameObject doesn't exist yet!
270
+ class BadComponent extends Component {
271
+ private position = this.gameObject.position // ERROR!
272
+
273
+ constructor() {
274
+ super()
275
+ }
276
+ }
277
+
278
+ // Good - Access in onCreate or later
279
+ class GoodComponent extends Component {
280
+ private position?: THREE.Vector3
281
+
282
+ protected onCreate(): void {
283
+ this.position = this.gameObject.position.clone()
284
+ }
285
+ }
286
+ ```
287
+
288
+ ### Don't Store References to Disposed GameObjects
289
+
290
+ ```typescript
291
+ // Bad - holding reference to disposed object
292
+ class Spawner extends Component {
293
+ private spawnedObjects: GameObject[] = []
294
+
295
+ public spawnObject(): void {
296
+ const obj = new GameObject("Spawned")
297
+ this.spawnedObjects.push(obj)
298
+ // If obj.dispose() is called elsewhere, array has dead reference
299
+ }
300
+ }
301
+
302
+ // Good - Remove from array when disposing
303
+ class Spawner extends Component {
304
+ private spawnedObjects: GameObject[] = []
305
+
306
+ public despawnObject(obj: GameObject): void {
307
+ obj.dispose()
308
+ const index = this.spawnedObjects.indexOf(obj)
309
+ if (index > -1) {
310
+ this.spawnedObjects.splice(index, 1)
311
+ }
312
+ }
313
+ }
314
+ ```
315
+
316
+ ## Related Systems
317
+
318
+ - [GameObject](GameObject.md) - Entity class that components attach to
319
+ - [VenusGame](VenusGame.md) - Main game class with lifecycle
320
+ - [ComponentUpdater](../../src/engine/core/ComponentUpdater.ts) - Internal update manager
321
+