Suggestions creating a basic ECS System

Hey there, I'm new to Rust and trying to make a basic ECS system. I'm pretty sure what I did sucks and afraid of sharing it, but I got to learn some how.

At the end of the post I'll share a playground with my full and very brief example code, but here's a (slightly) TLDR version:

Implementation:

  1. I declare an Entity struct:

    #[derive(Copy, Clone)]
    struct Entity {
    pub id : u32,
    pub components : [u32; MAX_COMPONENTS],
    }

  2. I declare an EntityManager that holds my Entity structs.

    struct EntityManager {
    pub entities : [Entity; MAX_ENTITIES],
    pub num_entities : usize,
    }

  3. I declare a few structs that will serve as components

    #[derive(Copy, Clone)]
    struct Player {
    pub hp: i32,
    }

    #[derive(Copy, Clone)]
    struct Inventory {
    pub blah: u32,
    }

  4. I declare a few u32's I can use as component TAGS, which I store in an entity's component array:

    pub const PLAYER_COMPONENT : u32 = 1 << 1;
    pub const INVENTORY_COMPONENT : u32 = 1 << 2;

  5. I also declare a ComponentManager where I manually add component arrays that hold declared components. I don't mind manually doing this all the time.

    struct ComponentManager {
    pub players : [Player; MAX_ENTITIES],
    pub inventories : [Inventory; MAX_ENTITIES],
    }

  6. I instantiate both managers and add components to the fist entity in the array:

    // Add a Player Component to Entity
    entity_manager.entities[0].components[0] = PLAYER_COMPONENT;
    // Add an Inventory Component to Entity
    entity_manager.entities[0].components[1] = PLAYER_COMPONENT | INVENTORY_COMPONENT;

  7. Then I finally loop through all entities and their comonents, and update their systems:

    // Loop through all Entities
    for y in 0 .. MAX_ENTITIES {
    let entity = entity_manager.entities[y];

     // Loop through Entities, components and check for N components
     for x in 0 .. MAX_COMPONENTS {
    
         // Update Player System
         if entity.components[x] == PLAYER_COMPONENT {
             let mut _comp = component_manager.players[y];
             println!("Update Player Component");
         }
         
         else if entity.components[x] == PLAYER_COMPONENT | INVENTORY_COMPONENT {
             let mut _comp = component_manager.inventories[y];
             println!("Update Player Inventory System");
         }
     }
    

    }

Again, I'm pretty sure this is kind of bad, but if any of you have any tips or even references to pages that teach me how to make ECS systems in rust, I would greatly appreciate it.

Playground Example

Thanks!

P.S.
I'm using arrays instead of vectors in order to keep my data on the stack. I read in game dev keeping as much on the stack is quicker.

Check out specs

Hi, if you want to make an ECS Catherine West's Keynote can teach you a few things.

1 Like

Thanks Krishna, but I wanted to learn how an ECS System is made. I'll keep it mind if I need a production level solution though.

Thanks leudz, I'll defintiely checkout the Keynote.

Is my implementation far from how an ECS system works though?

There's no one implementation, there are multiple ones, each with their pros and cons. So there's no real answer to "how close am I?" except if you base your design on a specific implementation.

However, arrays are a (very) bad idea, stack vs heap isn't a problem here (if ever).
In your systems, it shouldn't be an else if, just another if.

In the end it's not a great implementation but you can make a game with it. I'm not sure a game of what size though.

For resources there is this faq, these blog posts explain one way to make an ECS, this Overwatch talk but it's more advanced I'd say and there's this blog post exploring ideas.
And of course looking at diverse implementations, specs has already been mentioned. It's the most used (and recommended) in Rust but there are others. Rust is a young language so you can look at what C++ or Unity is doing.

1 Like

Excellent answer leudz, I'm marking this as the Solution.

I do have one more question if you don't mind:

You mentioned arrays are a bad idea, may I ask why? I'm ready to refactor my code but I'd like to know technically why and if vectors are a better solution.

Thanks again leudz, you've been helpful.

Rist doesn't accommodate arrays very well, especially when they are larger than 32 elements. So they aren't used all that much.

The only traits that are imemented for arrays larger than 32 elements are Clone and Copy. Other traits like PartialEq or AsRef are only inplemented for arrays of up to 32 elements.

1 Like

Understood thanks, I'm switching to Vectors then! :slight_smile:

First they are on the stack, this means you'll have to adjust the size of your stack. By default Rust will give you 2MB but your game will most likely need more.

Currently you are doing entity_manager.entities[0].components[0], but that's because you only have very few, you'll probably add a length fairly quick, you already have a capacity so why not use a Vec, it already does all that for you.

Plus imagine how many Pos you are going to have as oppose to, for example delta_time. With MAX_ENTITIES you're wasting a ton of memory.

There are probably even more reasons =)

1 Like

It makes sense. Especially what you said about MAX_ENTITIES. I appreciate the answer!

I'm going read some other example code and refactor my code.

Thanks again.

1 Like