@jarrodmedrano/claude-skills 1.0.2 → 1.0.4
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/.claude/skills/bevy/SKILL.md +406 -0
- package/.claude/skills/bevy/references/bevy_specific_tips.md +385 -0
- package/.claude/skills/bevy/references/common_pitfalls.md +217 -0
- package/.claude/skills/bevy/references/ecs_patterns.md +277 -0
- package/.claude/skills/bevy/references/project_structure.md +116 -0
- package/.claude/skills/bevy/references/ui_development.md +147 -0
- package/.claude/skills/domain-driven-design/SKILL.md +459 -0
- package/.claude/skills/domain-driven-design/references/ddd_foundations_and_patterns.md +664 -0
- package/.claude/skills/domain-driven-design/references/rich_hickey_principles.md +406 -0
- package/.claude/skills/domain-driven-design/references/visualization_examples.md +790 -0
- package/.claude/skills/domain-driven-design/references/wlaschin_patterns.md +639 -0
- package/.claude/skills/game-design-theory/SKILL.md +102 -0
- package/.claude/skills/game-design-theory/design-principles.md +308 -0
- package/.claude/skills/game-design-theory/gameplay-elements.md +213 -0
- package/.claude/skills/game-design-theory/player-psychology.md +175 -0
- package/.claude/skills/game-design-theory/playtesting.md +321 -0
- package/.claude/skills/game-design-theory/storytelling.md +219 -0
- package/.claude/skills/game-feel/SKILL.md +305 -0
- package/.claude/skills/game-feel/references/adsr-tuning.md +271 -0
- package/.claude/skills/game-feel/references/classic-profiles.md +279 -0
- package/.claude/skills/game-feel/references/perception-thresholds.md +160 -0
- package/.claude/skills/game-feel/references/polish-effects.md +246 -0
- package/.claude/skills/game-feel/references/simulation-recipes.md +306 -0
- package/.claude/skills/game-feel/references/six-metrics.md +239 -0
- package/.claude/skills/godot/SKILL.md +728 -0
- package/.claude/skills/godot/assets/templates/attribute_template.gd +109 -0
- package/.claude/skills/godot/assets/templates/component_template.gd +76 -0
- package/.claude/skills/godot/assets/templates/interaction_template.gd +108 -0
- package/.claude/skills/godot/assets/templates/item_resource.tres +11 -0
- package/.claude/skills/godot/assets/templates/spell_resource.tres +20 -0
- package/.claude/skills/godot/references/architecture-patterns.md +608 -0
- package/.claude/skills/godot/references/common-pitfalls.md +518 -0
- package/.claude/skills/godot/references/file-formats.md +491 -0
- package/.claude/skills/godot/references/godot4-physics-api.md +302 -0
- package/.claude/skills/godot/scripts/validate_tres.py +145 -0
- package/.claude/skills/godot/scripts/validate_tscn.py +170 -0
- package/.claude/skills/level-design/SKILL.md +249 -0
- package/.claude/skills/level-design/anticipatory-play.md +223 -0
- package/.claude/skills/level-design/hiding-linearity.md +181 -0
- package/.claude/skills/level-design/indie-practices.md +286 -0
- package/.claude/skills/level-design/open-world-planning.md +294 -0
- package/.claude/skills/level-design/play-personas.md +240 -0
- package/.claude/skills/level-design/procedural-handmade.md +271 -0
- package/.claude/skills/level-design/themed-environments.md +264 -0
- package/.claude/skills/react-three-fiber/SKILL.md +2055 -0
- package/.claude/skills/react-three-fiber/scripts/build-scene.ts +171 -0
- package/package.json +3 -1
- package/scripts/install.js +16 -1
- package/templates/github-actions/README.md +36 -0
- /package/.claude/{commands/design-review → agents}/design-review-agent.md +0 -0
- /package/.claude/{commands/code-review → agents}/pragmatic-code-review-subagent.md +0 -0
- /package/{.claude/commands/code-review → templates/github-actions}/claude-code-review-custom.yml +0 -0
- /package/{.claude/commands/code-review → templates/github-actions}/claude-code-review.yml +0 -0
- /package/{.claude/commands/security-review → templates/github-actions}/security.yml +0 -0
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
# Bevy-Specific Development Tips
|
|
2
|
+
|
|
3
|
+
## Bevy 0.17 Specific Changes
|
|
4
|
+
|
|
5
|
+
**Important:** Bevy 0.17 introduced several breaking API changes. If you encounter compilation errors related to materials, events, or colors, refer to this section.
|
|
6
|
+
|
|
7
|
+
### Material Component Wrapper
|
|
8
|
+
|
|
9
|
+
In Bevy 0.17, material handles are wrapped in `MeshMaterial3d<T>`:
|
|
10
|
+
|
|
11
|
+
```rust
|
|
12
|
+
// ❌ Bevy 0.15/0.16 - This will fail in 0.17
|
|
13
|
+
Query<&Handle<StandardMaterial>>
|
|
14
|
+
|
|
15
|
+
// ✅ Bevy 0.17 - Use the wrapper component
|
|
16
|
+
Query<&MeshMaterial3d<StandardMaterial>>
|
|
17
|
+
|
|
18
|
+
// Access the inner handle with .0
|
|
19
|
+
fn update_materials(
|
|
20
|
+
query: Query<&MeshMaterial3d<StandardMaterial>>,
|
|
21
|
+
mut materials: ResMut<Assets<StandardMaterial>>,
|
|
22
|
+
) {
|
|
23
|
+
for material_3d in query.iter() {
|
|
24
|
+
if let Some(material) = materials.get_mut(&material_3d.0) {
|
|
25
|
+
material.emissive = LinearRgba::RED;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Error symptoms:**
|
|
32
|
+
- `Handle<StandardMaterial> is not a Component`
|
|
33
|
+
- Query trait bounds not satisfied
|
|
34
|
+
|
|
35
|
+
**Solution:** Always use `MeshMaterial3d<T>` wrapper when querying material components.
|
|
36
|
+
|
|
37
|
+
### Observer Pattern (Replaces Events)
|
|
38
|
+
|
|
39
|
+
Bevy 0.17 introduces observers as a replacement for the event system:
|
|
40
|
+
|
|
41
|
+
```rust
|
|
42
|
+
// ❌ Old event pattern (Bevy 0.15/0.16)
|
|
43
|
+
#[derive(Event)]
|
|
44
|
+
struct SpellCastEvent { spell_name: String }
|
|
45
|
+
|
|
46
|
+
app.add_event::<SpellCastEvent>()
|
|
47
|
+
.add_systems(Update, handle_spell_cast);
|
|
48
|
+
|
|
49
|
+
fn handle_spell_cast(mut events: EventReader<SpellCastEvent>) {
|
|
50
|
+
for event in events.read() {
|
|
51
|
+
info!("Cast: {}", event.spell_name);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
fn cast_spell(mut events: EventWriter<SpellCastEvent>) {
|
|
56
|
+
events.send(SpellCastEvent { spell_name: "Fireball".into() });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ✅ Bevy 0.17 observer pattern
|
|
60
|
+
#[derive(Event, Clone)] // Must derive Clone!
|
|
61
|
+
struct SpellCastEvent { spell_name: String }
|
|
62
|
+
|
|
63
|
+
app.add_observer(handle_spell_cast); // Observer, not system
|
|
64
|
+
|
|
65
|
+
fn handle_spell_cast(
|
|
66
|
+
trigger: Trigger<SpellCastEvent>, // Trigger parameter
|
|
67
|
+
// ... other system params
|
|
68
|
+
) {
|
|
69
|
+
let event = trigger.event();
|
|
70
|
+
info!("Cast: {}", event.spell_name);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
fn cast_spell(mut commands: Commands) {
|
|
74
|
+
commands.trigger(SpellCastEvent { spell_name: "Fireball".into() });
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Key differences:**
|
|
79
|
+
- Events **must derive `Clone`** in addition to `Event`
|
|
80
|
+
- Use `add_observer(handler)` instead of `add_event()` + `add_systems()`
|
|
81
|
+
- Handler takes `Trigger<T>` as first parameter, use `.event()` to access data
|
|
82
|
+
- Trigger with `commands.trigger()` instead of `EventWriter::send()`
|
|
83
|
+
- Observers are not systems - they're called directly when triggered
|
|
84
|
+
|
|
85
|
+
**Error symptoms:**
|
|
86
|
+
- `MyEvent is not a Message`
|
|
87
|
+
- `method 'send' not found for MessageWriter`
|
|
88
|
+
- `method 'read' not found`
|
|
89
|
+
|
|
90
|
+
**Solution:** Migrate to the observer pattern as shown above.
|
|
91
|
+
|
|
92
|
+
### Color Operations
|
|
93
|
+
|
|
94
|
+
Direct color arithmetic operations aren't supported in Bevy 0.17:
|
|
95
|
+
|
|
96
|
+
```rust
|
|
97
|
+
// ❌ Doesn't compile
|
|
98
|
+
let emissive = color * 0.5;
|
|
99
|
+
let darker = color - 0.2;
|
|
100
|
+
|
|
101
|
+
// ✅ Extract components manually
|
|
102
|
+
let emissive = Color::srgb(
|
|
103
|
+
color.to_srgba().red * 0.5,
|
|
104
|
+
color.to_srgba().green * 0.5,
|
|
105
|
+
color.to_srgba().blue * 0.5,
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
// Or use LinearRgba for math operations
|
|
109
|
+
let linear = color.to_linear();
|
|
110
|
+
let dimmed = LinearRgba::rgb(
|
|
111
|
+
linear.red * 0.5,
|
|
112
|
+
linear.green * 0.5,
|
|
113
|
+
linear.blue * 0.5,
|
|
114
|
+
);
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**Error symptoms:**
|
|
118
|
+
- `cannot multiply Color by {float}`
|
|
119
|
+
- `no implementation for Color * f32`
|
|
120
|
+
|
|
121
|
+
**Solution:** Convert to component form or use `LinearRgba` for mathematical operations.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Using Bevy Registry Examples
|
|
126
|
+
|
|
127
|
+
**The registry examples are your bible.** Bevy ships with extensive examples that demonstrate best practices and patterns.
|
|
128
|
+
|
|
129
|
+
**Location:**
|
|
130
|
+
```bash
|
|
131
|
+
~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bevy-0.17.1/examples
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**When to consult registry examples:**
|
|
135
|
+
- Before implementing a new feature type
|
|
136
|
+
- When unsure about API usage
|
|
137
|
+
- To see working patterns for complex systems
|
|
138
|
+
- To understand how plugins should be structured
|
|
139
|
+
- For reference implementations of common game mechanics
|
|
140
|
+
|
|
141
|
+
**How to use them:**
|
|
142
|
+
1. Browse the examples directory for relevant use cases
|
|
143
|
+
2. Study the complete implementation (not just snippets)
|
|
144
|
+
3. Note how they structure components, systems, and plugins
|
|
145
|
+
4. Adapt patterns to your specific needs
|
|
146
|
+
|
|
147
|
+
There are MANY examples covering:
|
|
148
|
+
- 2D/3D rendering
|
|
149
|
+
- Animation
|
|
150
|
+
- Audio
|
|
151
|
+
- Input handling
|
|
152
|
+
- UI systems
|
|
153
|
+
- Physics
|
|
154
|
+
- Scenes and assets
|
|
155
|
+
- And much more
|
|
156
|
+
|
|
157
|
+
**Always refer to examples before diving into implementation.**
|
|
158
|
+
|
|
159
|
+
## Plugin Structure
|
|
160
|
+
|
|
161
|
+
Break your app into discrete modules using plugins whenever possible.
|
|
162
|
+
|
|
163
|
+
**Why use plugins:**
|
|
164
|
+
- Organizes code by feature/domain
|
|
165
|
+
- Makes systems reusable
|
|
166
|
+
- Improves code discoverability
|
|
167
|
+
- Enables modular development
|
|
168
|
+
- Follows Bevy best practices
|
|
169
|
+
|
|
170
|
+
**Plugin pattern:**
|
|
171
|
+
```rust
|
|
172
|
+
use bevy::prelude::*;
|
|
173
|
+
|
|
174
|
+
pub struct CombatPlugin;
|
|
175
|
+
|
|
176
|
+
impl Plugin for CombatPlugin {
|
|
177
|
+
fn build(&self, app: &mut App) {
|
|
178
|
+
app
|
|
179
|
+
.add_event::<DamageEvent>()
|
|
180
|
+
.add_systems(Startup, setup_combat)
|
|
181
|
+
.add_systems(Update, (
|
|
182
|
+
process_damage,
|
|
183
|
+
check_death,
|
|
184
|
+
update_health_bars,
|
|
185
|
+
));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// In main.rs
|
|
190
|
+
fn main() {
|
|
191
|
+
App::new()
|
|
192
|
+
.add_plugins(DefaultPlugins)
|
|
193
|
+
.add_plugins(CombatPlugin)
|
|
194
|
+
.add_plugins(MovementPlugin)
|
|
195
|
+
.add_plugins(UIPlugin)
|
|
196
|
+
.run();
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
**References:**
|
|
201
|
+
- Plugin guide: https://bevy.org/learn/quick-start/getting-started/plugins/
|
|
202
|
+
- System sets: https://bevy-cheatbook.github.io/programming/system-sets.html
|
|
203
|
+
|
|
204
|
+
## Build Performance and Optimization
|
|
205
|
+
|
|
206
|
+
### Dynamic Linking
|
|
207
|
+
|
|
208
|
+
**Always use dynamic linking during development:**
|
|
209
|
+
```bash
|
|
210
|
+
cargo build --features bevy/dynamic_linking
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
**Why:**
|
|
214
|
+
- 2-3x faster compile times
|
|
215
|
+
- Critical for iteration speed
|
|
216
|
+
- Only affects development builds
|
|
217
|
+
|
|
218
|
+
**Setup in `.cargo/config.toml`:**
|
|
219
|
+
```toml
|
|
220
|
+
[target.x86_64-unknown-linux-gnu]
|
|
221
|
+
linker = "clang"
|
|
222
|
+
rustflags = ["-C", "link-arg=-fuse-ld=lld"]
|
|
223
|
+
|
|
224
|
+
[target.x86_64-apple-darwin]
|
|
225
|
+
rustflags = ["-C", "link-arg=-fuse-ld=/usr/local/opt/llvm/bin/ld64.lld"]
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
**Optimization levels** - See: https://bevy.org/learn/quick-start/getting-started/setup/
|
|
229
|
+
|
|
230
|
+
For faster dev builds, add to `Cargo.toml`:
|
|
231
|
+
```toml
|
|
232
|
+
[profile.dev]
|
|
233
|
+
opt-level = 1
|
|
234
|
+
|
|
235
|
+
[profile.dev.package."*"]
|
|
236
|
+
opt-level = 3
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Build Management
|
|
240
|
+
|
|
241
|
+
**CRITICAL: Do not delete target binaries freely!**
|
|
242
|
+
|
|
243
|
+
Bevy takes **minutes** to rebuild from scratch. Be mindful of:
|
|
244
|
+
|
|
245
|
+
1. **Target directory management:**
|
|
246
|
+
- Avoid `cargo clean` unless absolutely necessary
|
|
247
|
+
- Incremental builds are your friend
|
|
248
|
+
- Each clean rebuild costs valuable development time
|
|
249
|
+
|
|
250
|
+
2. **Version and dependency management:**
|
|
251
|
+
- Bevy is under active development
|
|
252
|
+
- Be mindful of the version you are using
|
|
253
|
+
- Dependencies can get tangled easily
|
|
254
|
+
- Version mismatches can force complete rebuilds
|
|
255
|
+
- Stick to one Bevy version per project when possible
|
|
256
|
+
|
|
257
|
+
3. **Crate dependencies:**
|
|
258
|
+
- Adding/removing dependencies triggers rebuilds
|
|
259
|
+
- Changing feature flags triggers rebuilds
|
|
260
|
+
- Plan dependency changes carefully
|
|
261
|
+
- Batch dependency updates when possible
|
|
262
|
+
|
|
263
|
+
**Best practices:**
|
|
264
|
+
- Use `cargo check` for quick validation (no binary)
|
|
265
|
+
- Use `cargo build --features bevy/dynamic_linking` for testing
|
|
266
|
+
- Only use `cargo clean` when dealing with corrupted build artifacts
|
|
267
|
+
- Keep a stable `Cargo.lock` for consistent builds
|
|
268
|
+
|
|
269
|
+
## Domain-Driven Design for ECS
|
|
270
|
+
|
|
271
|
+
**Pure ECS structure demands careful data modeling.**
|
|
272
|
+
|
|
273
|
+
### Think Before You Code
|
|
274
|
+
|
|
275
|
+
Because it's hard to search a massive list of systems in one file, you must:
|
|
276
|
+
|
|
277
|
+
1. **Design the data model first:**
|
|
278
|
+
- What entities exist in your domain?
|
|
279
|
+
- What components do they need?
|
|
280
|
+
- What behaviors (systems) operate on them?
|
|
281
|
+
- How do components relate?
|
|
282
|
+
|
|
283
|
+
2. **Refer to docs and existing code:**
|
|
284
|
+
- Check Bevy examples for similar patterns
|
|
285
|
+
- Review the official docs for component design
|
|
286
|
+
- Look at existing project code for consistency
|
|
287
|
+
- Understand the domain before implementing
|
|
288
|
+
|
|
289
|
+
3. **Use bounded contexts:**
|
|
290
|
+
- Group related components together
|
|
291
|
+
- Create plugins per domain area
|
|
292
|
+
- Keep systems focused on single responsibilities
|
|
293
|
+
- Avoid cross-domain coupling
|
|
294
|
+
|
|
295
|
+
### Example Domain Modeling Process
|
|
296
|
+
|
|
297
|
+
**Bad approach:**
|
|
298
|
+
```
|
|
299
|
+
❌ Start coding immediately
|
|
300
|
+
❌ Add systems to one giant file
|
|
301
|
+
❌ Discover missing components mid-implementation
|
|
302
|
+
❌ Hard to navigate, hard to maintain
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
**Good approach:**
|
|
306
|
+
```
|
|
307
|
+
✅ Define the domain (e.g., "Combat System")
|
|
308
|
+
✅ List entities (Player, Enemy, Projectile)
|
|
309
|
+
✅ List components (Health, Damage, Armor)
|
|
310
|
+
✅ List events (DamageEvent, DeathEvent)
|
|
311
|
+
✅ List systems (process_damage, check_death, spawn_projectile)
|
|
312
|
+
✅ Check examples for similar implementations
|
|
313
|
+
✅ Create CombatPlugin
|
|
314
|
+
✅ Implement incrementally
|
|
315
|
+
✅ Test at each step
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### File Organization for Discoverability
|
|
319
|
+
|
|
320
|
+
```
|
|
321
|
+
src/
|
|
322
|
+
├── main.rs # App setup only
|
|
323
|
+
├── plugins/
|
|
324
|
+
│ ├── mod.rs
|
|
325
|
+
│ ├── combat.rs # CombatPlugin
|
|
326
|
+
│ ├── movement.rs # MovementPlugin
|
|
327
|
+
│ └── inventory.rs # InventoryPlugin
|
|
328
|
+
├── components/
|
|
329
|
+
│ ├── mod.rs
|
|
330
|
+
│ ├── combat.rs # Health, Armor, Damage
|
|
331
|
+
│ ├── movement.rs # Velocity, Speed
|
|
332
|
+
│ └── inventory.rs # Inventory, Item
|
|
333
|
+
└── events.rs # All game events
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
**Benefits:**
|
|
337
|
+
- Easy to find related code
|
|
338
|
+
- Clear domain boundaries
|
|
339
|
+
- Plugin-based modularity
|
|
340
|
+
- Searchable by feature/domain
|
|
341
|
+
|
|
342
|
+
## Version Management
|
|
343
|
+
|
|
344
|
+
**Bevy is under active development.**
|
|
345
|
+
|
|
346
|
+
1. **Check your Bevy version:**
|
|
347
|
+
```bash
|
|
348
|
+
cargo tree | grep bevy
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
2. **Stay on one version per project:**
|
|
352
|
+
- Avoid mixing Bevy versions
|
|
353
|
+
- Update all Bevy crates together
|
|
354
|
+
- Test thoroughly after version updates
|
|
355
|
+
|
|
356
|
+
3. **API changes between versions:**
|
|
357
|
+
- Read the migration guide when updating
|
|
358
|
+
- Bevy's API evolves rapidly
|
|
359
|
+
- Code from older versions may not work
|
|
360
|
+
- Examples are version-specific
|
|
361
|
+
|
|
362
|
+
4. **When seeking help:**
|
|
363
|
+
- Always mention your Bevy version
|
|
364
|
+
- Check if examples match your version
|
|
365
|
+
- Look for version-specific documentation
|
|
366
|
+
|
|
367
|
+
## Summary Checklist
|
|
368
|
+
|
|
369
|
+
**Before implementing:**
|
|
370
|
+
- [ ] Check registry examples for similar features
|
|
371
|
+
- [ ] Design the data model (entities, components, events, systems)
|
|
372
|
+
- [ ] Create a plugin for the feature domain
|
|
373
|
+
- [ ] Review existing code for patterns
|
|
374
|
+
|
|
375
|
+
**During development:**
|
|
376
|
+
- [ ] Use `cargo build --features bevy/dynamic_linking`
|
|
377
|
+
- [ ] Avoid `cargo clean` unless necessary
|
|
378
|
+
- [ ] Test incrementally
|
|
379
|
+
- [ ] Keep systems focused and organized
|
|
380
|
+
|
|
381
|
+
**After implementation:**
|
|
382
|
+
- [ ] Verify the feature works
|
|
383
|
+
- [ ] Check for code organization issues
|
|
384
|
+
- [ ] Document domain-specific patterns
|
|
385
|
+
- [ ] Update plugin structure if needed
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# Common Bevy Pitfalls Reference
|
|
2
|
+
|
|
3
|
+
## 1. Using Old Event System in Bevy 0.17
|
|
4
|
+
|
|
5
|
+
**❌ Problem:**
|
|
6
|
+
```rust
|
|
7
|
+
// Bevy 0.15/0.16 event system doesn't work in 0.17
|
|
8
|
+
#[derive(Event)]
|
|
9
|
+
struct MyEvent { data: String }
|
|
10
|
+
|
|
11
|
+
app.add_event::<MyEvent>()
|
|
12
|
+
.add_systems(Update, handle_event);
|
|
13
|
+
|
|
14
|
+
fn handle_event(mut events: EventReader<MyEvent>) { /* ... */ }
|
|
15
|
+
fn trigger(mut events: EventWriter<MyEvent>) { /* ... */ }
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**Symptoms:**
|
|
19
|
+
- Compilation error: `MyEvent is not a Message`
|
|
20
|
+
- `method 'send' not found for MessageWriter`
|
|
21
|
+
- `method 'read' not found for MessageReader`
|
|
22
|
+
|
|
23
|
+
**✅ Solution:**
|
|
24
|
+
Migrate to the observer pattern:
|
|
25
|
+
```rust
|
|
26
|
+
// Bevy 0.17 observer pattern
|
|
27
|
+
#[derive(Event, Clone)] // Must derive Clone!
|
|
28
|
+
struct MyEvent { data: String }
|
|
29
|
+
|
|
30
|
+
app.add_observer(handle_event); // Use observer, not system
|
|
31
|
+
|
|
32
|
+
fn handle_event(
|
|
33
|
+
trigger: Trigger<MyEvent>, // Trigger parameter
|
|
34
|
+
// ... other params
|
|
35
|
+
) {
|
|
36
|
+
let event = trigger.event();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
fn trigger_event(mut commands: Commands) {
|
|
40
|
+
commands.trigger(MyEvent { data: "test".into() });
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
See `references/bevy_specific_tips.md` for complete migration guide.
|
|
45
|
+
|
|
46
|
+
## 2. Querying Material Handles in Bevy 0.17
|
|
47
|
+
|
|
48
|
+
**❌ Problem:**
|
|
49
|
+
```rust
|
|
50
|
+
// Bevy 0.15/0.16 pattern doesn't work in 0.17
|
|
51
|
+
Query<&Handle<StandardMaterial>>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Symptoms:**
|
|
55
|
+
- `Handle<StandardMaterial> is not a Component`
|
|
56
|
+
- Query trait bounds not satisfied
|
|
57
|
+
|
|
58
|
+
**✅ Solution:**
|
|
59
|
+
Use the `MeshMaterial3d` wrapper:
|
|
60
|
+
```rust
|
|
61
|
+
Query<&MeshMaterial3d<StandardMaterial>>
|
|
62
|
+
|
|
63
|
+
// Access handle with .0
|
|
64
|
+
for material_3d in query.iter() {
|
|
65
|
+
if let Some(material) = materials.get_mut(&material_3d.0) {
|
|
66
|
+
material.emissive = color;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## 3. Forgetting to Register Systems
|
|
72
|
+
|
|
73
|
+
**❌ Problem:**
|
|
74
|
+
```rust
|
|
75
|
+
// Created system but forgot to add to app
|
|
76
|
+
pub fn my_new_system() { /* ... */ }
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**✅ Solution:**
|
|
80
|
+
Always add to `main.rs`:
|
|
81
|
+
```rust
|
|
82
|
+
.add_systems(Update, my_new_system)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## 2. Borrowing Conflicts
|
|
86
|
+
|
|
87
|
+
**❌ Problem:**
|
|
88
|
+
```rust
|
|
89
|
+
// Can't have multiple mutable borrows
|
|
90
|
+
mut query1: Query<&mut Transform>,
|
|
91
|
+
mut query2: Query<&mut Transform>, // Error!
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**✅ Solution:**
|
|
95
|
+
```rust
|
|
96
|
+
// Use get_many_mut for specific entities
|
|
97
|
+
mut query: Query<&mut Transform>,
|
|
98
|
+
|
|
99
|
+
if let Ok([mut a, mut b]) = query.get_many_mut([entity_a, entity_b]) {
|
|
100
|
+
// Can mutate both
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## 3. Infinite Loops with Events
|
|
105
|
+
|
|
106
|
+
**❌ Problem:**
|
|
107
|
+
```rust
|
|
108
|
+
// System reads and writes same event type
|
|
109
|
+
fn system(
|
|
110
|
+
mut events: EventWriter<MyEvent>,
|
|
111
|
+
reader: EventReader<MyEvent>,
|
|
112
|
+
) {
|
|
113
|
+
for event in reader.read() {
|
|
114
|
+
events.send(MyEvent); // Infinite loop!
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**✅ Solution:**
|
|
120
|
+
Use different event types or add termination condition.
|
|
121
|
+
|
|
122
|
+
## 4. Not Using Changed<T>
|
|
123
|
+
|
|
124
|
+
**❌ Problem:**
|
|
125
|
+
```rust
|
|
126
|
+
// Runs every frame for every entity
|
|
127
|
+
fn system(query: Query<&BigFive>) {
|
|
128
|
+
for traits in query.iter() {
|
|
129
|
+
// Expensive calculation every frame
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**✅ Solution:**
|
|
135
|
+
```rust
|
|
136
|
+
// Only runs when BigFive changes
|
|
137
|
+
fn system(query: Query<&BigFive, Changed<BigFive>>) {
|
|
138
|
+
for traits in query.iter() {
|
|
139
|
+
// Only when needed
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## 5. Entity Queries After Despawn
|
|
145
|
+
|
|
146
|
+
**❌ Problem:**
|
|
147
|
+
```rust
|
|
148
|
+
commands.entity(entity).despawn();
|
|
149
|
+
// Later in same system
|
|
150
|
+
let component = query.get(entity).unwrap(); // Crash!
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
**✅ Solution:**
|
|
154
|
+
Commands apply at end of stage. Use `Ok()` pattern:
|
|
155
|
+
```rust
|
|
156
|
+
if let Ok(component) = query.get(entity) {
|
|
157
|
+
// Safe
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## 6. Material/Asset Handle Confusion
|
|
162
|
+
|
|
163
|
+
**❌ Problem:**
|
|
164
|
+
```rust
|
|
165
|
+
// Created material but didn't store handle
|
|
166
|
+
materials.add(StandardMaterial { .. }); // Handle dropped!
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**✅ Solution:**
|
|
170
|
+
```rust
|
|
171
|
+
let material_handle = materials.add(StandardMaterial { .. });
|
|
172
|
+
commands.spawn((
|
|
173
|
+
MeshMaterial3d(material_handle),
|
|
174
|
+
// ...
|
|
175
|
+
));
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## 7. System Ordering Issues
|
|
179
|
+
|
|
180
|
+
**❌ Problem:**
|
|
181
|
+
```rust
|
|
182
|
+
// UI updates before state changes
|
|
183
|
+
.add_systems(Update, (
|
|
184
|
+
update_ui,
|
|
185
|
+
process_input, // Wrong order!
|
|
186
|
+
))
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**✅ Solution:**
|
|
190
|
+
Order systems by dependencies:
|
|
191
|
+
```rust
|
|
192
|
+
.add_systems(Update, (
|
|
193
|
+
// Input processing
|
|
194
|
+
process_input,
|
|
195
|
+
|
|
196
|
+
// State changes
|
|
197
|
+
update_state,
|
|
198
|
+
|
|
199
|
+
// UI updates (reads state)
|
|
200
|
+
update_ui,
|
|
201
|
+
))
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## 8. Not Filtering Queries Early
|
|
205
|
+
|
|
206
|
+
**❌ Problem:**
|
|
207
|
+
```rust
|
|
208
|
+
// Filter in loop (inefficient)
|
|
209
|
+
Query<(&A, Option<&B>, Option<&C>)>
|
|
210
|
+
// Then check in loop
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
**✅ Solution:**
|
|
214
|
+
```rust
|
|
215
|
+
// Filter in query (efficient)
|
|
216
|
+
Query<&A, (With<B>, Without<C>)>
|
|
217
|
+
```
|