@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,308 +1,308 @@
|
|
|
1
|
-
# SkeletalRenderer
|
|
2
|
-
|
|
3
|
-
SkeletalRenderer loads and displays animated character meshes with skeletal animations. It properly clones bone structures and works with the animation system.
|
|
4
|
-
|
|
5
|
-
## Quick Start
|
|
6
|
-
|
|
7
|
-
```typescript
|
|
8
|
-
import { GameObject, SkeletalRenderer } from "@series-ai/rundot-3d-engine"
|
|
9
|
-
import { AssetManager } from "@series-ai/rundot-3d-engine/assets"
|
|
10
|
-
|
|
11
|
-
// 1. Preload skeletal model
|
|
12
|
-
await AssetManager.preloadSkeletalModel("Character/character_main.fbx")
|
|
13
|
-
|
|
14
|
-
// 2. Create character with skeletal renderer
|
|
15
|
-
const character = new GameObject("Character")
|
|
16
|
-
const renderer = new SkeletalRenderer("Character/character_main.fbx")
|
|
17
|
-
character.addComponent(renderer)
|
|
18
|
-
|
|
19
|
-
// 3. Access skeletal model for animation
|
|
20
|
-
const skeletalModel = renderer.getSkeletalModel()
|
|
21
|
-
// Use with AnimationSystem
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
## Common Use Cases
|
|
25
|
-
|
|
26
|
-
### Basic Character Setup
|
|
27
|
-
|
|
28
|
-
```typescript
|
|
29
|
-
class Character extends Component {
|
|
30
|
-
private skeletalRenderer?: SkeletalRenderer
|
|
31
|
-
|
|
32
|
-
protected async onCreate(): Promise<void> {
|
|
33
|
-
// Must preload first
|
|
34
|
-
await AssetManager.preloadSkeletalModel("Character/character_main.fbx")
|
|
35
|
-
|
|
36
|
-
// Add renderer
|
|
37
|
-
this.skeletalRenderer = new SkeletalRenderer("Character/character_main.fbx")
|
|
38
|
-
this.gameObject.addComponent(this.skeletalRenderer)
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
### With Custom Material
|
|
44
|
-
|
|
45
|
-
```typescript
|
|
46
|
-
import * as THREE from "three"
|
|
47
|
-
|
|
48
|
-
// Create custom material
|
|
49
|
-
const characterMaterial = new THREE.MeshStandardMaterial({
|
|
50
|
-
color: 0xff0000,
|
|
51
|
-
metalness: 0.1,
|
|
52
|
-
roughness: 0.8
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
// Apply to skeletal renderer
|
|
56
|
-
const renderer = new SkeletalRenderer(
|
|
57
|
-
"Character/character_main.fbx",
|
|
58
|
-
characterMaterial
|
|
59
|
-
)
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
### Integration with Animation System
|
|
63
|
-
|
|
64
|
-
```typescript
|
|
65
|
-
import { AnimationControllerComponent } from "@series-ai/rundot-3d-engine/systems"
|
|
66
|
-
|
|
67
|
-
class AnimatedCharacter extends Component {
|
|
68
|
-
protected async onCreate(): Promise<void> {
|
|
69
|
-
// Preload model and animations
|
|
70
|
-
await AssetManager.preloadSkeletalModel("Character/character.fbx")
|
|
71
|
-
await AssetManager.preloadAssets([
|
|
72
|
-
"Animations/walk.fbx",
|
|
73
|
-
"Animations/run.fbx",
|
|
74
|
-
"Animations/idle.fbx"
|
|
75
|
-
])
|
|
76
|
-
|
|
77
|
-
// Add skeletal renderer
|
|
78
|
-
const renderer = new SkeletalRenderer("Character/character.fbx")
|
|
79
|
-
this.gameObject.addComponent(renderer)
|
|
80
|
-
|
|
81
|
-
// Add animation controller
|
|
82
|
-
const animController = new AnimationControllerComponent()
|
|
83
|
-
this.gameObject.addComponent(animController)
|
|
84
|
-
|
|
85
|
-
// Get skeletal model for animation setup
|
|
86
|
-
const model = renderer.getSkeletalModel()
|
|
87
|
-
if (model) {
|
|
88
|
-
// Animation controller will find and use this model
|
|
89
|
-
animController.setTarget(model)
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
### Accessing Model for Custom Animation
|
|
96
|
-
|
|
97
|
-
```typescript
|
|
98
|
-
class CustomAnimatedCharacter extends Component {
|
|
99
|
-
private mixer?: THREE.AnimationMixer
|
|
100
|
-
private renderer?: SkeletalRenderer
|
|
101
|
-
|
|
102
|
-
protected onCreate(): void {
|
|
103
|
-
this.renderer = new SkeletalRenderer("Character/character.fbx")
|
|
104
|
-
this.gameObject.addComponent(this.renderer)
|
|
105
|
-
|
|
106
|
-
const model = this.renderer.getSkeletalModel()
|
|
107
|
-
if (model) {
|
|
108
|
-
// Set up custom animation mixer
|
|
109
|
-
this.mixer = new THREE.AnimationMixer(model)
|
|
110
|
-
|
|
111
|
-
// Load and play animation
|
|
112
|
-
const clip = AssetManager.getAnimationClip("Animations/walk.fbx")
|
|
113
|
-
if (clip) {
|
|
114
|
-
const action = this.mixer.clipAction(clip)
|
|
115
|
-
action.play()
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
public update(deltaTime: number): void {
|
|
121
|
-
this.mixer?.update(deltaTime)
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
## API Overview
|
|
127
|
-
|
|
128
|
-
### Constructor
|
|
129
|
-
|
|
130
|
-
```typescript
|
|
131
|
-
new SkeletalRenderer(
|
|
132
|
-
assetPath: string,
|
|
133
|
-
material?: THREE.Material
|
|
134
|
-
)
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
### Methods
|
|
138
|
-
|
|
139
|
-
- `getGroup(): THREE.Group | null` - Get wrapper group (attached to GameObject)
|
|
140
|
-
- `getSkeletalModel(): THREE.Object3D | null` - Get skeletal model for animation
|
|
141
|
-
- `getAssetPath(): string` - Get asset path being rendered
|
|
142
|
-
- `getMaterial(): THREE.Material | null` - Get custom material if set
|
|
143
|
-
- `setMaterial(material: THREE.Material): void` - Change material at runtime
|
|
144
|
-
|
|
145
|
-
### Shadow Behavior
|
|
146
|
-
|
|
147
|
-
SkeletalRenderer always:
|
|
148
|
-
- Casts shadows (`castShadow: true`)
|
|
149
|
-
- Receives shadows (`receiveShadow: true`)
|
|
150
|
-
- These cannot be disabled (characters always interact with lighting)
|
|
151
|
-
|
|
152
|
-
## Patterns & Best Practices
|
|
153
|
-
|
|
154
|
-
### Always Preload Before Using
|
|
155
|
-
|
|
156
|
-
```typescript
|
|
157
|
-
// Good - Preload in game initialization
|
|
158
|
-
class MyGame extends VenusGame {
|
|
159
|
-
protected async onStart(): Promise<void> {
|
|
160
|
-
// Preload all character models
|
|
161
|
-
await AssetManager.preloadSkeletalModel("Character/player.fbx")
|
|
162
|
-
await AssetManager.preloadSkeletalModel("Character/enemy.fbx")
|
|
163
|
-
|
|
164
|
-
// Now safe to use
|
|
165
|
-
this.createCharacters()
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// Bad - Don't use without preloading
|
|
170
|
-
const renderer = new SkeletalRenderer("Character/player.fbx") // Error!
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
### Use with AnimationControllerComponent
|
|
174
|
-
|
|
175
|
-
```typescript
|
|
176
|
-
// Good - Let AnimationController manage animations
|
|
177
|
-
const renderer = new SkeletalRenderer("Character/char.fbx")
|
|
178
|
-
this.gameObject.addComponent(renderer)
|
|
179
|
-
|
|
180
|
-
const animController = new AnimationControllerComponent()
|
|
181
|
-
this.gameObject.addComponent(animController)
|
|
182
|
-
// AnimationController finds and uses the skeletal model
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
### Material Customization
|
|
186
|
-
|
|
187
|
-
```typescript
|
|
188
|
-
// Apply game-specific materials to characters
|
|
189
|
-
const toonMaterial = MaterialUtils.createToonMaterial(0xff5500)
|
|
190
|
-
|
|
191
|
-
const renderer = new SkeletalRenderer(
|
|
192
|
-
"Character/character.fbx",
|
|
193
|
-
toonMaterial
|
|
194
|
-
)
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
## Anti-Patterns
|
|
198
|
-
|
|
199
|
-
### Don't Use for Static Meshes
|
|
200
|
-
|
|
201
|
-
```typescript
|
|
202
|
-
// Bad - SkeletalRenderer is for animated characters only
|
|
203
|
-
const tree = new SkeletalRenderer("tree.fbx")
|
|
204
|
-
|
|
205
|
-
// Good - Use MeshRenderer for static objects
|
|
206
|
-
const renderer = new MeshRenderer("tree")
|
|
207
|
-
const treeObj = new GameObject("TreeMesh")
|
|
208
|
-
treeObj.addComponent(renderer)
|
|
209
|
-
this.gameObject.add(treeObj)
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
### Don't Forget Preloading
|
|
213
|
-
|
|
214
|
-
```typescript
|
|
215
|
-
// Bad - Throws error "No skeletal model found"
|
|
216
|
-
const renderer = new SkeletalRenderer("Character/player.fbx")
|
|
217
|
-
this.gameObject.addComponent(renderer) // ERROR!
|
|
218
|
-
|
|
219
|
-
// Good - Preload first
|
|
220
|
-
await AssetManager.preloadSkeletalModel("Character/player.fbx")
|
|
221
|
-
const renderer = new SkeletalRenderer("Character/player.fbx")
|
|
222
|
-
this.gameObject.addComponent(renderer) // Works!
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
### Don't Clone Manually
|
|
226
|
-
|
|
227
|
-
```typescript
|
|
228
|
-
// Bad - Manual cloning breaks bones
|
|
229
|
-
const model = AssetManager.getAssetGroup("Character/char.fbx")
|
|
230
|
-
const clone = model.clone() // Broken bones!
|
|
231
|
-
|
|
232
|
-
// Good - Use SkeletalRenderer (uses SkeletonCache)
|
|
233
|
-
const renderer = new SkeletalRenderer("Character/char.fbx")
|
|
234
|
-
// Properly clones with bone structure intact
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
## Character Setup Workflow
|
|
238
|
-
|
|
239
|
-
Typical workflow for setting up animated characters:
|
|
240
|
-
|
|
241
|
-
```typescript
|
|
242
|
-
class CharacterSetup {
|
|
243
|
-
static async preloadAssets(): Promise<void> {
|
|
244
|
-
// 1. Preload character model
|
|
245
|
-
await AssetManager.preloadSkeletalModel("Character/player.fbx")
|
|
246
|
-
|
|
247
|
-
// 2. Preload animations
|
|
248
|
-
await AssetManager.preloadAssets([
|
|
249
|
-
"Animations/idle.fbx",
|
|
250
|
-
"Animations/walk.fbx",
|
|
251
|
-
"Animations/run.fbx",
|
|
252
|
-
"Animations/jump.fbx"
|
|
253
|
-
])
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
static create(): GameObject {
|
|
257
|
-
// 3. Create GameObject
|
|
258
|
-
const character = new GameObject("Player")
|
|
259
|
-
|
|
260
|
-
// 4. Add SkeletalRenderer
|
|
261
|
-
const renderer = new SkeletalRenderer("Character/player.fbx")
|
|
262
|
-
character.addComponent(renderer)
|
|
263
|
-
|
|
264
|
-
// 5. Add AnimationController
|
|
265
|
-
const animController = new AnimationControllerComponent()
|
|
266
|
-
character.addComponent(animController)
|
|
267
|
-
|
|
268
|
-
// 6. Set up animation states
|
|
269
|
-
animController.addState("idle", "Animations/idle.fbx")
|
|
270
|
-
animController.addState("walk", "Animations/walk.fbx")
|
|
271
|
-
animController.setState("idle")
|
|
272
|
-
|
|
273
|
-
return character
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
```
|
|
277
|
-
|
|
278
|
-
## Performance Considerations
|
|
279
|
-
|
|
280
|
-
### Skeletal Animation Cost
|
|
281
|
-
|
|
282
|
-
Skeletal rendering is more expensive than static meshes:
|
|
283
|
-
- Bone calculations per frame
|
|
284
|
-
- Animation blending
|
|
285
|
-
- Skin weights processing
|
|
286
|
-
|
|
287
|
-
### Optimization Tips
|
|
288
|
-
|
|
289
|
-
```typescript
|
|
290
|
-
// Reduce character LOD distance
|
|
291
|
-
VenusGame.getAnimationCulling().setDistanceCullingEnabled(true)
|
|
292
|
-
VenusGame.getAnimationCulling().setMaxDistance(50) // Units
|
|
293
|
-
|
|
294
|
-
// Use frustum culling
|
|
295
|
-
VenusGame.setAnimationCullingCamera(camera, 1.2)
|
|
296
|
-
|
|
297
|
-
// Limit active characters
|
|
298
|
-
// Pool inactive characters instead of creating/destroying
|
|
299
|
-
```
|
|
300
|
-
|
|
301
|
-
## Related Systems
|
|
302
|
-
|
|
303
|
-
- [AssetManager](AssetManager.md) - Preloading skeletal models
|
|
304
|
-
- [AnimationControllerComponent](../systems/AnimationSystem.md) - Animation management
|
|
305
|
-
- [MeshRenderer](MeshRenderer.md) - For non-animated meshes
|
|
306
|
-
- [GameObject](../core/GameObject.md) - Entity class
|
|
307
|
-
- [Component](../core/Component.md) - Base component class
|
|
308
|
-
|
|
1
|
+
# SkeletalRenderer
|
|
2
|
+
|
|
3
|
+
SkeletalRenderer loads and displays animated character meshes with skeletal animations. It properly clones bone structures and works with the animation system.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import { GameObject, SkeletalRenderer } from "@series-ai/rundot-3d-engine"
|
|
9
|
+
import { AssetManager } from "@series-ai/rundot-3d-engine/assets"
|
|
10
|
+
|
|
11
|
+
// 1. Preload skeletal model
|
|
12
|
+
await AssetManager.preloadSkeletalModel("Character/character_main.fbx")
|
|
13
|
+
|
|
14
|
+
// 2. Create character with skeletal renderer
|
|
15
|
+
const character = new GameObject("Character")
|
|
16
|
+
const renderer = new SkeletalRenderer("Character/character_main.fbx")
|
|
17
|
+
character.addComponent(renderer)
|
|
18
|
+
|
|
19
|
+
// 3. Access skeletal model for animation
|
|
20
|
+
const skeletalModel = renderer.getSkeletalModel()
|
|
21
|
+
// Use with AnimationSystem
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Common Use Cases
|
|
25
|
+
|
|
26
|
+
### Basic Character Setup
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
class Character extends Component {
|
|
30
|
+
private skeletalRenderer?: SkeletalRenderer
|
|
31
|
+
|
|
32
|
+
protected async onCreate(): Promise<void> {
|
|
33
|
+
// Must preload first
|
|
34
|
+
await AssetManager.preloadSkeletalModel("Character/character_main.fbx")
|
|
35
|
+
|
|
36
|
+
// Add renderer
|
|
37
|
+
this.skeletalRenderer = new SkeletalRenderer("Character/character_main.fbx")
|
|
38
|
+
this.gameObject.addComponent(this.skeletalRenderer)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### With Custom Material
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
import * as THREE from "three"
|
|
47
|
+
|
|
48
|
+
// Create custom material
|
|
49
|
+
const characterMaterial = new THREE.MeshStandardMaterial({
|
|
50
|
+
color: 0xff0000,
|
|
51
|
+
metalness: 0.1,
|
|
52
|
+
roughness: 0.8
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
// Apply to skeletal renderer
|
|
56
|
+
const renderer = new SkeletalRenderer(
|
|
57
|
+
"Character/character_main.fbx",
|
|
58
|
+
characterMaterial
|
|
59
|
+
)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Integration with Animation System
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
import { AnimationControllerComponent } from "@series-ai/rundot-3d-engine/systems"
|
|
66
|
+
|
|
67
|
+
class AnimatedCharacter extends Component {
|
|
68
|
+
protected async onCreate(): Promise<void> {
|
|
69
|
+
// Preload model and animations
|
|
70
|
+
await AssetManager.preloadSkeletalModel("Character/character.fbx")
|
|
71
|
+
await AssetManager.preloadAssets([
|
|
72
|
+
"Animations/walk.fbx",
|
|
73
|
+
"Animations/run.fbx",
|
|
74
|
+
"Animations/idle.fbx"
|
|
75
|
+
])
|
|
76
|
+
|
|
77
|
+
// Add skeletal renderer
|
|
78
|
+
const renderer = new SkeletalRenderer("Character/character.fbx")
|
|
79
|
+
this.gameObject.addComponent(renderer)
|
|
80
|
+
|
|
81
|
+
// Add animation controller
|
|
82
|
+
const animController = new AnimationControllerComponent()
|
|
83
|
+
this.gameObject.addComponent(animController)
|
|
84
|
+
|
|
85
|
+
// Get skeletal model for animation setup
|
|
86
|
+
const model = renderer.getSkeletalModel()
|
|
87
|
+
if (model) {
|
|
88
|
+
// Animation controller will find and use this model
|
|
89
|
+
animController.setTarget(model)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Accessing Model for Custom Animation
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
class CustomAnimatedCharacter extends Component {
|
|
99
|
+
private mixer?: THREE.AnimationMixer
|
|
100
|
+
private renderer?: SkeletalRenderer
|
|
101
|
+
|
|
102
|
+
protected onCreate(): void {
|
|
103
|
+
this.renderer = new SkeletalRenderer("Character/character.fbx")
|
|
104
|
+
this.gameObject.addComponent(this.renderer)
|
|
105
|
+
|
|
106
|
+
const model = this.renderer.getSkeletalModel()
|
|
107
|
+
if (model) {
|
|
108
|
+
// Set up custom animation mixer
|
|
109
|
+
this.mixer = new THREE.AnimationMixer(model)
|
|
110
|
+
|
|
111
|
+
// Load and play animation
|
|
112
|
+
const clip = AssetManager.getAnimationClip("Animations/walk.fbx")
|
|
113
|
+
if (clip) {
|
|
114
|
+
const action = this.mixer.clipAction(clip)
|
|
115
|
+
action.play()
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
public update(deltaTime: number): void {
|
|
121
|
+
this.mixer?.update(deltaTime)
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## API Overview
|
|
127
|
+
|
|
128
|
+
### Constructor
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
new SkeletalRenderer(
|
|
132
|
+
assetPath: string,
|
|
133
|
+
material?: THREE.Material
|
|
134
|
+
)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Methods
|
|
138
|
+
|
|
139
|
+
- `getGroup(): THREE.Group | null` - Get wrapper group (attached to GameObject)
|
|
140
|
+
- `getSkeletalModel(): THREE.Object3D | null` - Get skeletal model for animation
|
|
141
|
+
- `getAssetPath(): string` - Get asset path being rendered
|
|
142
|
+
- `getMaterial(): THREE.Material | null` - Get custom material if set
|
|
143
|
+
- `setMaterial(material: THREE.Material): void` - Change material at runtime
|
|
144
|
+
|
|
145
|
+
### Shadow Behavior
|
|
146
|
+
|
|
147
|
+
SkeletalRenderer always:
|
|
148
|
+
- Casts shadows (`castShadow: true`)
|
|
149
|
+
- Receives shadows (`receiveShadow: true`)
|
|
150
|
+
- These cannot be disabled (characters always interact with lighting)
|
|
151
|
+
|
|
152
|
+
## Patterns & Best Practices
|
|
153
|
+
|
|
154
|
+
### Always Preload Before Using
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
// Good - Preload in game initialization
|
|
158
|
+
class MyGame extends VenusGame {
|
|
159
|
+
protected async onStart(): Promise<void> {
|
|
160
|
+
// Preload all character models
|
|
161
|
+
await AssetManager.preloadSkeletalModel("Character/player.fbx")
|
|
162
|
+
await AssetManager.preloadSkeletalModel("Character/enemy.fbx")
|
|
163
|
+
|
|
164
|
+
// Now safe to use
|
|
165
|
+
this.createCharacters()
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Bad - Don't use without preloading
|
|
170
|
+
const renderer = new SkeletalRenderer("Character/player.fbx") // Error!
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Use with AnimationControllerComponent
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
// Good - Let AnimationController manage animations
|
|
177
|
+
const renderer = new SkeletalRenderer("Character/char.fbx")
|
|
178
|
+
this.gameObject.addComponent(renderer)
|
|
179
|
+
|
|
180
|
+
const animController = new AnimationControllerComponent()
|
|
181
|
+
this.gameObject.addComponent(animController)
|
|
182
|
+
// AnimationController finds and uses the skeletal model
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Material Customization
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
// Apply game-specific materials to characters
|
|
189
|
+
const toonMaterial = MaterialUtils.createToonMaterial(0xff5500)
|
|
190
|
+
|
|
191
|
+
const renderer = new SkeletalRenderer(
|
|
192
|
+
"Character/character.fbx",
|
|
193
|
+
toonMaterial
|
|
194
|
+
)
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Anti-Patterns
|
|
198
|
+
|
|
199
|
+
### Don't Use for Static Meshes
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
// Bad - SkeletalRenderer is for animated characters only
|
|
203
|
+
const tree = new SkeletalRenderer("tree.fbx")
|
|
204
|
+
|
|
205
|
+
// Good - Use MeshRenderer for static objects
|
|
206
|
+
const renderer = new MeshRenderer("tree")
|
|
207
|
+
const treeObj = new GameObject("TreeMesh")
|
|
208
|
+
treeObj.addComponent(renderer)
|
|
209
|
+
this.gameObject.add(treeObj)
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Don't Forget Preloading
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
// Bad - Throws error "No skeletal model found"
|
|
216
|
+
const renderer = new SkeletalRenderer("Character/player.fbx")
|
|
217
|
+
this.gameObject.addComponent(renderer) // ERROR!
|
|
218
|
+
|
|
219
|
+
// Good - Preload first
|
|
220
|
+
await AssetManager.preloadSkeletalModel("Character/player.fbx")
|
|
221
|
+
const renderer = new SkeletalRenderer("Character/player.fbx")
|
|
222
|
+
this.gameObject.addComponent(renderer) // Works!
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Don't Clone Manually
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
// Bad - Manual cloning breaks bones
|
|
229
|
+
const model = AssetManager.getAssetGroup("Character/char.fbx")
|
|
230
|
+
const clone = model.clone() // Broken bones!
|
|
231
|
+
|
|
232
|
+
// Good - Use SkeletalRenderer (uses SkeletonCache)
|
|
233
|
+
const renderer = new SkeletalRenderer("Character/char.fbx")
|
|
234
|
+
// Properly clones with bone structure intact
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Character Setup Workflow
|
|
238
|
+
|
|
239
|
+
Typical workflow for setting up animated characters:
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
class CharacterSetup {
|
|
243
|
+
static async preloadAssets(): Promise<void> {
|
|
244
|
+
// 1. Preload character model
|
|
245
|
+
await AssetManager.preloadSkeletalModel("Character/player.fbx")
|
|
246
|
+
|
|
247
|
+
// 2. Preload animations
|
|
248
|
+
await AssetManager.preloadAssets([
|
|
249
|
+
"Animations/idle.fbx",
|
|
250
|
+
"Animations/walk.fbx",
|
|
251
|
+
"Animations/run.fbx",
|
|
252
|
+
"Animations/jump.fbx"
|
|
253
|
+
])
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
static create(): GameObject {
|
|
257
|
+
// 3. Create GameObject
|
|
258
|
+
const character = new GameObject("Player")
|
|
259
|
+
|
|
260
|
+
// 4. Add SkeletalRenderer
|
|
261
|
+
const renderer = new SkeletalRenderer("Character/player.fbx")
|
|
262
|
+
character.addComponent(renderer)
|
|
263
|
+
|
|
264
|
+
// 5. Add AnimationController
|
|
265
|
+
const animController = new AnimationControllerComponent()
|
|
266
|
+
character.addComponent(animController)
|
|
267
|
+
|
|
268
|
+
// 6. Set up animation states
|
|
269
|
+
animController.addState("idle", "Animations/idle.fbx")
|
|
270
|
+
animController.addState("walk", "Animations/walk.fbx")
|
|
271
|
+
animController.setState("idle")
|
|
272
|
+
|
|
273
|
+
return character
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## Performance Considerations
|
|
279
|
+
|
|
280
|
+
### Skeletal Animation Cost
|
|
281
|
+
|
|
282
|
+
Skeletal rendering is more expensive than static meshes:
|
|
283
|
+
- Bone calculations per frame
|
|
284
|
+
- Animation blending
|
|
285
|
+
- Skin weights processing
|
|
286
|
+
|
|
287
|
+
### Optimization Tips
|
|
288
|
+
|
|
289
|
+
```typescript
|
|
290
|
+
// Reduce character LOD distance
|
|
291
|
+
VenusGame.getAnimationCulling().setDistanceCullingEnabled(true)
|
|
292
|
+
VenusGame.getAnimationCulling().setMaxDistance(50) // Units
|
|
293
|
+
|
|
294
|
+
// Use frustum culling
|
|
295
|
+
VenusGame.setAnimationCullingCamera(camera, 1.2)
|
|
296
|
+
|
|
297
|
+
// Limit active characters
|
|
298
|
+
// Pool inactive characters instead of creating/destroying
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
## Related Systems
|
|
302
|
+
|
|
303
|
+
- [AssetManager](AssetManager.md) - Preloading skeletal models
|
|
304
|
+
- [AnimationControllerComponent](../systems/AnimationSystem.md) - Animation management
|
|
305
|
+
- [MeshRenderer](MeshRenderer.md) - For non-animated meshes
|
|
306
|
+
- [GameObject](../core/GameObject.md) - Entity class
|
|
307
|
+
- [Component](../core/Component.md) - Base component class
|
|
308
|
+
|