🤖 Flesh out bevy-0.19 and bevy-upgrade skills from research
Replace scaffold placeholders with full skill content: - bevy-0.19: ECS cheat sheet, critical traps table, architecture guide, and 7 reference files - bevy-upgrade: version migration workflow, transition references for 0.15→0.19, and 3 per-bump reference files
This commit is contained in:
@@ -1,33 +1,52 @@
|
||||
---
|
||||
name: bevy-upgrade
|
||||
description: "TODO: Use the skill-creator skill to write this. See scaffolding goals below."
|
||||
description: Migrate Bevy (Rust game engine) projects between versions, 0.15 through 0.19. Rename tables, semantic behavior shifts, and a per-bump migration workflow. Use when the task bumps the bevy version in Cargo.toml, fixes compile errors after an upgrade, or modernizes code using removed APIs (Parent, EventWriter/EventReader, despawn_recursive, derive(Bundle), StateScoped). For writing new code on the current version, defer to the bevy-0.19 skill.
|
||||
---
|
||||
|
||||
# Bevy Upgrade — Version Migration Guide
|
||||
# Bevy Upgrade
|
||||
|
||||
> **This skill is a scaffold.** Use `/skill-creator` to create the full skill content from the research material.
|
||||
Bevy ships breaking changes every minor release. Upgrades are tractable if run as a disciplined loop — one minor version at a time, mechanical renames before semantic fixes, behavior shifts verified by running the game, not just compiling it.
|
||||
|
||||
## Scaffolding Goals
|
||||
## Workflow
|
||||
|
||||
1. **Use the `skill-creator:skill-creator` skill** to create the full SKILL.md content
|
||||
2. Feed it the version-change annotations from research reports 01-06 in [`docs/workpad/research/bevy-skill-research/`](../../../../docs/workpad/research/bevy-skill-research/00-index.md)
|
||||
3. The skill should trigger when the user asks to upgrade/migrate Bevy versions, or when the `bevy` skill detects upgrade work
|
||||
4. Core content:
|
||||
- Breaking-change chain from 0.16 through 0.19+
|
||||
- Rename tables (old name -> new name, which version)
|
||||
- Semantic shifts (behavior changes, not just renames)
|
||||
- Step-by-step migration patterns for each version bump
|
||||
- Common upgrade pitfalls and their fixes
|
||||
5. Populate `references/` with per-version-transition files (e.g., `0.17-to-0.18.md`, `0.18-to-0.19.md`)
|
||||
6. The skill should be invocable both directly and from the `bevy` skill
|
||||
1. **Establish current and target versions.** Read `Cargo.toml` (`bevy` plus ecosystem crates: physics, UI, networking — each is pinned to a Bevy minor and must be bumped in lockstep).
|
||||
2. **Upgrade one minor version at a time.** Skipping versions compounds renames and makes errors unattributable. For each bump:
|
||||
1. Bump `bevy` and every Bevy-ecosystem crate to versions compatible with the target. If an ecosystem crate has no compatible release, stop and surface it — that blocks the bump.
|
||||
2. `cargo check`, then apply the **rename table** for the transition (see references) across all errors. These are mechanical.
|
||||
3. Fix remaining compile errors using the transition's **API shape changes** (signatures, moved modules, new required destructuring).
|
||||
4. Audit for the transition's **semantic shifts** — changes that compile clean but behave differently. Grep for the listed patterns; these do not announce themselves.
|
||||
5. `cargo check` clean → run the game / test suite and smoke-test the areas the semantic shifts touch.
|
||||
3. **Anything not covered by the references**: fetch the official migration guide at `https://bevy.org/learn/migration-guides/<from>-to-<to>/` and work through it — the references cover the high-frequency changes, not every niche API.
|
||||
4. **Post-migration cleanup**: once on the target version, use the **bevy-0.19** skill to rewrite awkward mechanically-ported code into current idioms (e.g. old buffered events that should now be observers, manual child bookkeeping that relationships handle).
|
||||
|
||||
## Reference Material
|
||||
## Transition references
|
||||
|
||||
Same research as the `bevy` skill — version-change annotations are woven into each report:
|
||||
Load only the transitions on your path:
|
||||
|
||||
- [01-plugins.md](../../../../docs/workpad/research/bevy-skill-research/01-plugins.md) — Plugin API changes across versions
|
||||
- [02-ecs-core.md](../../../../docs/workpad/research/bevy-skill-research/02-ecs-core.md) — ECS API renames and new patterns
|
||||
- [03-hierarchy.md](../../../../docs/workpad/research/bevy-skill-research/03-hierarchy.md) — Parent->ChildOf transition
|
||||
- [04-events-observers.md](../../../../docs/workpad/research/bevy-skill-research/04-events-observers.md) — Event/Message split
|
||||
- [05-input.md](../../../../docs/workpad/research/bevy-skill-research/05-input.md) — Input API changes
|
||||
- [06-camera-gotchas.md](../../../../docs/workpad/research/bevy-skill-research/06-camera-gotchas.md) — Camera API changes
|
||||
| File | Covers |
|
||||
|------|--------|
|
||||
| `references/0.15-to-0.17.md` | 0.15→0.16 and 0.16→0.17: `Parent`→`ChildOf`, recursive `despawn`, bundle removal, `StateScoped` replacement |
|
||||
| `references/0.17-to-0.18.md` | The largest transition: Event/Message split, fallible systems, state-transition semantics, renames across hierarchy/render/input |
|
||||
| `references/0.18-to-0.19.md` | Resources-as-components, `Scene`→`WorldAsset`, asset API changes |
|
||||
|
||||
## Detecting the source version from code
|
||||
|
||||
When `Cargo.toml` is ambiguous (workspace inheritance, git deps), the code itself dates the project:
|
||||
|
||||
| You see | Version is |
|
||||
|---------|-----------|
|
||||
| `Parent`, `despawn_recursive()`, `#[derive(Bundle)]`, `StateScoped` | ≤0.15 |
|
||||
| `ChildOf` but `EventWriter`/`EventReader`/`on_event` | 0.16–0.17 |
|
||||
| `MessageWriter`/`MessageReader`, `-> Result` systems | 0.18+ |
|
||||
| `WorldAsset`/`WorldAssetRoot`, `Without<IsResource>` filters | 0.19 |
|
||||
|
||||
## The traps of upgrading
|
||||
|
||||
- **Renames that compile but misbehave**: some old names still exist with new meaning. `Event` compiles in 0.18+ but means observer-events, not buffered messages — a mechanical `EventWriter → MessageWriter` port is right, but a type left as `#[derive(Event)]` and "written" nowhere fires no observers silently.
|
||||
- **Behavior shifts need runtime verification**: `State::set()` same-state re-firing (0.18), transform-propagation reads, `Query<Entity>` matching resource entities (0.19). `cargo check` proves nothing here — run it.
|
||||
- **Ecosystem crates lag.** A Bevy bump is only as ready as its slowest dependency. Check compatibility before starting, not after.
|
||||
- **Don't modernize mid-bump.** During a bump, make the minimal change that satisfies the new API; idiomatic rewrites happen after the chain is complete (with the bevy-0.19 skill). Mixing the two makes regressions unattributable.
|
||||
|
||||
## Related skill
|
||||
|
||||
**bevy-0.19** — writing correct, idiomatic code on the current version. Use it after the migration chain completes, and whenever the task turns from porting into feature work.
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
# 0.15 → 0.16 → 0.17
|
||||
|
||||
The 0.16 transition restructured hierarchy and spawning; 0.17 was comparatively quiet. Both are covered here. For changes not listed, consult the official guides: `https://bevy.org/learn/migration-guides/0-15-to-0-16/` and `.../0-16-to-0-17/`.
|
||||
|
||||
## Renames & removals (mechanical)
|
||||
|
||||
| Old (≤0.15) | New (0.16+) | Notes |
|
||||
|-------------|-------------|-------|
|
||||
| `Parent` component | `ChildOf(pub Entity)` | Lives on the child; entities *with* `ChildOf` are children |
|
||||
| `*parent` (Deref to Entity) | `child_of.parent()` | Deref removed |
|
||||
| `despawn_recursive()` | `despawn()` | `despawn()` is now recursive by default |
|
||||
| `despawn_descendants()` | `despawn_related::<Children>()` | |
|
||||
| `#[derive(Bundle)]` | spawn tuples + `#[require(...)]` | Bundles removed as a user-facing pattern |
|
||||
| `StateScoped(state)` | `DespawnOnExit(state)` / `DespawnOnEnter(state)` | |
|
||||
| `parent_entity()` (child spawner) | `target_entity()` | |
|
||||
|
||||
## API shape changes
|
||||
|
||||
- **Hierarchy is relationship-based**: `ChildOf` implements `Relationship`, `Children` implements `RelationshipTarget`; they sync automatically via hooks. Delete any manual `Children` bookkeeping — it now fights the engine.
|
||||
- **Spawning children**: `commands.spawn((Comp, ChildOf(parent)))` works directly; `with_children(|spawner| ...)` remains. `add_child` / `add_children` / `insert_children` attach existing entities.
|
||||
- **`Children` iteration** yields `Entity` via `IntoIterator`: `for child in &children`. Ports of `children.iter().copied()` compile but are noise — simplify during cleanup.
|
||||
- **Required components** replace bundles: `#[require(Transform, Visibility)]` on a marker pulls in dependencies at spawn. When porting a `Bundle` struct, its fields usually become either a spawn tuple at call sites or `#[require(...)]` on the primary component.
|
||||
- Query relationship extensions appear: `query.related::<ChildOf>(entity)`, `query.relationship_sources::<Children>(entity)`.
|
||||
|
||||
## Semantic shifts (audit, don't just compile)
|
||||
|
||||
- **`despawn()` is recursive.** Code that relied on `despawn()` orphaning children (intentionally detaching them) now destroys them. If detach-then-despawn was the intent, `detach_all_children()` first (name as of 0.18; `clear_children()` in 0.16–0.17).
|
||||
- **Adding `ChildOf` has side effects**: parent's `Children` updates immediately via hooks, and lifecycle observers on `Children` fire. Ordering assumptions around manual parent updates are void.
|
||||
|
||||
## Porting checklist
|
||||
|
||||
1. `grep -rn "Parent\b"` — replace with `ChildOf`, fix accessors to `.parent()`.
|
||||
2. `grep -rn "despawn_recursive\|despawn_descendants"` — rename; then audit remaining plain `despawn()` calls for the orphaning assumption.
|
||||
3. `grep -rn "derive(Bundle)"` — convert to tuples/`#[require]`.
|
||||
4. `grep -rn "StateScoped"` — replace with `DespawnOnExit`/`DespawnOnEnter`.
|
||||
5. Remove manual child-list maintenance (observers/systems pushing into `Children`).
|
||||
@@ -0,0 +1,57 @@
|
||||
# 0.17 → 0.18
|
||||
|
||||
The largest recent transition. Its centerpiece — the Event/Message split — is architectural, not just a rename. Official guide for the long tail: `https://bevy.org/learn/migration-guides/0-17-to-0-18/`.
|
||||
|
||||
## The Event/Message split
|
||||
|
||||
The old buffered event system was split in two:
|
||||
|
||||
| Old (≤0.17) | New (0.18) |
|
||||
|-------------|-----------|
|
||||
| `#[derive(Event)]` (buffered use) | `#[derive(Message)]` |
|
||||
| `EventWriter<T>` / `.send(e)` | `MessageWriter<T>` / `.write(m)` |
|
||||
| `EventReader<T>` | `MessageReader<T>` |
|
||||
| `Events<T>` | `Messages<T>` |
|
||||
| `on_event::<T>()` run condition | `on_message::<T>()` |
|
||||
|
||||
`Event` still exists but now means **observer-events only** (`commands.trigger()` + `On<E>` observers, immediate push). This is the transition's biggest trap: a type ported to `#[derive(Event)]` and written with a `MessageWriter` won't compile, but a type left as `Event` with a forgotten reader silently does nothing.
|
||||
|
||||
Port rule: every buffered event → `Message`. Only convert to `Event`+observer deliberately, during post-migration cleanup, where immediate reaction is actually wanted.
|
||||
|
||||
New in 0.18: `#[derive(EntityEvent)]` with `#[event_target]` for entity-targeted events, optionally `#[entity_event(propagate)]` to bubble up `ChildOf`.
|
||||
|
||||
## Renames & moves (mechanical)
|
||||
|
||||
| Old | New |
|
||||
|-----|-----|
|
||||
| `remove_child` / `remove_children` / `clear_children` | `detach_child` / `detach_children` / `detach_all_children` |
|
||||
| `bevy::render::render_resource::ShaderRef` | `bevy::shader::ShaderRef` |
|
||||
| `WinitPlugin::<WakeUp>` | `WinitPlugin` (no longer generic) |
|
||||
| `AmbientLight` (single type) | `GlobalAmbientLight` **resource** + `AmbientLight` **camera component** |
|
||||
| `Camera.target` field | `RenderTarget` required component |
|
||||
| `GltfPlugin` `use_model_forward_direction` | `convert_coordinates` + `rotate_scene_entity` / `rotate_meshes` flags |
|
||||
|
||||
## API shape changes
|
||||
|
||||
- **Fallible systems**: systems may return `Result` (= `Result<(), BevyError>`); errors log and the system retries next tick. Adopt during the bump wherever it deletes `unwrap()`s cheaply; systematically during cleanup.
|
||||
- **Mesh accessors** became `try_*` returning `Result<_, MeshAccessError>`.
|
||||
- **`MaterialPlugin` fields** `prepass_enabled` / `shadows_enabled` became `Material` trait methods.
|
||||
- **`LoadContext::path()`** returns `AssetPath`, not `Path`; image `reinterpret_*` methods return `Result`.
|
||||
- **Immutable components**: `#[component(immutable)]` available.
|
||||
- **Input feature gates**: `mouse`, `keyboard`, `gamepad`, `touch`, `gestures` are now cargo features. Default-on, but builds with `default-features = false` must list them — otherwise input silently stops arriving.
|
||||
|
||||
## Semantic shifts (audit, don't just compile)
|
||||
|
||||
- **`NextState::set()` same-state transitions fire.** `set(Playing)` while in `Playing` now triggers `OnExit(Playing)` + `OnEnter(Playing)` (previously a no-op). One-time setup in `OnEnter` (spawn camera, load level) re-runs. Audit every `next_state.set(...)` reachable while already in that state; use `set_if_neq()` where the old behavior is wanted.
|
||||
- **Material shader bind groups**: material bindings are `@group(3)`; `@group(2)` is the mesh storage buffer. Old custom shaders using `@group(2)` fail with `Storage class Storage doesn't match the shader Uniform`. Update all `.wgsl` material shaders.
|
||||
- **`MouseMotion`, `MouseWheel` and other input streams are Messages now** — readers must be `MessageReader`.
|
||||
|
||||
## Porting checklist
|
||||
|
||||
1. `grep -rn "EventWriter\|EventReader\|on_event\|Events<"` — rename to Message equivalents; `.send(` → `.write(`.
|
||||
2. `grep -rn "derive(Event)"` — decide per type: buffered (→ `Message`) or observed (stays `Event`, needs a trigger + observer).
|
||||
3. `grep -rn "remove_child\|clear_children"` — rename to `detach_*`.
|
||||
4. `grep -rn "next_state.set\|NextState"` — audit same-state re-fire.
|
||||
5. `grep -rn "@group(2)" --include=*.wgsl` — move material bindings to group 3.
|
||||
6. `grep -rn "AmbientLight"` — split into resource vs camera component usage.
|
||||
7. Check `Cargo.toml` for `default-features = false` on bevy → add input features.
|
||||
@@ -0,0 +1,36 @@
|
||||
# 0.18 → 0.19
|
||||
|
||||
Smaller than 0.17→0.18 but with one deep semantic change: resources became components. Official guide for the long tail: `https://bevy.org/learn/migration-guides/0-18-to-0-19/`.
|
||||
|
||||
## Renames & moves (mechanical)
|
||||
|
||||
| Old (0.18) | New (0.19) |
|
||||
|------------|-----------|
|
||||
| `Scene` / `SceneRoot` | `WorldAsset` / `WorldAssetRoot` |
|
||||
| `AssetPath::resolve(&str)` / `resolve_embed(&str)` | `resolve_str()` / `resolve_embed_str()` (`resolve()`/`resolve_embed()` now take `&AssetPath`) |
|
||||
| `Hdr` in `bevy_render` | `bevy_camera` |
|
||||
| `Camera` screen-space specular transmission fields | `ScreenSpaceTransmission` component |
|
||||
|
||||
## API shape changes
|
||||
|
||||
- **`Assets::get_mut`** returns `AssetMut<A>` (mutation-tracked), not `&mut A`. Deref for access; code storing the `&mut A` needs restructuring.
|
||||
- **`Ref<T>.clone()`** returns `Ref<T>`, not the inner value. Old code relying on clone-through must use `ref.deref().clone()`.
|
||||
- **GLTF material loading** returns `GltfMaterial`; request a `StandardMaterial` with the `#Material0/std` label suffix.
|
||||
- **`EntityComponentsTrigger`** gained archetype fields — exhaustive destructuring breaks; add `..`:
|
||||
`let EntityComponentsTrigger { components, .. } = e.trigger();`
|
||||
|
||||
## Semantic shifts (audit, don't just compile)
|
||||
|
||||
- **Resources are components on abstract entities.** Consequences:
|
||||
- `Query<Entity>` and other very broad queries now match resource entities. Any "iterate all entities" logic (cleanup sweeps, entity counts, serialization, debug overlays) silently includes resources — filter with `Without<IsResource>`.
|
||||
- A type can no longer derive both `Component` and `Resource`. Split such types or pick one role.
|
||||
- **Scene rename is semantic-adjacent**: `WorldAsset` naming reflects the same data model, but grep for the old names in strings/reflection paths, not just types.
|
||||
|
||||
## Porting checklist
|
||||
|
||||
1. `grep -rn "SceneRoot\|Scene>" ` — rename to `WorldAssetRoot`/`WorldAsset`.
|
||||
2. `grep -rn "Query<Entity[,>]"` and other broad queries — add `Without<IsResource>` where resources must not appear.
|
||||
3. `grep -rn "derive(Component" | grep "Resource"` — find dual-derive types; split them.
|
||||
4. `grep -rn "get_mut" ` on `Assets<...>` — adapt to `AssetMut`.
|
||||
5. `grep -rn "EntityComponentsTrigger {"` — add `..` to destructuring.
|
||||
6. GLTF material handles — append `/std` labels where `StandardMaterial` is expected.
|
||||
Reference in New Issue
Block a user