Recently I’ve been looking into ECS and Data-Oriented Programming and I have been trying to figure out how to conceptually implement an ECS. Especially how unity implemented their ECS (Because it's so freaking fast!). I could’ve done more research but in my experience diving right in is more fun. Obviously the first ideas are not going to be correct but hey, I enjoy thinking and designing systems so why not?
Edit: Ok so I fixed up the explanations up a LOT so that they make more sense. Now a personal note, although this design works, it is horribly bad in terms of performance, one reason being... lists take up twice as much CPU cache. Arrays are always superior.
If you don’t know what ECS is Read this Wikipedia page.
I really learned a lot about ECS and Data-Oriented design from reading the bitsquid engine blog and the t-machine blog they were part of the inspiration for this blog. Although t-machine is not as low-level and data-oriented as me or the makers of bitsquid, he had the best ECS and networking explanations I have found. And his posts have good humor to them. (Another amazing blog is the VoxelFarm blog, one guy making an endless procedural terrain that generates to the visible horizon)
The one place I have learned the most from though is taking Computer Science. I highly recommend this for anyone serious about programming. You can take “cs50’s introduction to computer science” on edx.org (pronounced ed X). Single-handedly the place I have learned more about computers than anywhere else.
Now back to my first conceptual version of ECS, it was pretty bad. (Note I did note actually code this, so I made some bad assumptions)
I knew that for data-oriented design I should make the components hold the data and somehow “connect” the Entities to them. So I did what I thought was obvious I defined the entity’s as a struct with an int ID and a reference to a linked list of its components.
Now Entities aren't explicitly their components data but simply hold pointers to a list of components.
The code for an entity would look something like this:
I then made a structure containing lists of components by type. This is slightly redundant, but it will help us access all specific components really easily. (like all the renderer components for rendering)
I then made a structure to hold the Entities:
Archetypes in this case are Entities with specific components, like the player would have a playerHealth component, causing it to be stored in its own archetype, and the enemies have an enemieComponent causing them to be stored in their own component.
Yay! Ok now, how do you, let's say, render an entity? We can go through the components structure and find the list of renderer components. This will be O(n) but that's ok since we will only do this once. Then we can iterate over all of the renderers and render them every frame... But wait, we also need to know if the entity has a position component (or a rotation component, or a scale component) to know where to render it!
Alright we could check if an entity has a pos component by holding a pointer to our parent entity in the component. No I don't like that, since every single component will need to define a pointer to its owner. Instead, we could just have a pointer to the pos component from within the renderer component, so now every entity with a renderer component must have a position component.
But now we have a problem, when we instantiate an entity we have to set up those pointers, and if we want to instantiate thousands of entities or load a scene (which is pretty much the same) it becomes very slow... for a small game this may be ok, the performance for it is not that bad as long as your not trying to load some ridiculous number of entities.
At this point I am not happy, I am obsessed with performance, and I have to have super fast instantiation! Ever since I watched a Unity talk about their DOTS instantiating 100,000 entities in just 9ms(!) I had to figure out how they did it!!! Here is a link to that talk.
We can instantiate Entities but not extremely fast, ok sure.
I don't even remember if I tried to keep working on this ECS version, but I realized it wasn't going to work. I hope you readers have learned something from this, like I did, and look forward to a post on a fundamentally different approach to ECS. I found out it's much like a database, with archetypes being like tables in a database. See you there!
Comments
Post a Comment