Entity-Component Confusion

Started by
28 comments, last by Tangletail 9 years, 1 month ago

Alright, So I am now programming the entity and components. I know what the general Idea of it is.

A number of entities in an array.

And components are stored in some form of way.

The entities are just simply structs with set data in them, as in...

BaseID: Assuming the engine wants to instance from a database. Or in the case that this would matter some way.
UID: The instanced ID.

And that's pretty much it for entity.


The part that confuses me... are components.

Confusion 1:

Lets say some arbitrary component had been defined some form... some way. It has a system that processes it when the time comes. I got that much.

What confuses me though is how do you store the component in an efficient way? My understanding of the entity-component system is that it simplify things, and reduces cache misses, right?

So far... from what I can gather from studying Artemis is that the components are stored in maps. Aren't hash tables less efficient for the cache? Given that it's normally scattered around the memory?

For simplicity sake, I can see it working in a loop. You simply pass the entity's ID into the map, and the managers will process information.

I guess the next one is an array for storing components... but I see problems here.

Confusion 2:

Say components are stored in arrays... like I have been noticing on every other forum I searched. Don't these positions change frequently as your code updates things?

I.E. When a player shoots a ship, and the ship is now dead. It should be removed. So it's flagged as such, and at the end of the loop, it gets removed from the list, and everything gets shifted down to keep data clustered tightly.

For a game with everything having every single component. I can see this being ok, unless threading says no. But what about every entity having only components it needs? Static geometry only containing physics, position, model, and material. While an AI Agent would need, update, position, model, and material?

That would mean that everything in the array suddenly gets whack. And you got a wall that is able to shoot for some reason. (Yes... that happened)

Right? But what about storing them with the Entity?

Confusion 3:

An array will not conform very well with this. As the array will only hold a predictable amount of data. So that's out of the bag. Lets say we do use std::list<> then.

If memory serves correctly, that is actually a linked list. Which would be entirely counter productive when you are trying to keep the amount of needed pointers down in the code. Not to mention memory is still scattered around.

Does anyone have any advice?

Advertisement
1
Does Artemis actually say that it is a solution for optimising your game?
The "ECS" phrase originally popped up as a solution for flexibility and empowering game-designers. Only recently have people started making ECS frameworks with an eye to performance optimisation.

Some of the older ECS frameworks I've used had horrible performance, but you put up with it because you wanted to use the other features (which at the time was basically writing OOP from an XML file instead of C++ code :()

2
It's common to not compact arrays when items are removed, and instead have two extra arrays/lists of indices. One contains the indices of valid array elements so that you can iterate through all the items in the array, the other contains the free elements so that you can allocate new items.
At this point you're basically dealing with a pool with a free-list, not a basic array.

3
The memory requirements of a game are completely predictable, which means it's quite feasible to use fixed size allocations instead of growable ones.
If that's too hard, a std::vector is still probably a better choice than a std::list though!!


So far... from what I can gather from studying Artemis is that the components are stored in maps. Aren't hash tables less efficient for the cache? Given that it's normally scattered around the memory?

I just looked at the source code, and the components appear to be stored in arrays.


Does Artemis actually say that it is a solution for optimising your game?
Actually, original Artemis doesn't stores components in maps. I know Ashley (libGDX sponsored ECS framework) does, and in fact gives pretty crappy iteration performance. Artemis, its most popular fork artemis-odb and my own fork, all store components in arrays.

What Artemis did is have a component manager storing components in an array of arrays of components. So each slot in the array would correspond to a component type, and the array in that slot would contain all the components of that type for all entities that had them.

Thats why if you look at the source, you got a Bag of Bags of components. For retrieving a component you'd do componentsByType.get(componentTypeIndex).get(entityID). Thats esentially componentsByType[typeIndex][entityID]. Array of arrays of components.

Now, Artemis is made in Java, so the thing is, its not arrays of components but arrays of pointers to components. Yes, this gives bad cache locality*, but thats all you have in Java unless you want to go the way of the ByteBuffer and pretty much implement array of structs by hand, which is only viable if a) You have a lot of experience with this stuff and know exactly what components will get processed often in which groups or b) You already have a semi complete game and can profile to see what components are worth having close to eachother.

I'm not a performance guru but I advocate for the following: Just do plain arrays of pointers to components. Right now it will probably be enough for whatever use case you have, and its something you can perfectly optimize for later when you have enough test cases to try. Its easier to optimize for a specific target rather than thinking right now months (or years!) ahead what your use cases might be in the future to warrant such optimizations.

Moreover, having array of pointers to components means that you don't need to do any complex entity -> component mapping. Just have an array of pointers, and to get a component just index into it with the entity's ID. ie:

Spatial* sp = spatials[entityID];
Velocity* vel = velocities[entityID];

This means that you'll have one indirection when you access the component BUT the important point right now is have something working and nail the ECS way of doing things. Systems shouldn't know the particulars of how components are stored, so you can come up with an interface for the systems to retrieve components, and resolve the way they're stored behind their backs so to speak. That way if in the future you want to optimize further by storing components contiguously or compacting their arrays, you can do so transparently.

* Actually it depends of the GC algorithm, if the component objects are discovered by the GC pass via said array, they'll get copied to an older generation one next to the other. Which helps a bit.

"I AM ZE EMPRAH OPENGL 3.3 THE CORE, I DEMAND FROM THEE ZE SHADERZ AND MATRIXEZ"

My journals: dustArtemis ECS framework and Making a Terrain Generator

The main issue is that I am writing in C++, and most of the actual game logic is going to be done with LUA. I'd like the engine to be modular, and easily modified when needed to be. So... it's basically turning OOP onto it's head to a degree. Think Dungeon Siege.

The plan is for the engine to support a massive number of items on the screen at once, so... cache performance is kinda needed, when DirectX doesn't have to much problems with rendering thousands of objects. And each item can be scripted if needed, or be skipped if it doesn't use a script.

My previous work with C++ and games... tell me that pointers can get nasty if there are to many, simply because debugging because painful. Especially when you can't always track where something is in the memory, and when it changes.

I'll take a look at Artimis-obd though. I might be able to port it into C. In the mean time. Any other ideas?


The main issue is that I am writing in C++
Look at this C++ port of Artemis?

"I AM ZE EMPRAH OPENGL 3.3 THE CORE, I DEMAND FROM THEE ZE SHADERZ AND MATRIXEZ"

My journals: dustArtemis ECS framework and Making a Terrain Generator

Huh... didn't see that one. Quick question though. What is a "Bag" this is actually the first time I've heard of this storage method.


What is a "Bag" this is actually the first time I've heard of this storage method.

It's a class in the Artemis framework. Read the source code. The comment says:

Collection type a bit like ArrayList but does not preserve the order of its entities, speedwise it is very good, especially suited for games.

The main issue is that I am writing in C++, and most of the actual game logic is going to be done with LUA. I'd like the engine to be modular, and easily modified when needed to be. So... it's basically turning OOP onto it's head to a degree. Think Dungeon Siege.

ECS is currently a trending fad in the hobbyist world of game development, and while it is a useful tool in your belt, it’s only really useful to you if you know how to wield it.

I’ve already discussed this here and here:

Anyway, based on what you've said and some more thought of my own, I think I'm going to go with an OO/ECS hybrid; utilizing classes for systems of behavior that don't work so well when split up, and supporting components for attachable/detachable behavior where it makes sense (for example, power ups in a racing game would be a great candidate for utilizing components, while the actual driving mechanics might not be).

This is exactly the correct approach. If you scour the forums you will find many cases where people have been exposed to ECS (typically through Unity 3D) and immediately just go full-on ECS without really considering what its real benefits are, especially given the gap between them and the makers of Unity 3D.
If there is anything a programmer needs to know about programming, it’s that there are indeed many approaches to any given system, but there are no single best answers. Every solution should be taking the benefits from one approach and the benefits from another approach.

You might have thoroughly thought about why you’ve decided to go this route and how ECS can benefit you, and maybe you have a tool-chain and scripting language in-place (you seem to have scripting at least), or you might be in the camp with those who just think, “it’s what they do these days, so I will do it too,” (it’s not really what most industry professionals do).

If you haven’t really understood the reasons to go ECS, you should stop and research more and reconsider.

That being said, using it with LUA (a LUA component) could be very powerful.
On the other hand that can be done just as easily via object-oriented programming, while maintaining better overall engine organization (ECS makes it extremely easy to violate a lot of coding practices that otherwise make your code clean, modular, and safe).


In the end, the best way to know if ECS is right for you is to consider how, in the future, you plan to add and initialize all these components to objects.
If you’re planning to have set-up code that adds hard-codedly and initializes components, you are doing it wrong and ECS has no meaning for you.
You have to at least have a fairly complex file format, a good parser, an interpreter, and hopefully a good tool for creating said file format.

If you are not planning on really putting in effort to make these things, ECS has no meaning for you.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

I've already researched a lot of different methods before coming to ECS. And from what I can understand the flexibility is what I needed for the project I had in mind. Not to mention it so far has made certain things easier to do... and interesting at the same time.

Wall that is able to shoot and move around due to a weird glitch with the component arrangement. It was a constantly reproduced bug, so I took a look at what was going on in the memory, and there was the problem in the loop.

It actually further set the idea for me, as that glitch really made a funny feature for me. Which... I may actually explore a little further. But first things first is to get the system working properly.

My end goal is that the engine can be easily modified, with the actual low-levels only being touched to add features to the engine, or make updates and improvements. Other wise, the engine will be a dumb fired fire-and-forget sort of system.

With luaJIT for faster iteration times. I'm not to bothered at the moment with having scripts automatically reload. I'm more concerned with getting things working before features.

This topic is closed to new replies.

Advertisement