✨ Scaffold bevy plugin with bevy-0.19 and bevy-upgrade skills
Add plugin skeleton with versioned skill directories, TODO SKILL.md scaffolds with goals for skill-creator, and copy over bevy-skill-research reports from spacecraft into docs/workpad/research/.
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
# Bevy Skill Research: Hierarchy & Relationships
|
||||
|
||||
> **Bevy version**: 0.18, with 0.19 notes
|
||||
> **Date**: 2026-07-07
|
||||
|
||||
## ChildOf Replaces Parent (Bevy 0.16+)
|
||||
|
||||
- `Parent` renamed to **`ChildOf(pub Entity)`** — "entities with ChildOf are children, not parents"
|
||||
- `Deref` removed; use **`child_of.parent()`** to get the parent Entity
|
||||
- `ChildOf` implements `Relationship`; `Children` implements `RelationshipTarget` — they auto-sync via hooks
|
||||
- Adding `ChildOf(parent)` to child auto-inserts `Children` on parent; removing it auto-updates parent
|
||||
- Despawning a parent **auto-despawns all descendants** (was `despawn_recursive`, now just `despawn()`)
|
||||
|
||||
## Spawning Children
|
||||
|
||||
```rust
|
||||
// Insert ChildOf directly
|
||||
commands.spawn(ChildOf(parent));
|
||||
|
||||
// Inline child on parent
|
||||
commands.entity(parent).with_child((SpriteBundle::default(), ChildOf(parent)));
|
||||
|
||||
// Multiple children via closure
|
||||
commands.spawn_empty().with_children(|spawner: &mut ChildSpawnerCommands| {
|
||||
spawner.spawn(MyComponent(255));
|
||||
let parent = spawner.target_entity(); // was parent_entity()
|
||||
});
|
||||
|
||||
// Add existing entities as children
|
||||
commands.entity(parent).add_child(child);
|
||||
commands.entity(parent).add_children(&[child1, child2]);
|
||||
commands.entity(parent).insert_children(index, &[child1, child2]);
|
||||
```
|
||||
|
||||
## Querying Hierarchy
|
||||
|
||||
- **`Children`** derefs to `[Entity]`, implements `IntoIterator`
|
||||
- Correct iteration: **`for child in &children`** or **`children.into_iter()`**
|
||||
- Wrong: ~~`children.iter().copied()`~~ — unnecessary, `IntoIterator` yields `Entity` directly
|
||||
- Methods: `len()`, `is_empty()`, `contains()`, `first()`, `last()`, `sort_by()`
|
||||
- Query extensions (0.16+): `query.related::<ChildOf>(entity)`, `query.relationship_sources::<Children>(entity)`
|
||||
|
||||
## Detaching vs Despawning (0.18 Renames)
|
||||
|
||||
| Old name (≤0.17) | New name (0.18+) | Effect |
|
||||
|-------------------|------------------|--------|
|
||||
| `remove_children` | **`detach_children`** | Detach only, do NOT despawn |
|
||||
| `clear_children` | **`detach_all_children`** | Detach all, do NOT despawn |
|
||||
| `remove_child` | **`detach_child`** | Detach one, do NOT despawn |
|
||||
|
||||
To actually despawn children: `despawn_related::<Children>()`
|
||||
|
||||
## Transform Propagation
|
||||
|
||||
- `GlobalTransform` is auto-propagated through `ChildOf` hierarchy
|
||||
- Child `Transform` is relative to parent; `GlobalTransform` is computed world-space
|
||||
- Propagation runs in **`PostUpdate`** — reading `GlobalTransform` in `Update` gives stale (last-frame) values
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
| Mistake | Correct |
|
||||
|---------|---------|
|
||||
| `Parent` component | `ChildOf(entity)` |
|
||||
| `children.iter().copied()` | `children.into_iter()` or `for child in &children` |
|
||||
| `*child_of` (deref) | `child_of.parent()` |
|
||||
| `despawn_recursive()` | `despawn()` (auto-recursive now) |
|
||||
| `parent_entity()` in spawner | `target_entity()` |
|
||||
| `clear_children()` | `detach_all_children()` (0.18+) |
|
||||
| `remove_children()` | `detach_children()` (0.18+) |
|
||||
| Reading GlobalTransform in Update | Read in PostUpdate, or accept one-frame lag |
|
||||
Reference in New Issue
Block a user