325e278af1
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/.
95 lines
3.9 KiB
Markdown
95 lines
3.9 KiB
Markdown
# Bevy Skill Research: Plugin Architecture
|
|
|
|
> **Bevy version**: 0.18, with 0.19 notes
|
|
> **Date**: 2026-07-07
|
|
|
|
## Plugin Trait (`bevy::app::Plugin`)
|
|
|
|
**Required**:
|
|
- `fn build(&self, app: &mut App)` — runs immediately on `add_plugins`
|
|
|
|
**Optional (provided defaults)**:
|
|
- `fn ready(&self, _app: &App) -> bool` — default `true`; override for async init
|
|
- `fn finish(&self, _app: &mut App)` — runs after all plugins report `ready()`, before first schedule tick; use for setup that depends on other plugins' async work
|
|
- `fn cleanup(&self, _app: &mut App)` — runs after `finish()`, before schedule; remove temp resources
|
|
- `fn name(&self) -> &str` — defaults to type name; used for uniqueness check
|
|
- `fn is_unique(&self) -> bool` — default `true`; set `false` for multi-instance plugins
|
|
|
|
Trait bounds: `Downcast + Any + Send + Sync`.
|
|
|
|
Any `fn(&mut App)` auto-implements `Plugin`.
|
|
|
|
## Plugin Dependencies
|
|
|
|
No formal "depends-on" declaration. Patterns:
|
|
- `app.is_plugin_added::<T>() -> bool` to guard or panic
|
|
- Sub-plugins: call `app.add_plugins(ChildPlugin)` inside `build()`. Bevy deduplicates by name (unique plugins panic on double-add)
|
|
- `finish()` is the idiomatic place for cross-plugin setup that needs another plugin's resources to exist
|
|
|
|
## Plugin Groups
|
|
|
|
`DefaultPlugins` / `MinimalPlugins` implement `PluginGroup`.
|
|
|
|
Customization via `PluginGroupBuilder` fluent API:
|
|
- `.disable::<T>()` — skip a plugin
|
|
- `.set(plugin)` — replace a plugin's config
|
|
- `.add_before::<Target>(plugin)` / `.add_after::<Target>(plugin)` — ordering
|
|
- `.enable::<T>()` — re-enable disabled
|
|
|
|
```rust
|
|
DefaultPlugins.disable::<AudioPlugin>().set(WindowPlugin { ... })
|
|
```
|
|
|
|
## App Builder Key Methods
|
|
|
|
| Method | Purpose |
|
|
|--------|---------|
|
|
| `add_plugins(P)` | Accepts plugin, plugin group, or tuple |
|
|
| `insert_resource(R)` | Insert (overwrites existing) |
|
|
| `init_resource::<R>()` | Insert via `Default`/`FromWorld` if absent |
|
|
| `add_systems(schedule, systems)` | Add to a named schedule |
|
|
| `configure_sets(schedule, sets)` | Configure system set ordering |
|
|
| `register_type::<T>()` | Register in `AppTypeRegistry` (reflection) |
|
|
| `is_plugin_added::<T>()` | Check if plugin was already added |
|
|
| `register_required_components::<T, R>()` | Component requirements (0.15+) |
|
|
|
|
## States (`bevy::state`)
|
|
|
|
Three flavors:
|
|
- **FreelyMutableState**: Changed via `NextState<S>` resource
|
|
- **SubStates**: Removed from world when parent state isn't in required variant
|
|
- **ComputedStates**: Derived automatically via `fn compute(sources) -> Option<Self>`
|
|
|
|
Registration:
|
|
- `app.init_state::<S>()` — register with default initial
|
|
- `app.insert_state(value)` — register with explicit initial
|
|
|
|
Transition schedules: `OnEnter(S)`, `OnExit(S)`, `OnTransition(S)`
|
|
|
|
Entity scoping: `DespawnOnEnter(S)`, `DespawnOnExit(S)` (replaced `StateScoped` in 0.16+)
|
|
|
|
Run condition: `in_state(S) -> impl Condition` (in `bevy::state::condition`)
|
|
|
|
**0.18 change**: `State::set()` now always triggers `OnEnter`/`OnExit` even for same-state transitions. Use `set_if_neq()` for old behavior.
|
|
|
|
## Schedules (Built-in Labels)
|
|
|
|
| Phase | Schedules |
|
|
|-------|-----------|
|
|
| Startup | `PreStartup`, `Startup`, `PostStartup` (run once) |
|
|
| Per-frame | `First`, `PreUpdate`, `Update`, `PostUpdate`, `Last` |
|
|
| Fixed timestep | `FixedFirst`, `FixedPreUpdate`, `FixedUpdate`, `FixedPostUpdate`, `FixedLast` (inside `FixedMain`, driven by `RunFixedMainLoop`) |
|
|
| Other | `Main`, `SpawnScene` (new) |
|
|
|
|
## Run Conditions (`bevy::ecs::schedule::common_conditions`)
|
|
|
|
Key functions:
|
|
- `in_state(s)` — state matching
|
|
- `resource_exists::<R>()`, `resource_added::<R>()`, `resource_changed::<R>()`, `resource_equals(val)`
|
|
- `any_with_component::<T>()`, `any_component_removed::<T>()`
|
|
- `run_once()`, `not(cond)`
|
|
- `on_message::<E>()` (renamed from `on_event` in 0.18)
|
|
- `condition_changed(cond)`, `condition_changed_to(cond)`
|
|
|
|
Combinators: `.and(cond)`, `.or(cond)` on any condition.
|