Skip to content

Latest commit

 

History

History
141 lines (107 loc) · 4.29 KB

too_many_parameters_for_systems.md

File metadata and controls

141 lines (107 loc) · 4.29 KB

Too Many Parameters For Systems

Sometimes, it is the systems that have a long list of parameters. For example, we may need to access entities and resources of different types in one system.

In this case, we use SystemParam to group parameters of a system. In the following code, we have a struct PlayerAction that derives SystemParam and contains a Query and a ResMut.

#[derive(Component)]
struct Name(String);

#[derive(Component)]
struct Hp(u32);

#[derive(Component)]
struct Enemy;

#[derive(Resource)]
struct EnemyCounter(u32);

#[derive(SystemParam)]
struct PlayerAction<'w, 's> {
    enemies: Query<'w, 's, (&'static Name, &'static mut Hp), With<Enemy>>,
    counter: ResMut<'w, EnemyCounter>,
}

Similar to WorldQuery, the references in Query have lifetime 'static, and are mut if they need to be mutable. Yet, we have to connect the lifetimes ('w and 's) between SystemParam and Query (as well as Res and ResMut).

A benefit of using SystemParam is that we can have methods for the structs deriving SystemParam. This helps us reusing code when we need it in multiple systems.

impl<'w, 's> PlayerAction<'w, 's> {
    fn attack_enemies(&mut self) {
        for (name, mut hp) in &mut self.enemies {
            hp.0 = 0;
            self.counter.0 -= 1;
            println!("{} is dead.", name.0);
        }

        println!();
    }

    fn enemy_information(&self) {
        println!("Enemy count:");
        println!("{}", self.counter.0);

        println!("Current enemies:");
        for (name, hp) in &self.enemies {
            println!("Name: {}, HP: {}", name.0, hp.0);
        }

        println!();
    }
}

( Note that self.enemies in the enemy_information method does not need to be mutable. We declare it as mutable for the attack_enemies method. In practice, we should avoid this kind of usage. )

Then, we can use the SystemParam in systems.

fn enemy_information(player_action: PlayerAction) {
    player_action.enemy_information();
}

fn attack_enemies(mut player_action: PlayerAction) {
    player_action.attack_enemies();
}

The full code is as follows:

use bevy::{
    app::{App, PostUpdate, PreUpdate, Startup, Update},
    ecs::{
        component::Component,
        query::With,
        system::{Commands, Query, ResMut, Resource, SystemParam},
    },
};

fn main() {
    App::new()
        .insert_resource(EnemyCounter(2))
        .add_systems(Startup, setup)
        .add_systems(PreUpdate, enemy_information)
        .add_systems(Update, attack_enemies)
        .add_systems(PostUpdate, enemy_information)
        .run();
}

// the code for the components: Name, Hp and Enemy

// the code for the resource: EnemyCounter

fn setup(mut commands: Commands) {
    commands.spawn((Name("Friend 1".into()), Hp(100)));
    commands.spawn((Name("Friend 2".into()), Hp(250)));
    commands.spawn((Name("Friend 3".into()), Hp(150)));

    commands.spawn((Name("Enemy 1".into()), Hp(300), Enemy));
    commands.spawn((Name("Enemy 2".into()), Hp(500), Enemy));
}

// the code for the SystemParam: PlayerAction

// the code for the systems: enemy_information and attack_enemies

Output;

Enemy count:
2
Current enemies:
Name: Enemy 1, HP: 300
Name: Enemy 2, HP: 500

Enemy 1 is dead.
Enemy 2 is dead.

Enemy count:
0
Current enemies:
Name: Enemy 1, HP: 0
Name: Enemy 2, HP: 0

➡️ Next: Too Many Systems

📘 Back: Table of contents