Using component markers into your entities

After a little poll on the Bevy's Discord with an (almost) unexpected result, I wanted to talk about the component markers into the Bevy Engine.

But before we dig into this and learn for those who don't know what we are talking about, let review the poll and its result to see how much the comunity agreed on its usage.

The poll

Do you (almost) always include a marker component into your entities to ease your queries?
If so do you create some really specific marker to deeply filter them or do you finish the filtering conditionaly in the iter?
Let say I have the player character, some enemies and the enemies could be 🐍 or 🦀.
All of these entities have Life component.

What will you do if you need to query only the 🦀?

You:
🍏 Just get all entities which have Life and conditionaly filter the iter.
🥝 Create a Player component marker and an Enemy one and conditionaly filter the iter.
🍍 Create a Player, Enemy, Snake, and Crab component marker.
🌭 Man... Nobody do any of that, from which universe are you coming from?

Results: 7x🍍

The result was a little unexpected in the way that all the participants chosen the same response and I'm not from another universe (I was pretty sure of that one 👽).

Ok, so all the participants are ok to say that it's a good practice to deeply use component markers, but how deep do they use them? That is another question that I wanted to answer:

Do you have some kind of "limit"?
Let say we have red and blue 🦀 and the red ones could live in the water on on the earth, do you create BlueCrab, RedWaterCrab and RedLandCrab markers?

Here again, all the participants agreed to say that it depends on what is the meaning of the colors and place of life: is that just the appearance of the mob, or does the colors give some specific capabilities to the crab?

If the colors and the environment of the crab makes that sort of crab specific, a marker component is appropriated, not based on the color, but on the capability instead.

To continue with our 🦀 and 🐍 example, imagine:

The red land crab can dig.
the blue one can't (they live in trees).
The red water crab can't.
But the 🐍 can dig too! (hope you are still here!).

Here the most appropriate way to use component markers would be to create Snake, the Crab derived into their respective colors and a Digger component:
the Digger marker will only be added to the 🐍 and the 🦀 who can dig (directly from the spawn method).

The code

Ok, now let's code a little to explain the simple example bellow:

struct Life(pub u8);

pub struct Player;
struct Character {
    life: Life,
    friend: Player
}

struct Snake {
    life: Life,
    friend: Enemy,
}

struct BlueCrab {
    life: Life,
    friend: Enemy
}

struct RedLandCrab {
    life: Life,
    friend: Enemy,
}

struct RedWaterCrab {
    life: Life,
    friend: Enemy
}

Here we created a component Life and some structs that will be our entities, Player, the Snake and all sort of Crabs.

No problem here, but using the queries into Bevy, we can only search for the entities with the components they need to hold to be in our set:

fn check_life(mut commands:Commands, query:Query<&Life>) {
    for entity in query.iter() {
        // Here we get the life of the Player and all of our tiny mobs

        // And probably few others entities type which contain the Life component (NPC, buildings, ...)
    }
}

Using an Enemy component marker only when needed and the With dynamic type into the query, we can filter out the Player:

struct Life(pub u8);

pub struct Player;
struct Character {
    life: Life,
    friend: Player
}

pub struct Enemy;
struct Snake {
    life: Life,
    friend: Enemy,
}

struct BlueCrab {
    life: Life,
    friend: Enemy
}

struct RedLandCrab {
    life: Life,
    friend: Enemy,
}

struct RedWaterCrab {
    life: Life,
    friend: Enemy
}

fn check_enemies_life(mut commands:Commands, query:Query<&Life,With<Enemy>>) {
    for entity in query.iter() {
        // Here we get all of our enemies only
    }
}

Here we get only the enemies, but how to select only thos who can dig?

Let's just add another component marker to be more specific!

struct Life(pub u8);

pub struct Player;
struct Character {
    life: Life,
    friend: Player
}

pub struct Enemy;
pub struct Digger;
struct Snake {
    life: Life,
    friend: Enemy,
    can_dig: Digger // Snakes can dig
}

struct BlueCrab {
    life: Life,
    friend: Enemy
}

struct RedLandCrab {
    life: Life,
    friend: Enemy,
    can_dig: Digger // RedLandCrabs can dig
}

struct RedWaterCrab {
    life: Life,
    friend: Enemy
}

fn check_diggers_life(mut commands:Commands, query:Query<&Life,(With<Enemy>,With<Digger>)>) {
    for entity in query.iter() {
        // Here we get only the Diggers life!
    }
}

I hope now that you understand the utilities of the component markers, when and why to use them.

A special thanks to TheRawMeatball and leonsver1 for their active participation into the Discord discussion.

Comments

Be the first to post a comment!

Add a comment

Preview