@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 +6 -6
- package/docs/core/Component.md +321 -321
- package/docs/core/GameObject.md +204 -204
- package/docs/core/VenusGame.md +316 -316
- package/docs/patterns/ComponentCommunication.md +337 -337
- package/docs/patterns/CreatingGameObjects.md +290 -290
- package/docs/patterns/MeshColliders.md +338 -338
- package/docs/patterns/MeshLoading.md +316 -316
- package/docs/physics/Colliders.md +249 -249
- package/docs/physics/PhysicsSystem.md +151 -151
- package/docs/physics/RigidBodyComponent.md +201 -201
- package/docs/rendering/AssetManager.md +308 -308
- package/docs/rendering/InstancedRenderer.md +286 -286
- package/docs/rendering/MeshRenderer.md +286 -286
- package/docs/rendering/SkeletalRenderer.md +308 -308
- package/docs/systems/AnimationSystem.md +75 -75
- package/docs/systems/AudioSystem.md +79 -79
- package/docs/systems/InputManager.md +101 -101
- package/docs/systems/LightingSystem.md +101 -101
- package/docs/systems/ParticleSystem.md +44 -44
- package/docs/systems/PrefabSystem.md +60 -60
- package/docs/systems/StowKitSystem.md +77 -77
- package/docs/systems/TweenSystem.md +132 -132
- package/docs/systems/UISystem.md +73 -73
- package/package.json +1 -1
- package/scripts/postinstall.mjs +63 -51
|
@@ -1,290 +1,290 @@
|
|
|
1
|
-
# Creating GameObjects
|
|
2
|
-
|
|
3
|
-
Best practices for GameObject instantiation, hierarchy, and lifecycle management.
|
|
4
|
-
|
|
5
|
-
## Basic Creation
|
|
6
|
-
|
|
7
|
-
```typescript
|
|
8
|
-
// Always name your GameObjects
|
|
9
|
-
const player = new GameObject("Player")
|
|
10
|
-
player.position.set(0, 1, 0)
|
|
11
|
-
|
|
12
|
-
// Add components for behavior
|
|
13
|
-
player.addComponent(new PlayerController())
|
|
14
|
-
player.addComponent(new RigidBodyComponentThree({
|
|
15
|
-
type: RigidBodyType.DYNAMIC
|
|
16
|
-
}))
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
## Hierarchy Patterns
|
|
20
|
-
|
|
21
|
-
### Parent-Child Relationships
|
|
22
|
-
|
|
23
|
-
```typescript
|
|
24
|
-
// Create parent
|
|
25
|
-
const vehicle = new GameObject("Vehicle")
|
|
26
|
-
|
|
27
|
-
// Create children
|
|
28
|
-
const body = new GameObject("Body")
|
|
29
|
-
const wheel1 = new GameObject("Wheel1")
|
|
30
|
-
const wheel2 = new GameObject("Wheel2")
|
|
31
|
-
|
|
32
|
-
// Build hierarchy
|
|
33
|
-
vehicle.add(body)
|
|
34
|
-
vehicle.add(wheel1)
|
|
35
|
-
vehicle.add(wheel2)
|
|
36
|
-
|
|
37
|
-
// Position children relative to parent
|
|
38
|
-
wheel1.position.set(-1, 0, 0)
|
|
39
|
-
wheel2.position.set(1, 0, 0)
|
|
40
|
-
|
|
41
|
-
// Moving parent moves all children
|
|
42
|
-
vehicle.position.x += 5
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
### Separation of Concerns
|
|
46
|
-
|
|
47
|
-
```typescript
|
|
48
|
-
// Separate visual from logic
|
|
49
|
-
const character = new GameObject("Character")
|
|
50
|
-
|
|
51
|
-
// Logic/physics on parent
|
|
52
|
-
character.addComponent(new CharacterController())
|
|
53
|
-
character.addComponent(new RigidBodyComponentThree({
|
|
54
|
-
type: RigidBodyType.DYNAMIC,
|
|
55
|
-
shape: ColliderShape.CAPSULE
|
|
56
|
-
}))
|
|
57
|
-
|
|
58
|
-
// Visual as child (can be offset)
|
|
59
|
-
const visual = new GameObject("Visual")
|
|
60
|
-
visual.addComponent(new SkeletalRenderer("character.fbx"))
|
|
61
|
-
character.add(visual)
|
|
62
|
-
visual.position.y = -1 // Offset visual down
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
## Component Organization
|
|
66
|
-
|
|
67
|
-
### Single Responsibility
|
|
68
|
-
|
|
69
|
-
```typescript
|
|
70
|
-
// Good - Each component does one thing
|
|
71
|
-
const enemy = new GameObject("Enemy")
|
|
72
|
-
enemy.addComponent(new EnemyAI()) // Behavior
|
|
73
|
-
enemy.addComponent(new HealthComponent()) // Health tracking
|
|
74
|
-
enemy.addComponent(new MeshRenderer("enemy_mesh")) // Visual
|
|
75
|
-
|
|
76
|
-
// Bad - One component doing everything
|
|
77
|
-
enemy.addComponent(new EnemyEverything()) // AI + health + rendering
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
### Component Dependencies
|
|
81
|
-
|
|
82
|
-
```typescript
|
|
83
|
-
class WeaponComponent extends Component {
|
|
84
|
-
private meshRenderer?: MeshRenderer
|
|
85
|
-
|
|
86
|
-
protected onCreate(): void {
|
|
87
|
-
// Get required components
|
|
88
|
-
this.meshRenderer = this.getComponent(MeshRenderer)
|
|
89
|
-
|
|
90
|
-
if (!this.meshRenderer) {
|
|
91
|
-
console.error("WeaponComponent requires MeshRenderer!")
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
## Lifecycle Management
|
|
98
|
-
|
|
99
|
-
### Proper Initialization
|
|
100
|
-
|
|
101
|
-
```typescript
|
|
102
|
-
class SpawnManager extends Component {
|
|
103
|
-
private enemies: GameObject[] = []
|
|
104
|
-
|
|
105
|
-
public spawnEnemy(): void {
|
|
106
|
-
// Create
|
|
107
|
-
const enemy = new GameObject("Enemy")
|
|
108
|
-
|
|
109
|
-
// Configure
|
|
110
|
-
enemy.position.set(10, 0, 10)
|
|
111
|
-
enemy.addComponent(new EnemyAI())
|
|
112
|
-
|
|
113
|
-
// Track
|
|
114
|
-
this.enemies.push(enemy)
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
protected onCleanup(): void {
|
|
118
|
-
// Clean up all spawned objects
|
|
119
|
-
for (const enemy of this.enemies) {
|
|
120
|
-
enemy.dispose()
|
|
121
|
-
}
|
|
122
|
-
this.enemies = []
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
### Pooling Pattern
|
|
128
|
-
|
|
129
|
-
```typescript
|
|
130
|
-
class ObjectPool {
|
|
131
|
-
private pool: GameObject[] = []
|
|
132
|
-
private active: GameObject[] = []
|
|
133
|
-
|
|
134
|
-
public get(): GameObject {
|
|
135
|
-
// Reuse from pool
|
|
136
|
-
let obj = this.pool.pop()
|
|
137
|
-
|
|
138
|
-
if (!obj) {
|
|
139
|
-
// Create new if pool empty
|
|
140
|
-
obj = new GameObject("Pooled")
|
|
141
|
-
obj.addComponent(new PooledComponent())
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
obj.setEnabled(true)
|
|
145
|
-
this.active.push(obj)
|
|
146
|
-
return obj
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
public release(obj: GameObject): void {
|
|
150
|
-
// Return to pool
|
|
151
|
-
obj.setEnabled(false)
|
|
152
|
-
const index = this.active.indexOf(obj)
|
|
153
|
-
if (index > -1) {
|
|
154
|
-
this.active.splice(index, 1)
|
|
155
|
-
this.pool.push(obj)
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
public cleanup(): void {
|
|
160
|
-
// Dispose all
|
|
161
|
-
for (const obj of [...this.pool, ...this.active]) {
|
|
162
|
-
obj.dispose()
|
|
163
|
-
}
|
|
164
|
-
this.pool = []
|
|
165
|
-
this.active = []
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
## Common Patterns
|
|
171
|
-
|
|
172
|
-
### Factory Pattern
|
|
173
|
-
|
|
174
|
-
```typescript
|
|
175
|
-
class EnemyFactory {
|
|
176
|
-
static createBasicEnemy(): GameObject {
|
|
177
|
-
const enemy = new GameObject("BasicEnemy")
|
|
178
|
-
enemy.addComponent(new EnemyAI())
|
|
179
|
-
enemy.addComponent(new HealthComponent(100))
|
|
180
|
-
|
|
181
|
-
const visual = new GameObject("Visual")
|
|
182
|
-
visual.addComponent(new MeshRenderer("enemy_basic"))
|
|
183
|
-
enemy.add(visual)
|
|
184
|
-
|
|
185
|
-
return enemy
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
static createBossEnemy(): GameObject {
|
|
189
|
-
const boss = new GameObject("BossEnemy")
|
|
190
|
-
boss.addComponent(new BossAI())
|
|
191
|
-
boss.addComponent(new HealthComponent(1000))
|
|
192
|
-
|
|
193
|
-
const visual = new GameObject("Visual")
|
|
194
|
-
visual.addComponent(new SkeletalRenderer("boss.fbx"))
|
|
195
|
-
boss.add(visual)
|
|
196
|
-
|
|
197
|
-
return boss
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
### Builder Pattern
|
|
203
|
-
|
|
204
|
-
```typescript
|
|
205
|
-
class CharacterBuilder {
|
|
206
|
-
private character: GameObject
|
|
207
|
-
|
|
208
|
-
constructor(name: string) {
|
|
209
|
-
this.character = new GameObject(name)
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
withMesh(meshName: string): this {
|
|
213
|
-
const visual = new GameObject("Visual")
|
|
214
|
-
visual.addComponent(new MeshRenderer(meshName))
|
|
215
|
-
this.character.add(visual)
|
|
216
|
-
return this
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
withPhysics(options: RigidBodyOptions): this {
|
|
220
|
-
this.character.addComponent(new RigidBodyComponentThree(options))
|
|
221
|
-
return this
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
withAI(): this {
|
|
225
|
-
this.character.addComponent(new EnemyAI())
|
|
226
|
-
return this
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
build(): GameObject {
|
|
230
|
-
return this.character
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// Use it
|
|
235
|
-
const enemy = new CharacterBuilder("Enemy")
|
|
236
|
-
.withMesh("enemy_mesh")
|
|
237
|
-
.withPhysics({ type: RigidBodyType.DYNAMIC })
|
|
238
|
-
.withAI()
|
|
239
|
-
.build()
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
## Anti-Patterns
|
|
243
|
-
|
|
244
|
-
### Don't Create in Update
|
|
245
|
-
|
|
246
|
-
```typescript
|
|
247
|
-
// BAD - Creates objects every frame
|
|
248
|
-
public update(deltaTime: number): void {
|
|
249
|
-
const temp = new GameObject("Temp") // Memory leak!
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// GOOD - Create once, reuse
|
|
253
|
-
private tempObject: GameObject
|
|
254
|
-
protected onCreate(): void {
|
|
255
|
-
this.tempObject = new GameObject("Temp")
|
|
256
|
-
}
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
### Don't Forget Disposal
|
|
260
|
-
|
|
261
|
-
```typescript
|
|
262
|
-
// BAD - Memory leak
|
|
263
|
-
spawnProjectile() {
|
|
264
|
-
const bullet = new GameObject("Bullet")
|
|
265
|
-
// Never disposed!
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// GOOD - Track and clean up
|
|
269
|
-
private bullets: GameObject[] = []
|
|
270
|
-
|
|
271
|
-
spawnProjectile() {
|
|
272
|
-
const bullet = new GameObject("Bullet")
|
|
273
|
-
this.bullets.push(bullet)
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
cleanupBullet(bullet: GameObject) {
|
|
277
|
-
bullet.dispose()
|
|
278
|
-
const index = this.bullets.indexOf(bullet)
|
|
279
|
-
if (index > -1) {
|
|
280
|
-
this.bullets.splice(index, 1)
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
## Related Patterns
|
|
286
|
-
|
|
287
|
-
- [Mesh Loading](MeshLoading.md) - Loading meshes correctly
|
|
288
|
-
- [Component Communication](ComponentCommunication.md) - Inter-component patterns
|
|
289
|
-
- [GameObject](../core/GameObject.md) - GameObject documentation
|
|
290
|
-
|
|
1
|
+
# Creating GameObjects
|
|
2
|
+
|
|
3
|
+
Best practices for GameObject instantiation, hierarchy, and lifecycle management.
|
|
4
|
+
|
|
5
|
+
## Basic Creation
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
// Always name your GameObjects
|
|
9
|
+
const player = new GameObject("Player")
|
|
10
|
+
player.position.set(0, 1, 0)
|
|
11
|
+
|
|
12
|
+
// Add components for behavior
|
|
13
|
+
player.addComponent(new PlayerController())
|
|
14
|
+
player.addComponent(new RigidBodyComponentThree({
|
|
15
|
+
type: RigidBodyType.DYNAMIC
|
|
16
|
+
}))
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Hierarchy Patterns
|
|
20
|
+
|
|
21
|
+
### Parent-Child Relationships
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
// Create parent
|
|
25
|
+
const vehicle = new GameObject("Vehicle")
|
|
26
|
+
|
|
27
|
+
// Create children
|
|
28
|
+
const body = new GameObject("Body")
|
|
29
|
+
const wheel1 = new GameObject("Wheel1")
|
|
30
|
+
const wheel2 = new GameObject("Wheel2")
|
|
31
|
+
|
|
32
|
+
// Build hierarchy
|
|
33
|
+
vehicle.add(body)
|
|
34
|
+
vehicle.add(wheel1)
|
|
35
|
+
vehicle.add(wheel2)
|
|
36
|
+
|
|
37
|
+
// Position children relative to parent
|
|
38
|
+
wheel1.position.set(-1, 0, 0)
|
|
39
|
+
wheel2.position.set(1, 0, 0)
|
|
40
|
+
|
|
41
|
+
// Moving parent moves all children
|
|
42
|
+
vehicle.position.x += 5
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Separation of Concerns
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
// Separate visual from logic
|
|
49
|
+
const character = new GameObject("Character")
|
|
50
|
+
|
|
51
|
+
// Logic/physics on parent
|
|
52
|
+
character.addComponent(new CharacterController())
|
|
53
|
+
character.addComponent(new RigidBodyComponentThree({
|
|
54
|
+
type: RigidBodyType.DYNAMIC,
|
|
55
|
+
shape: ColliderShape.CAPSULE
|
|
56
|
+
}))
|
|
57
|
+
|
|
58
|
+
// Visual as child (can be offset)
|
|
59
|
+
const visual = new GameObject("Visual")
|
|
60
|
+
visual.addComponent(new SkeletalRenderer("character.fbx"))
|
|
61
|
+
character.add(visual)
|
|
62
|
+
visual.position.y = -1 // Offset visual down
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Component Organization
|
|
66
|
+
|
|
67
|
+
### Single Responsibility
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
// Good - Each component does one thing
|
|
71
|
+
const enemy = new GameObject("Enemy")
|
|
72
|
+
enemy.addComponent(new EnemyAI()) // Behavior
|
|
73
|
+
enemy.addComponent(new HealthComponent()) // Health tracking
|
|
74
|
+
enemy.addComponent(new MeshRenderer("enemy_mesh")) // Visual
|
|
75
|
+
|
|
76
|
+
// Bad - One component doing everything
|
|
77
|
+
enemy.addComponent(new EnemyEverything()) // AI + health + rendering
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Component Dependencies
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
class WeaponComponent extends Component {
|
|
84
|
+
private meshRenderer?: MeshRenderer
|
|
85
|
+
|
|
86
|
+
protected onCreate(): void {
|
|
87
|
+
// Get required components
|
|
88
|
+
this.meshRenderer = this.getComponent(MeshRenderer)
|
|
89
|
+
|
|
90
|
+
if (!this.meshRenderer) {
|
|
91
|
+
console.error("WeaponComponent requires MeshRenderer!")
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Lifecycle Management
|
|
98
|
+
|
|
99
|
+
### Proper Initialization
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
class SpawnManager extends Component {
|
|
103
|
+
private enemies: GameObject[] = []
|
|
104
|
+
|
|
105
|
+
public spawnEnemy(): void {
|
|
106
|
+
// Create
|
|
107
|
+
const enemy = new GameObject("Enemy")
|
|
108
|
+
|
|
109
|
+
// Configure
|
|
110
|
+
enemy.position.set(10, 0, 10)
|
|
111
|
+
enemy.addComponent(new EnemyAI())
|
|
112
|
+
|
|
113
|
+
// Track
|
|
114
|
+
this.enemies.push(enemy)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
protected onCleanup(): void {
|
|
118
|
+
// Clean up all spawned objects
|
|
119
|
+
for (const enemy of this.enemies) {
|
|
120
|
+
enemy.dispose()
|
|
121
|
+
}
|
|
122
|
+
this.enemies = []
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Pooling Pattern
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
class ObjectPool {
|
|
131
|
+
private pool: GameObject[] = []
|
|
132
|
+
private active: GameObject[] = []
|
|
133
|
+
|
|
134
|
+
public get(): GameObject {
|
|
135
|
+
// Reuse from pool
|
|
136
|
+
let obj = this.pool.pop()
|
|
137
|
+
|
|
138
|
+
if (!obj) {
|
|
139
|
+
// Create new if pool empty
|
|
140
|
+
obj = new GameObject("Pooled")
|
|
141
|
+
obj.addComponent(new PooledComponent())
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
obj.setEnabled(true)
|
|
145
|
+
this.active.push(obj)
|
|
146
|
+
return obj
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
public release(obj: GameObject): void {
|
|
150
|
+
// Return to pool
|
|
151
|
+
obj.setEnabled(false)
|
|
152
|
+
const index = this.active.indexOf(obj)
|
|
153
|
+
if (index > -1) {
|
|
154
|
+
this.active.splice(index, 1)
|
|
155
|
+
this.pool.push(obj)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
public cleanup(): void {
|
|
160
|
+
// Dispose all
|
|
161
|
+
for (const obj of [...this.pool, ...this.active]) {
|
|
162
|
+
obj.dispose()
|
|
163
|
+
}
|
|
164
|
+
this.pool = []
|
|
165
|
+
this.active = []
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Common Patterns
|
|
171
|
+
|
|
172
|
+
### Factory Pattern
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
class EnemyFactory {
|
|
176
|
+
static createBasicEnemy(): GameObject {
|
|
177
|
+
const enemy = new GameObject("BasicEnemy")
|
|
178
|
+
enemy.addComponent(new EnemyAI())
|
|
179
|
+
enemy.addComponent(new HealthComponent(100))
|
|
180
|
+
|
|
181
|
+
const visual = new GameObject("Visual")
|
|
182
|
+
visual.addComponent(new MeshRenderer("enemy_basic"))
|
|
183
|
+
enemy.add(visual)
|
|
184
|
+
|
|
185
|
+
return enemy
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
static createBossEnemy(): GameObject {
|
|
189
|
+
const boss = new GameObject("BossEnemy")
|
|
190
|
+
boss.addComponent(new BossAI())
|
|
191
|
+
boss.addComponent(new HealthComponent(1000))
|
|
192
|
+
|
|
193
|
+
const visual = new GameObject("Visual")
|
|
194
|
+
visual.addComponent(new SkeletalRenderer("boss.fbx"))
|
|
195
|
+
boss.add(visual)
|
|
196
|
+
|
|
197
|
+
return boss
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Builder Pattern
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
class CharacterBuilder {
|
|
206
|
+
private character: GameObject
|
|
207
|
+
|
|
208
|
+
constructor(name: string) {
|
|
209
|
+
this.character = new GameObject(name)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
withMesh(meshName: string): this {
|
|
213
|
+
const visual = new GameObject("Visual")
|
|
214
|
+
visual.addComponent(new MeshRenderer(meshName))
|
|
215
|
+
this.character.add(visual)
|
|
216
|
+
return this
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
withPhysics(options: RigidBodyOptions): this {
|
|
220
|
+
this.character.addComponent(new RigidBodyComponentThree(options))
|
|
221
|
+
return this
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
withAI(): this {
|
|
225
|
+
this.character.addComponent(new EnemyAI())
|
|
226
|
+
return this
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
build(): GameObject {
|
|
230
|
+
return this.character
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Use it
|
|
235
|
+
const enemy = new CharacterBuilder("Enemy")
|
|
236
|
+
.withMesh("enemy_mesh")
|
|
237
|
+
.withPhysics({ type: RigidBodyType.DYNAMIC })
|
|
238
|
+
.withAI()
|
|
239
|
+
.build()
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Anti-Patterns
|
|
243
|
+
|
|
244
|
+
### Don't Create in Update
|
|
245
|
+
|
|
246
|
+
```typescript
|
|
247
|
+
// BAD - Creates objects every frame
|
|
248
|
+
public update(deltaTime: number): void {
|
|
249
|
+
const temp = new GameObject("Temp") // Memory leak!
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// GOOD - Create once, reuse
|
|
253
|
+
private tempObject: GameObject
|
|
254
|
+
protected onCreate(): void {
|
|
255
|
+
this.tempObject = new GameObject("Temp")
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Don't Forget Disposal
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
// BAD - Memory leak
|
|
263
|
+
spawnProjectile() {
|
|
264
|
+
const bullet = new GameObject("Bullet")
|
|
265
|
+
// Never disposed!
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// GOOD - Track and clean up
|
|
269
|
+
private bullets: GameObject[] = []
|
|
270
|
+
|
|
271
|
+
spawnProjectile() {
|
|
272
|
+
const bullet = new GameObject("Bullet")
|
|
273
|
+
this.bullets.push(bullet)
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
cleanupBullet(bullet: GameObject) {
|
|
277
|
+
bullet.dispose()
|
|
278
|
+
const index = this.bullets.indexOf(bullet)
|
|
279
|
+
if (index > -1) {
|
|
280
|
+
this.bullets.splice(index, 1)
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
## Related Patterns
|
|
286
|
+
|
|
287
|
+
- [Mesh Loading](MeshLoading.md) - Loading meshes correctly
|
|
288
|
+
- [Component Communication](ComponentCommunication.md) - Inter-component patterns
|
|
289
|
+
- [GameObject](../core/GameObject.md) - GameObject documentation
|
|
290
|
+
|