So while looking at some other stuff, I figured I really should get this ECS stuff worked out, since it is not a pattern I have really used in any of my game stuff, or professional non-game software.
But there are many different approaches, and I am not clear on if there is any consensus on wrong and right ways to do things. Especially avoiding performance pitfalls if it was to be applied to large numbers of objects.
-
A detail I keep coming across that I am definitely not liking is the use of maps (e.g. C# Dictionary or C++ std::unordered_map) in the Entity to store the components for that entity, sets to track either entities for a system or components of a type, etc.. Entities are added to and removed from systems when the components change. Then each update, for each system, the system iterates the entities in the set it is tracking. It must also then go through the component map to actually get those component instances, so that can be a lot of map lookups.
Is this a good style, because I keep finding it but really not sure it is one I will like? Having the hash maps and sets definitely not something I like seeing. Often when profiling all types of application its the hash maps I end up finding on the hot paths, sometimes even with integer keys, and generally try to avoid them whenever possible.
I have seen talk of not having an Entity object at all, instead have ordered arrays for each component so they can be iterated in step by systems to get a union (which could also allow some performance gains over OOP entity processing). Is this generally successful? And is there any examples?
One thing I have been thinking of is that if each component instance has an EntityId and is stored ordered by it that such iteration is easy, but then deleting entities will be expensive because it would mean moving every component instance along after it along. Doing so would also invalidate any direct pointers to components I might want to have to avoid searching the component lists for Entity X repeatedly.
Which also brings the question of, what should not be part of the ECS in general?
-
I am wondering what to do about other "objects" directly owned by entities. Should these "child objects" actually be entities in an ecs? I never had these in my entity hierarchy but there is some shared data and logic I handled using other non-entity classes? For example a ship may have several turrets, which do have their own positions, etc. I have generally made a Turret class or such to contain that, what their current target is, shooting, etc, just not one that ever inherited an Entity style class.
Other things make use of this Turret class as well, such as tanks or buildings. An important note is that a Turret can not simply be a component, because an entity might have multiple turrets (ships being the most common). Other examples would be inventory items etc., which I only have in an "entity wrapper" when they are directly present in the world. - Another object I've also kept separate is weapon projectiles. There is only a few major types and thus are pretty different, so I just coded them as their own things. These are much slower than real-life bullets, so can easily get large numbers, as well as a lot being created and destroyed. But in theory could use a common position, graphics, velocity, collision, etc. component as other things.
- Particle systems. Particles have even larger numbers, potentially with dozens existing for every projectile, so I am thinking there is no way those can be part of an ECS? The particle systems though again I am not sure, as they do have an origin position, take part in rendering, etc.
- Terrain tiles. Vast numbers, feels like this should still be its own thing, even in a compact format, it adds up, and can make good use of special render logic.
- Stuff that is not world objects. Should these be part of the ECS? For example I have sound source objects to allow for the position to be updated by moving objects (non-moving and non-looping sound sources can just be discarded and the underlying sound instance will get cleaned up when the sound completes), so that could be a position component along with another component for the actual sound data? GUI elements also come to mind, but I feel that should almost certainly be left in its OOP hierarchy and tree reference structure.