Guidelines for determining what should be a component

Started by
38 comments, last by Zipster 6 years, 7 months ago

Background:

So I'm developing my first 2d C++ fighting game (for learning purposes) and I have a setup where I have 'components' which are really just data holders with maybe some simple utility functions such as GetComp(), AddToComp(), etc. All components get passed to component systems which are just functions which act on certain components (e.g. MovementSystem(PositionComp pos, VelocityComp vel), etc.) which are split up into their respective domains (Graphics, Animation, Physics, etc.). All components are held by 'Fighter' class which acts as the component holder that keeps everything together.

Issue:

The problem I'm having is I'm not sure how I decide what exactly should be made into a component vs what shouldn't. For example, for my animations I have a system which essentially takes a 'spriteComponent' and a 'animationClip' obj like so SetAnimation(spriteComponent sprite, animationClip anim). As it stands, my animationClip class is not considered a component and is not currently attached within my Fighter class, though I guess I could implement it that way. How do I decide what classes/types should be made into components? How is this typically handled in other ECS (entity component) like systems?

Advertisement

I'd default to making as few things components as possible, and prefer passing parameters to the components.

Think of it like this - what does a visual system need from its component to function properly? If nearly every visible entity requires an animation, then the entities that have that visual component should have the animation passed into that component. (I'm using "visual" as a concept, not a system name).

Anything that can be parameterized probably should be, though I'm sure I could think of exceptions. Most programmers recognize that Composition is more often preferred over Inheritance. I think when it comes to Entity Component Systems (ECS), composition and parameterization of components is more often preferred over more components.

Don't forget that you can make some components depend on the existence and data of other components - pass in a reference or ID as a parameter.

Why, for example, are Position and Velocity part of two different components? It's like separating every variable of a class into its own component - but for what benefit? Even walls can have velocities - just set the velocity to (0,0). And what about size and collision rects? I'd have them as part of the same component. If an object doesn't collide, for example, I'd just set a bool or set the collision rect to (0,0,0,0).

Think of components as structs, not solo variables. They are bundles of variables needed for the System to do its thing. Sometimes a system needs the data separated in more than one component, but they shouldn't be unless actual concrete benefit is gained by their separation (and theoretical future benefit doesn't cut it).

I'm not familiar with your particular code, but another thing that seems odd is that there is a Graphics system and an Animation system. What does the Animation system do?

 

Disclaimer: I've read buttloads about ECS systems, but don't have any practice/experience with them, as none of my projects have needed them yet. I intend to move to ECS when I move to 3D games, but don't find them necessary for any of the 2D projects I've worked on.

 

Thanks for the reply. I think this:

Quote

Think of components as structs, not solo variables. They are bundles of variables needed for the System to do its thing. Sometimes a system needs the data separated in more than one component, but they shouldn't be unless actual concrete benefit is gained by their separation (and theoretical future benefit doesn't cut it).

really hit the nail on the head. I'm kinda like you where I've read up on ECS systems and game engines but again this is my first time trying to implement these things into an actual project. 

Right now my animation system really isn't doing a whole lot lol. I know that, from what I've read, animation and graphics are usually very tightly coupled but I would still see them implemented as 'separate' systems in some code samples so that's why I'm starting out with two separate systems. It helps me reason about things better while I'm learning. Basically my animation system is just setting an index from a 'spritesheet' texture to a spritesheet component. The spritesheet comp holds the texture's current UV coordinates and has a SetUV function which set's the UV coords for whichever index is specified. In the graphics system I then just check that spritesheet component's current UVs set and renders the image found at those coords. More refactoring required but a good start for me :-> 

These are slightly off-topic from what you are currently reasoning over, but I've written some of my thoughts on common ECS design pitfalls here and here, and you may find them worth a read. I haven't mentally resolved every aspect of how I would design an ECS, but I've come to a few conclusions about how I wouldn't design them, basically by observing what I think others do wrong. Again, I should stress that I'm no expert, I just play one on TV.

ECS is a design pattern, and like any other pattern, you should not (ab)use patterns to "design by numbers". Patterns are not a play-book, they're a vocabulary so that it's easier for us to communicate with each other.

Questions like this boil down to: I've picked a pattern to use as my solution, but I'm not sure how to fit my problem into it.

Also, Entity Component Systems are not really a recognized design pattern outside of games... and it's not even popular within games. IMHO it's just an ad-hoc reinvention of the relational model.

To answer the question though: If you're using ECS, then everything is a component.

If you're using OOP, the guideline on when to break an object into two is called SRP.

10 hours ago, Servant of the Lord said:

Why, for example, are Position and Velocity part of two different components? It's like separating every variable of a class into it's own component - but for what benefit? Even walls can have velocities - just set the velocity to (0,0). And what about size and collision rects? I'd have them as part of the same component. If an object doesn't collide, for example, I'd just set a bool or set the collision rect to (0,0,0,0).

Finding the right degree of separation is not a simple task, as you have laid out. If you have Position, Scale, Rotation, Velocity, MoveSpeed, Collision, Size, ... as components, you end up having to add a fuckton of components to a single entity, to even make it work. On the other hand, if you end up merging too many responsbilities into components, you lose every benefit that ECS even has.

I personally strife for a middle ground - for example, there's a Transform componen that consists of position/scale/translation, and supports inheriting it from parent entities. Velocity isn't part of this component though, as it doesn't make sense for me - just as little sense as a "velocity" component - instead there are dedicated components that deal with velocity, ie. a Move-component, which contains configuration variables (move-speed, play animation while  moving, ...) but also deals with the velocity part; while on the other hand there could be a "physics" component which applies a velocity on its own.

Thats probably my conclusion - do not strife for declarative components like "position" or "velocity", but instead for functional ones like "Transform" and "Movement", and strife to do othe rest via variables; this should give a decent compromise.

@Juliean Fantastic, that helps a lot. This seems to be inline with what @Servant of the Lord was getting to as well, which was to not make components too granular. 

@Juliean Let me ask you though, how do these kinds of systems usually allow users of the engine (typically implementing the game logic layer) to access and set the values of the component variables? Are users typically given knowledge of this underlying engine component or do they typically manipulate these values indirectly via the containing entity class member functions or some other mechanism?

7 minutes ago, boagz57 said:

 Let me ask you though, how do these kinds of systems usually allow users of the engine (typically implementing the game logic layer) to access and set the values of the component variables? Are users typically given knowledge of this underlying engine component or do they typically manipulate these values indirectly via the containing entity class member functions or some other mechanism?

If you want to set the values of the components, then you should generally do this directly on the components, yeah. Like that:


auto& move = entity.GetComponent<Move>();
move.speed = 100.0;

For more complex operations you can eigther give the components methods, or add those to the systems and let the user access & call them as well. I'm doing this ie. for my move-commands:


void MoveSystem::MoveTo(Entity& entity, Vector3 vLocation)
{
  auto& move = entity.GetComponent<Move>();
  auto& transform = entity.GetComponent<Transform>();
  
  // ...
}

Those actually take an entity, but you could also just pass the components - I cannot give a clear advice on that, since I actually use a visual-scripting system for gameplay-logic, which has a specific set of requirements. For example, components do not exist as distinct types here, and I just add a "SetEntityMoveSpeed(entity, speed)" node to achieve the code above. Which might go against what I originally suggested, but its really just of how my system works.
Thats another general advice that I can give, see what works for your case, based on real-life observations. Something that might theoretically be the better/cleaner design might be a pain in the ass to work with, especially on a game-logic level.

Quote

If you want to set the values of the components, then you should generally do this directly on the components, yeah. Like that:

Haha, this is exactly how I was feeling when you were describing just manipulating components directly "yeah.....no?" Obviously because ever since you start programming your always taught to encapsulate and data hide and all that. But I think your final sentence explains it all

19 minutes ago, Juliean said:

Something that might theoretically be the better/cleaner design might be a pain in the ass to work with, especially on a game-logic level.

Sometimes the cost of bending/breaking the rules a bit is far less then adhering to them. Maybe this would be one of them given components are really theoretically just data containers and high level code is easier to deal with.

Though I will say then when it comes to my systems I like the idea of trying to implement no side effects by either just taking copies of components within systems and returning new components or taking const references and just pulling out the data I need from the components and again returning a new component. 

 

This topic is closed to new replies.

Advertisement