Designing an ECS for Catos

Home

Why choose ECS over EC?

Originally i wanted to use ECS from the start for catos. However after finishing Atomus and the Reflection system I choose EC.

/// sudo code of how an component would function in an EC env.
                class TransformComponent : Component {
                
                        public:
                            void init() override {};
                            void update() override{};
                            void destroy() override{};
                        private:
                            Vec3 position;
                }
                

Now I really liked this syntax (and still do), but when starting on my scripting implementation I realized that I really disliked how to whole flow of: cashing a different component to use later, in the update function actually do logic.

EC is error prone (IMO)

Lets say you made an component for your player, in the init function you would need to cash all the components which you would need to do logic for the player. Now first off all you would need to check if the entity even has the components needed for the player component. Now you could just do this check in the init function, but what if a component gets removed from the entity? Then you would be asking for data from a component that doesn't even exist! Now you could go and give every entity the same base data, such as transforms, rigid bodies, etc (as seen in cave engine ). But what if you want an entity that does not need all of that data, that would just be a waste of memory!

So what does ECS do different?

To put it really simple: ECS splits the data and logic of a component from the EC design.

So instead of doing what you would do with EC you would do:

// I use a struct here because I prefer using structs for structures that only hold fields.
                struct TransformComponent {
                    Vec3 position;
                }
                
                class System {
                    public:
                        void update(TransformCompont& tr, OtherComponent& o);
                }
                

Now after defining the TransformComponent you can create different systems that loop over different components.

Lets go back to that example about the player component. Here you could just create a new system, then tell the scene that you would like to iterate over components like the rigid-body and transform component. Then when the scene is updated your system gets every entity that has a rigid-body and transform component. So now if a component gets removed that your system needs you would not get any issues since the scene does not pass the entity to your system since it does not meet your given requirements.

now this does not mean that ECS is better then EC. In fact I would argue that in allot of cases EC is the "better" choice, because lets be realistic: if you're just making a simple game you would probably be able to handle checking and managing if components exist or not. And then having to write a system for everything would really be a pain. Now that I've said that I would still like to note that I prefer ECS (both because of the problems it solves and of the workflow it brings). I love the games that are extremely flexible and I would like my engine to support that!

Lets design an ECS!

My concerns for my ECS implementation.

What Catos needs from ECS.

The Workflow!

//Sudo Setup phase code
                
                struct TransformComponent {
                    vec3 Position;
                    vec4 Rotation;
                    vec3 Scale;
                }
                
                struct LightComponent {
                    vec3 position;
                    vec4 color;
                    int lightType;
                }
                
                class systemExample {
                    public:
                        static void update(TransformCompont& t, LightComponent& l) {
                                l.position = t.Position;
                                // other stuff
                        }
                }
                /// World = Scene.
                World world;
                
                ///Note to self: this could be semi replaced with the help of the reflection system
                world.register_component<TransformComponent>();
                world.register_component<LightComponent >();
                
                /// Give it the components you want to modify.
                world.register_system<TransformComponent, LightComponent>(&systemExample::update);
                
                // entity is just an id
                Entity entity = world.new_entity();
                
// In the update loop of the application.
                world.update(); // all we need ;)
                

Closing Thoughts

First off all: thank you so much for reading! I don't except anyone to sit through this entire essay which is just an basic explanation of ECS. I mostly wrote this to get what I want and my reasons straight in my head. I would also like to say that I have small to none experience with actually writing an ECS, so lots of choices here are definitely subject to change! I also reckon I will write a new article when I actually finish the ECS with a more technical explanation on how it works behind the scenes.