Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411490 Posts in 69371 Topics- by 58428 Members - Latest Member: shelton786

April 24, 2024, 11:51:34 PM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsDeveloperTechnical (Moderator: ThemsAllTook)An Entity Component System with Data Locality in C++
Pages: [1]
Print
Author Topic: An Entity Component System with Data Locality in C++  (Read 2296 times)
Deckhead
Level 1
*



View Profile WWW
« on: May 19, 2020, 12:47:51 PM »

https://indiegamedev.net/2020/05/19/an-entity-component-system-with-data-locality-in-cpp/

I've written this for people moving away from beginner and into intermediate skill in C++. Would love to hear any feedback.
Logged

qMopey
Level 6
*


View Profile WWW
« Reply #1 on: May 21, 2020, 11:09:56 PM »

Decent article, but I do think the templates are really overkill. Also I find it a little hard to figure out how memory is laid out in your ECS. I did figure out it eventually, but really this should be first and foremost. For example, we could say something like this: Each component is stored in an array. An entity archetype is a list of component types, with one component array each. Therefor for each different type of entity there exists one array per component type. Then, a matching algorithm is used for systems to find which entities they can run on, and each component array is traversed linearly.

I do realize your article style is written like a tutorial, building up pieces incrementally, which is totally fine. However, for some readers (like myself) just laying out the fundamental details like memory layout and access patterns up front is great.

Back to the template thing... This is almost certainly not the kind of feedback you're interested -- I get that. The templates aren't needed, and you can delete all the code related to registering and removing components at run-time. In practice that kind of feature isn't very useful, and you can get rid of a lot of complexity here. For example in my game I just use boolean flags to enable/disable things, rather than modifying whether or not an entity has a particular component.

So we can alter the API to get rid of some functions.
  • AddComponent
  • RemoveComponent
  • RegisterEntity

So instead there could be a function to register an entity type, and also one to create an entity given the type. Registering an entity type would require an array of component types, and require each component type to have been registered prior. These types do not have to be templates, and instead can be run-time numbers (integers).

In general I think these simplifications would have some really strong benefits.
  • Lower complexity due to limited to zero use of templates. This makes it easier to understand, modify, fix bugs, and use in general.
  • No run-time changes to entities in terms of adding/removing components. This simplifies the kind of game logic that can be written (a very, very good thing), and simplifies implementation of the ECS.
  • Much better compile time when writing gameplay code -- no need to pull in complex ECS templates.
Logged
InfiniteStateMachine
Level 10
*****



View Profile
« Reply #2 on: May 22, 2020, 12:46:57 PM »

Always interesting to see another viewpoint of ECS. I'm finding more and more that the overall design shares a lot of similarity to relational databases.

Also qMopey. I'm not quite following the reasoning for removing the ability to add and remove components at runtime. Not being able to do that would be a huge disadvantage. Am I misunderstanding what you mean? Like you're more talking about pre-allocating them at load time and recycling them? Can I functionally still dynamically compose and modify the structure of an entity at runtime?
Logged

qMopey
Level 6
*


View Profile WWW
« Reply #3 on: May 22, 2020, 02:25:01 PM »

I'm not quite following the reasoning for removing the ability to add and remove components at runtime. Not being able to do that would be a huge disadvantage. Am I misunderstanding what you mean? Like you're more talking about pre-allocating them at load time and recycling them? Can I functionally still dynamically compose and modify the structure of an entity at runtime?

If an entity can only be constructed if the entity type is also provided (which maps to a list of component types), then there's no need to have any code that supports adding or removing components at runtime. So just delete that code. Having it around will be a liability and encourage it to be (unnecessarily) used in gameplay implementation.
Logged
InfiniteStateMachine
Level 10
*****



View Profile
« Reply #4 on: May 22, 2020, 02:37:02 PM »

Ah so you say this regarding the concept of archetypes in this article? I see now.


The ability to not be able to dynamically build up entities/actors procedurally at runtime makes some tasks virtually non-feasible but I suppose that might just be missing the point of focusing on locality and performance. Certainly not something I'd be willing to give up in practice.
Logged

qMopey
Level 6
*


View Profile WWW
« Reply #5 on: May 22, 2020, 03:46:40 PM »

What's your use case?
Logged
Deckhead
Level 1
*



View Profile WWW
« Reply #6 on: May 23, 2020, 04:49:43 AM »

Decent article, but I do think the templates are really overkill. Also I find it a little hard to figure out how memory is laid out in your ECS. I did figure out it eventually, but really this should be first and foremost. For example, we could say something like this: Each component is stored in an array. An entity archetype is a list of component types, with one component array each. Therefor for each different type of entity there exists one array per component type. Then, a matching algorithm is used for systems to find which entities they can run on, and each component array is traversed linearly.

I do realize your article style is written like a tutorial, building up pieces incrementally, which is totally fine. However, for some readers (like myself) just laying out the fundamental details like memory layout and access patterns up front is great.

Back to the template thing... This is almost certainly not the kind of feedback you're interested -- I get that. The templates aren't needed, and you can delete all the code related to registering and removing components at run-time. In practice that kind of feature isn't very useful, and you can get rid of a lot of complexity here. For example in my game I just use boolean flags to enable/disable things, rather than modifying whether or not an entity has a particular component.

So we can alter the API to get rid of some functions.
  • AddComponent
  • RemoveComponent
  • RegisterEntity

So instead there could be a function to register an entity type, and also one to create an entity given the type. Registering an entity type would require an array of component types, and require each component type to have been registered prior. These types do not have to be templates, and instead can be run-time numbers (integers).

In general I think these simplifications would have some really strong benefits.
  • Lower complexity due to limited to zero use of templates. This makes it easier to understand, modify, fix bugs, and use in general.
  • No run-time changes to entities in terms of adding/removing components. This simplifies the kind of game logic that can be written (a very, very good thing), and simplifies implementation of the ECS.
  • Much better compile time when writing gameplay code -- no need to pull in complex ECS templates.

Thanks for the feedback regarding the layout of memory stuff. I think that's something important that I overlooked. There's a few other improvements I'd like to add, and that's definitely one of them now.

As for the template stuff... yeah, I just don't agree. At least for me, the ability to add and remove components at run time is actually very advantageous for the new game I'm working on. I could do, as you say, with boolean flags, but I'd end up with way too many boolean flags, which would also mean I'm throwing in a bunch of if blocks, and, in my view anyway, a bunch of if statements is much less readable than template types.
Logged

InfiniteStateMachine
Level 10
*****



View Profile
« Reply #7 on: May 25, 2020, 10:13:58 AM »

What's your use case?

Procedurally building up a hierarchy at runtime without knowing beforehand how it will be constructed. It could have many components of the same type (emitters, scene components etc etc). Other things might be transferring ownership of a component, cloning a component to another entity. The list goes on and I can't really think of an engine that doesn't support runtime composition. Which is why I think I'm most likely misinterpreting your post.
Logged

qMopey
Level 6
*


View Profile WWW
« Reply #8 on: May 27, 2020, 06:12:49 PM »

At least for me, the ability to add and remove components at run time is actually very advantageous for the new game I'm working on. I could do, as you say, with boolean flags, but I'd end up with way too many boolean flags, which would also mean I'm throwing in a bunch of if blocks, and, in my view anyway, a bunch of if statements is much less readable than template types.

What's your use-case for run-time?

As for too many booleans, that's totally a fair point, but I do want to point out that Overwatch shipped with boolean flags.

What's your use case?

Procedurally building up a hierarchy at runtime without knowing beforehand how it will be constructed. It could have many components of the same type (emitters, scene components etc etc). Other things might be transferring ownership of a component, cloning a component to another entity. The list goes on and I can't really think of an engine that doesn't support runtime composition. Which is why I think I'm most likely misinterpreting your post.

Those sound like great use-cases, but there's no reason these features can't be built with the kind of API I described. We might be misunderstanding each other slightly, but if I did understand you, it seems like adding/removing components at run-time is just not necessary.

Do you have a more specific example we could entertain?
Logged
Deckhead
Level 1
*



View Profile WWW
« Reply #9 on: May 27, 2020, 09:10:03 PM »

At least for me, the ability to add and remove components at run time is actually very advantageous for the new game I'm working on. I could do, as you say, with boolean flags, but I'd end up with way too many boolean flags, which would also mean I'm throwing in a bunch of if blocks, and, in my view anyway, a bunch of if statements is much less readable than template types.

What's your use-case for run-time?

As for too many booleans, that's totally a fair point, but I do want to point out that Overwatch shipped with boolean flags.

What's your use case?

Procedurally building up a hierarchy at runtime without knowing beforehand how it will be constructed. It could have many components of the same type (emitters, scene components etc etc). Other things might be transferring ownership of a component, cloning a component to another entity. The list goes on and I can't really think of an engine that doesn't support runtime composition. Which is why I think I'm most likely misinterpreting your post.

Those sound like great use-cases, but there's no reason these features can't be built with the kind of API I described. We might be misunderstanding each other slightly, but if I did understand you, it seems like adding/removing components at run-time is just not necessary.

Do you have a more specific example we could entertain?

I can give you a use case in my game I'm working on right now.

I have a component called Highlight which is added and removed to entities based on whether they're currently highlighted or not. This information is used in a few different places, like rendering, as well as logic systems related to the User Interface.

Sure, I could put a boolean flag that says "is highlighted", and a color variable on the Tile component. But I'd also need the same things on the NPC component, the Player Controlled component, the Button component etc.

But it's much easier to simply put it in the Highlight component, so it's not in more than one place; and add/remove to entities as needed. It also means that if I needed to (an arbitrary example for now) add a "glow amount" to things that are high-lighted, I don't need to go source-code surfing to find all the things that should have the new variable added.
Logged

InfiniteStateMachine
Level 10
*****



View Profile
« Reply #10 on: May 28, 2020, 01:15:45 PM »

At least for me, the ability to add and remove components at run time is actually very advantageous for the new game I'm working on. I could do, as you say, with boolean flags, but I'd end up with way too many boolean flags, which would also mean I'm throwing in a bunch of if blocks, and, in my view anyway, a bunch of if statements is much less readable than template types.

What's your use-case for run-time?

As for too many booleans, that's totally a fair point, but I do want to point out that Overwatch shipped with boolean flags.

What's your use case?

Procedurally building up a hierarchy at runtime without knowing beforehand how it will be constructed. It could have many components of the same type (emitters, scene components etc etc). Other things might be transferring ownership of a component, cloning a component to another entity. The list goes on and I can't really think of an engine that doesn't support runtime composition. Which is why I think I'm most likely misinterpreting your post.

Those sound like great use-cases, but there's no reason these features can't be built with the kind of API I described. We might be misunderstanding each other slightly, but if I did understand you, it seems like adding/removing components at run-time is just not necessary.

Do you have a more specific example we could entertain?

I'll try, this may be contrived but for the sake of argument lets say the following:

You have a character editor. The character is the entity. At runtime the player can add different components that introduce different visuals and behaviors. Perhaps they add a gun component or a sword component, then from that they attach various effect components to those weapons. For the sake of further argument, in this case, all the components/behaviors have different data layouts and api's.

To add further to this, interactions with other player entities at runtime can result in these adornments being added, removed, or transferred.

EDIT : To add to this, I'm generally in favor of doing everything as statically as possible at edit time but I've run into situations at work where it starts to get a bit too painful.

FWIW I work mainly with the Unreal engine for my day job an in a way they do try to push you towards defining everything statically and there are some major advantages to doing it this way such as being able to do asset reference tracking at edit time.

That said, they do have a bailout system for when edit time composition is too cumbersome. They call it soft-object references. I avoid them as much as possible since it moves some error checking to runtime and often I can get away without using them but they are sometimes needed.


Logged

BorisTheBrave
Level 10
*****


View Profile WWW
« Reply #11 on: May 30, 2020, 12:09:47 AM »

But it's much easier to simply put it in the Highlight component, so it's not in more than one place; and add/remove to entities as needed.

Or you could have a Highlightable component that these things *always* have, it it has a boolean isHighlighted on it. That has all the advantages you asked for, while still not needing add/remove functionality, and being easier to work with (no worries about adding the component twice, dealing with nulls, etc).


The reason for add/remove is more stubtle than arguments about booleans. Generally, an ECS system make it very efficient to loop over a given component or archetype. So here's an example of where add/remove is useful:

Suppose I have a very large world with tons of zombies in it. But I only want the ones near the player to actually run the AI update step. By adding/removing a component as zombies get near/far from the player, I can sure that each tick, I can use ECS to loop over nearby zombies without having to even inspect all the other zombies. In many ECS engines, I believe this setup can be more efficient than having an isNear boolean.
Logged
Pages: [1]
Print
Jump to:  

Theme orange-lt created by panic