Definition:


  • ECS for shorten is a paradigm that breaks your program into core components:

  • For example: There is entity Player has two components HP and Level. If the player gets enough experience the player will level up. That is called level_up system for Player. Or when the enemy hit the Player, the HP will be decreased. That is also a system.

Bevy ECS:


#[derive(Component)]
struct Position {
	x: i32,
	y: i32,
}
  • Here we define the type of component Position.

  • System: Rust Function

fn print_position_system(query: Query<&Position>)
{
	for position in &query {
		println!("x = {}, y = {}", position.x, position.y);
	}
}
struct Entity(u64);

Practice


Example 1: Hello World and add system to your app


  • We will make an app to print “Hello, World!!“.
  • First we create the system hello_world:
fn hello_world() {
	println!("Hello, World!!");
}
  • Then add the system to App:
fn main()
{
	App::new()
		.add_systems(Update, hello_world)
		.run()
}
  • Update: the Schedule contains the systems that run once for per render frame.
  • add_systems: this adds the system to the Update schedule.

Example 2: First component


#[derive(Component)]
struct Person;
  • This is Rust’s Unit Struct. For mainly purpose of marker.

  • Next, give the Person a component Name:

#[derive(Component)]
struct Name(String);
  • To add our Person to the World we use a system called Startup system.
  • Startup: The Schedule that contains system that:
    • Runs only once.
    • Runs before all other systems right after the app starts.
fn add_people(mut commands: Commands) {
	command.spawn((Person, Name("Nguyen Nhat Minh".to_string())));
	command.spawn((Person, Name("Nguyen Hoang Trung".to_string())));
}
  • This systems will add the persons named “Nguyen Nhat Minh” and “Nguyen Hoang Trung” to our World.

  • Command

  • Finally, add the system to the schedule Startup in our main function:

fn main()
{
	App::new()
		.add_systems(Startup, add_people)
		.run();
}
  • To display the data in the World we create a system to query it:
fn show(query: Query<&Name, With(Person)>) {
	for name in &query {
		println!("name = {}", name.0);
	}
}
fn main()
{
	App::new()
		.add_systems(Startup, add_people)
		.add_systems(Update, show)
		.run();
}
  • If we want to change the Name of a Person, use mut Query:
fn change_name(mut query: Query<&mut Name, With<Person>>) {
    for mut name in &mut query {
        if name.0 == "Minh" {
            name.0 = "MINH".to_string();
        }
    }
}
  • Then add to our main like following code:
fn main()
{
    App::new()
        .add_systems(Startup, add_people)
        .add_systems(Update, (show, change_name, show).chain())
        .run();
}
  • chain() will order the actions. First show the name then change the name then show it again.

ECS vs OOP


  • In OOP, you have classes with data and functionality. Its objects will have the same data type but different values.
  • In ECS, the data is combination of components. That means an entity can have any kinds of that combination. The entity itself is already used to identify that data. Thanks to this, u can have an Entity with component Transform or Color.
  • A set of component of an entity is called entity’s Archetype.
    • Entities with the same Archetype will be stored in the same contiguous arrays.
    • Add/Remove component types mean that you are changing the archetype.