Nested components within ECS? Good idea?

Started by
12 comments, last by CrazyCdn 5 years, 10 months ago

Hello forum dwellers!

I've recently decided to dive into using ECS, as it looks like it solves so many issues with game development, especially with the game I am currently making (which involved many complex entities seamlessly interacting at the same time). It all seemed relatively straightforward. So far, I understand that:

1.) The entity is simply a container for components (which can also have an identifier)

2.) The components are interchangeable blocks of data which change the way the entity will behave (only containing data - no methods/functions/logic)

3.) The systems loop through all the entities and checks to see which components they contain. If it contains the appropriate components, act on them.

So far, this has been easy to implement and worked for the basic design. For example, I have a player, block and a ball. The rendering component is present in all 3, colliders in all 3, physics in the player and ball, and input only in the player. I recently looked up how components interact with one another, and that also seems simple. For example, if I want a player to animate due to a button press, I would create a system which requires a rendering component AND input component. The problem I seem to be having trouble understanding is exactly how to nest more complex entities and components.

For example, I want my player to be able to punch the ball around. If the player presses the "E" key, I want to create a fist which punches to the left (drawn over the player without the player animation changing). However, the fist itself punching the ball technically has a sprite that needs rendering and also a collision box, so should I create a "fist" component which contains 2 components (rendering and collision), or should I generate a new entity which contains those 2 components and let the systems act naturally on it? I feel like by nesting components within components, I end up with this spaghetti code where systems are calling other systems, removing the whole point of an ECS. It feels more "natural" to just leave the systems to wait and act on entities which contain the correct components. However, if I create a new "fist" entity, this needs to be tied to the player, so I ended up with an entity containing an entity which also seems ugly, or a weird entity with some kind of reference of linking ID to its creator. Am I missing something?

On another note, is it unsafe to add multiple instances of the same component? I've heard that it's bad practice too. For example, I might have entities which all require rendering, but some might want to use animated sprites, multiple layers of sprites, shaders, etc. Is it better to have multiple rendering components which can optionally interact, or possibly use a rendering component interface which multiple rendering components can inherit from? Is this inheritance not defeating the purpose of using an ECS in the first place?

 

Any help would be greatly appreciated!

Thanks, Zuhane

Advertisement

ECS if taken simple can be thought simple so to answer your last question, it depends! If you have a rendering system that takes SpriteComponent instances then you could attach multiple of them to the same entity but having a system that relys on a strict 1 on 1 coupling of components, any system that takes data from component a and places processed data in component b, then you should avoid using multiple instances of the same component on a single entity.

I never ever would let people in my team nest components into each other as components are simple data, they can't have nested components as those would break the ECS chain. Your problem/approach needs to target a different system, Render Graph. In almost every modern engine these days, you have some kind of Render Graph built-in that handles object hirachy and scene contents so if you want to display something on action then add a new entity to the graph.

The graph is also the first and only source for render systems as it contains any entity that is intended to be known in a scene and decides which entities are visible. It is as well a system as same as a data source.

Why render graph?, just becuase it handles parent-child relations of entities or rendering relevant components and makes the game more easy to switch entire entity trees to visible/invisible for rendering

Nested components can only make your system unnecessary complicated and adds nothing else, think about the worst nested components hierarchy can be another non-ECS inheritance tree. So avoid it.

In you example, fist is just an entity, not a component.

https://www.kbasm.com -- My personal website

https://github.com/wqking/eventpp  eventpp -- C++ library for event dispatcher and callback list

https://github.com/cpgf/cpgf  cpgf library -- free C++ open source library for reflection, serialization, script binding, callbacks, and meta data for OpenGL Box2D, SFML and Irrlicht.

16 hours ago, zuhane said:

On another note, is it unsafe to add multiple instances of the same component? 

Try telling any non-games programmer that having two fields of the same type in a table/object/structure is harmful and watch them laugh at you. 

There's a reason that 'Entity' frameworks in general (the inheritance kind, the composition kind, and the ECS kind) have never caught on outside of gamedev - they are unnecessarily restrictive on architecture, and are usually ad-hoc versions of paradigms that already exist anyway / solutions to problems that were already solved in better ways.

Entity frameworks are good for gamedev because we make really complex things, usually without too much architectural planning (and instead, lots of iteration and game-design changes), with large teams making different parts that have to somehow work together seamlessly. Putting an overly restrictive and generic architecture in place in this kind of chaotic environment can help a product come together... Even if it's laughably wrong. If you're making a framework for you, there's no reason to adopt dogmatic restrictions that you don't need to. 

FWIW though, many EC Frameworks support multiple components of the same type, e.g. entity.GetComponent<FooComponent>(index). 

ECS much more closely resembles classic relational databases (whereas most EC frameworks resemble classic OO) - so multiple components should be fine. In RDBMS terms it would be a query like: SELECT * from FOO where 'entity_id' == 42, and this could return any number of results. Or if you asked to select all the FOO's and join with the position data that belongs to their linked entity, you could get multiple FOO's per entity, with that entity's one position just fine. If an entity had multiple FOOs and also multiple positions, then the join query results would get a bit wacky though... You would need each FOO to also contain a position index to link it to the right one. 

16 hours ago, zuhane said:

For example, I want my player to be able to punch the ball around. If the player presses the "E" key, I want to create a fist which punches to the left (drawn over the player without the player animation changing). However, the fist itself punching the ball technically has a sprite that needs rendering and also a collision box, so should I create a "fist" component which contains 2 components (rendering and collision), or should I generate a new entity which contains those 2 components and let the systems act naturally on it? I feel like by nesting components within components, I end up with this spaghetti code where systems are calling other systems, removing the whole point of an ECS. It feels more "natural" to just leave the systems to wait and act on entities which contain the correct components. However, if I create a new "fist" entity, this needs to be tied to the player, so I ended up with an entity containing an entity which also seems ugly, or a weird entity with some kind of reference of linking ID to its creator. Am I missing something?

Fist is clearly an entity, so you should generate a new entity that contains 2 components. Player itself must be able to relate to the fist.

To relate those two, there are multiple ways, including your ECS method. Traditionally, with normal entity (game object) with non-component based model (either class mixins or bloated inheritance), it's done inside the game object class, which is the Player. In hybrid component-based model, it can be done inside a component that is attached to that Player class or just like the traditional one you can still do it in the game object. In Unity classic model it's even more restrictive because everything has to be inside a component. Now in your case with your ECS method, it gets even more complicated, as their interaction data should be defined on a component attached to a player, but the interaction of those two, is dictated by a (new?) system.

When your entities got tons of complex behaviors inter-connected to each other, which usually always will in the end after you're done defining your entities, you can already guess where you're going, hence this question. The reason is because "pure ECS" is too restrictive and now you're stuck to define the interaction, which is logical. Again that doesn't mean it doesn't work, but you already realized yourself where this is going to.

16 hours ago, zuhane said:

On another note, is it unsafe to add multiple instances of the same component? I've heard that it's bad practice too. For example, I might have entities which all require rendering, but some might want to use animated sprites, multiple layers of sprites, shaders, etc. Is it better to have multiple rendering components which can optionally interact, or possibly use a rendering component interface which multiple rendering components can inherit from? Is this inheritance not defeating the purpose of using an ECS in the first place?

I'm not sure why you need multiple instances though. Based on your example, I don't see why multiple usage would require multiple instances of the same component. Inheritance doesn't defeat the purpose of ECS, it's that ECS method that defeats the purpose of inheritance I think.

I think, using nested components sounds more like OOP to me. 

http://9tawan.net/en/

Thanks for the replies. I feel like they've helped, but that I still lack a strong understanding of how this works. I was under the impression that this paradigm was untouchable, but it only seems marginally more useful than inheritance, with its own laundry list of problems. I just wanted to clarify - are you saying that a complex object full of objects (a player with an inventory, for example) needs to contain a component which then tells the system to create a new entity? 

Does anyone know how you'd manage something which can be 0 to many? For example, a list of inventory objects attached to a player?

11 minutes ago, zuhane said:

but it only seems marginally more useful than inheritance

Please note that ECS is not a solution for inheritance hierarchies -- it's two steps removed from that. I know every single article on ECS starts off by showing some game code that's bad because it has too much inheritance and then "solves" that problem by switching to ECS... But... OOP has a rule that you should favor composition over inheritance. If code is based around lots and lots of inheritance, it's probably very incorrect according to OO theory! The immediate solution to that particular problem is to stop and actually learn how to use OOP properly, so that you can use composition instead of inheritance. Realizing that composition is the answer to your immediate issues, but then leaping to build an entire bloated framework to support a very limited form of composition when it's already a 1st class language feature, is a bit of an overreaction, to say the least.

Those kinds of ECS articles are the equivalent of a TV commercial showing a bunch of slow-mo black and white videos of people trying to drill screws into wood using a hammer and getting frustrated, and then colorful videos of people using an electric screwdriver to quickly get the screws in. The electric screwdrivers that they're selling aren't necessarily bad... but the hammer isn't bad either. They just decided to show it being used incorrectly. If you go and throw out all your hammers after seeing that commercial for an electric screwdriver, that's throwing the baby out with the bathwater...

3 hours ago, zuhane said:

are you saying that a complex object full of objects (a player with an inventory, for example) needs to contain a component which then tells the system to create a new entity? 

If there should be entities that would interact with the other entities, then yes. If inventory is just data, then no, as it should be just collection of data in your inventory component. It's usually both though. You need inventory component (if you will) to store each item information/data in the inventory, and use it whenever you wanna display it, like guns to select. In your design, since entity is nothing but numbers, component is nothing but data, then system should be the only one that would deal with the inventory list owned by the entity, including spawning new entity(es) if needed.

How you will inter-connect that with other entities and how these may affect the complexities when the game scales up, I honestly do not know cause I never scale a game architecture like that until today, but I can only smell it due to the restrictions given by such design.

Quote

Does anyone know how you'd manage something which can be 0 to many? For example, a list of inventory objects attached to a player?

I think that sounds about right already. It shouldn't be too complex I believe (not in terms of saying it should be easy, but in terms of it shouldn't be over-engineered to explain direct relation of 0..* or 1..*). Player has zero to many inventory objects stored in Player's inventory list. I think that's one way to phrase it. How you derive it to your architecture (or other architecture), you can re-read my previous post, paragraph 2. For example, Player is-a game object (entity) that has-an inventory list containing inventory objects, for object-centric model. Another example, Player is-a game object that has-an inventory list component that its job is to store inventory objects, for entity-component model.

The inventory object should only store information about the object, which when you want to use it can either be just data to exchange or something to display. When it comes to something to display, it may contain information to display it in form of new entity.

4 hours ago, Hodgman said:

Please note that ECS is not a solution for inheritance hierarchies -- it's two steps removed from that. I know every single article on ECS starts off by showing some game code that's bad because it has too much inheritance and then "solves" that problem by switching to ECS... But... OOP has a rule that you should favor composition over inheritance. If code is based around lots and lots of inheritance, it's probably very incorrect according to OO theory! The immediate solution to that particular problem is to stop and actually learn how to use OOP properly, so that you can use composition instead of inheritance. Realizing that composition is the answer to your immediate issues, but then leaping to build an entire bloated framework to support a very limited form of composition when it's already a 1st class language feature, is a bit of an overreaction, to say the least.

Those kinds of ECS articles are the equivalent of a TV commercial showing a bunch of slow-mo black and white videos of people trying to drill screws into wood using a hammer and getting frustrated, and then colorful videos of people using an electric screwdriver to quickly get the screws in. The electric screwdrivers that they're selling aren't necessarily bad... but the hammer isn't bad either. They just decided to show it being used incorrectly. If you go and throw out all your hammers after seeing that commercial for an electric screwdriver, that's throwing the baby out with the bathwater...

Thanks for the reply. I like the analogy too haha! I do have to ask, though, is there a specific resource you could recommend to help with this? I'd say that at this point I have quite a lot of experience with using XNA and C#, a degree in comp science, and I'm always reading up on programming patterns and paradigms, but even after all of these years, I find it nearly impossible to make anything more complex than a very simple arcade game. As soon the the code starts to expand, it becomes too much for me to store in my brain at any given time, and the smallest changes require so much paper-trailing that by the time I find the source I've forgotten the problem. It's very frustrating, as every time I start a project I can code very quickly, then I start to lose steam and eventually grind to a halt. I can create all the individual components, but tying them all together eventually leads to this huge stressful problem where I can't even begin to modify features or fix bugs without creating more.

I was hoping that ECS would help with my specific style of game (a separate one I've worked on for years), but I feel like there are so many factors involved, such as ordering, inter-dependencies, nested objects, etc. Is it commonplace to simply keep refactoring? I feel like I've got so far (and been through 2 prototypes) and that throwing it away and starting again would be a pretty big deal at this point.

This topic is closed to new replies.

Advertisement