bevy systemparam grouping
Guide for bevy systemparam grouping
Bevy ECS UI Parameter Grouping Pattern
Problem
Bevy has a hard limit of 16 parameters for system functions. Complex UI systems often need:
- Multiple queries (player, inventory, equipment, skills, etc.)
- Multiple resources (game data, UI state, assets, etc.)
- Multiple event writers (use item, equip, unequip, cast spell, etc.)
This quickly exceeds the 16-parameter limit.
Solution: SystemParam Struct
Group related parameters into a custom
SystemParam struct.Implementation
File:
src/systems/ui/mod.rs#[derive(bevy::ecs::system::SystemParam)]
pub struct UiParams<'w, 's> {
pub ui_assets: Res<'w, UiAssets>,
pub interactables: Query<'w, 's, &'static crate::systems::api::Interactable>,
pub player_q: Query<'w, 's, (
Entity,
&'static crate::systems::player::skills::Skills,
Option<&'static crate::systems::skills::magic::ActiveSpell>
), With<crate::systems::player::Player>>,
pub skills_q: Query<'w, 's, &'static crate::systems::player::skills::Skills>,
pub inventory_q: Query<'w, 's, &'static crate::systems::player::inventory::Inventory>,
pub equipment_q: Query<'w, 's, &'static crate::systems::player::equipment::Equipment>,
pub skills_data: Res<'w, crate::systems::data::SkillsData>,
}Usage in System
fn ui_main_layout(
mut contexts: EguiContexts,
mut ui_state: ResMut<UiState>,
mut combat_style_writer: EventWriter<CombatStyleEvent>,
editor_state: Option<Res<crate::systems::editor::EditorState>>,
mut last_interaction: Local<String>,
mut interaction_reader: EventReader<InteractionEvent>,
params: UiParams, // Single parameter containing all grouped params
mut plugin_ui_state: ResMut<PluginUiState>,
mut use_item_writer: EventWriter<crate::systems::player::inventory::UseItemEvent>,
mut unequip_item_writer: EventWriter<crate::systems::player::equipment::UnequipItemEvent>,
mut spell_book_state: ResMut<spell_book_ui::SpellBookState>,
mut select_spell_writer: EventWriter<crate::systems::skills::magic::SelectSpellEvent>,
mut coconut_ui_state: ResMut<coconut_stacking_ui::CoconutUIState>,
) {
// Unpack for easier usage
let ui_assets = params.ui_assets;
let player_q = params.player_q;
let inventory_q = params.inventory_q;
let equipment_q = params.equipment_q;
// ... use as normal
}Benefits
- Stays Under Limit: Counts as 1 parameter instead of N
- Organized: Groups related parameters logically
- Reusable: Can use same struct in multiple systems
- Type-Safe: Full compile-time checking
Grouping Guidelines
Good Candidates for Grouping
- Read-only queries (player, inventory, skills, etc.)
- Resources that are frequently used together
- Data that represents a "view" of the game state
Keep Separate
- Event writers (often need
mut) - State resources that are modified (
ResMut) - Local state (
Local<T>) - Context objects (
EguiContexts)
Example Grouping Strategy
// Group 1: Read-only game state
#[derive(SystemParam)]
pub struct GameStateParams<'w, 's> {
pub player_q: Query<...>,
pub inventory_q: Query<...>,
pub equipment_q: Query<...>,
}
// Group 2: UI resources
#[derive(SystemParam)]
pub struct UiResourceParams<'w> {
pub ui_assets: Res<'w, UiAssets>,
pub game_data: Res<'w, GameData>,
pub theme: Res<'w, UiTheme>,
}
// Keep separate: mutable state and event writers
fn my_system(
game_state: GameStateParams,
ui_resources: UiResourceParams,
mut ui_state: ResMut<UiState>,
mut events: EventWriter<MyEvent>,
) { ... }Lifetime Parameters
'w: World lifetime (for resources)'s: System state lifetime (for queries)
Both are required when deriving
SystemParam.Common Pitfalls
❌ Don't group mutable state
// BAD: Can't have multiple mutable borrows
#[derive(SystemParam)]
pub struct BadParams<'w> {
pub state1: ResMut<'w, State1>,
pub state2: ResMut<'w, State2>, // Might conflict!
}✅ Do group read-only queries
// GOOD: Multiple immutable borrows are fine
#[derive(SystemParam)]
pub struct GoodParams<'w, 's> {
pub query1: Query<'w, 's, &'static Component1>,
pub query2: Query<'w, 's, &'static Component2>,
}Real-World Example
Before (18 parameters - won't compile):
fn ui_system(
contexts: EguiContexts,
ui_state: ResMut<UiState>,
ui_assets: Res<UiAssets>,
player_q: Query<...>,
inventory_q: Query<...>,
equipment_q: Query<...>,
skills_q: Query<...>,
game_data: Res<GameData>,
// ... 10 more parameters
) { }After (12 parameters - compiles):
fn ui_system(
contexts: EguiContexts,
ui_state: ResMut<UiState>,
params: UiParams, // Groups 7 parameters
// ... 9 more parameters
) { }