C++ Guidelines for determining what should be a component

Recommended Posts

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?

Share this post


Link to post
Share on other sites

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.

 

Share this post


Link to post
Share on other sites

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 :-> 

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites

@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?

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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. 

 

Edited by boagz57

Share this post


Link to post
Share on other sites

The canonical name for your 'component' is entity or boid (entities with behaviour) if you want to search for further reading. 

The major component implementations that realize the data in the boids are called engines. e.g. graphics engine, audio engine. physics engine

Share this post


Link to post
Share on other sites
12 hours ago, Shannon Barber said:

The canonical name for your 'component' is entity or boid (entities with behaviour) if you want to search for further reading. 

I've only seen boids used as a reference to the algorithm of the same name from the 80's, or a synonym for that style of steering behaviours.

22 hours ago, Juliean said:

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.

This was studied extensively in the 70's and led to the formal theories of data normalisation. That's one reason why I said earlier that ECS is just an ad-hoc rediscovery of the relational model :)

There's so much existing, applicable knowledge and jargon here, but then its 'inventors' can't pretend that ECS is in any way new or novel :D

In my personal opinion though, you can't ever just look at what data you have/need and figure out how it should be laid out. You need to look at how that data is transformed during the frame. e.g. are there any functions that consume only position (but not scale/rotation/velocity)? Is velocity always used at the same time as size, etc... Once you know which data is used together, and which data isn't used together, then you've got a much better idea already as to how it should be stored.

Share this post


Link to post
Share on other sites

Hey guys, sorry for the slightly off topic, but since since I've read all replies and would like to know more, could you suggest me some good learning material on the subject? (with C++ examples, only language I know so far)

Also if possible some stuff on the line below, like even only what I should be google-ing for in order to understand how that code is working :P

entity.GetComponent<Move>()

 

Edited by MarcusAseth

Share this post


Link to post
Share on other sites

There are probably a hundred threads on this, so search the forums. The right answer is entirely dependent on your own needs and desires, and also on the language you're using. There's also a lot of very opinionated articles online, many of which contradict each other, and many of which promote methods which nobody is using in shipped games.

That entity.GetComponent<Move> call is simply a template method that looks up the relevant component in a list and returns it. The template argument both lets the function know which component to find, and also what type to cast it to. In C# or Python, it would be trivial to implement it by checking the type explicitly; in C++, you might need to use std::type_index to get a per-class number which would be the key in a map of components, or something similar.

Share this post


Link to post
Share on other sites
7 hours ago, MarcusAseth said:

Also if possible some stuff on the line below, like even only what I should be google-ing for in order to understand how that code is working :P


entity.GetComponent<Move>()

That's one of the popular ways to do components that everyone seems to do, but no one has sufficiently justified why - at least to my arbitrary standard. =)

(the only time I like the general idea of getting components from an entity is when scripting, and in that situation, I'd use a proxy class to act as an interface to the concept of "entity", since in the DOD-styles of ECS that I prefer, entities are a concept, not a class, existing mostly just as an ID)

It's often over-abstraction, IMO.

First, one has to ask, who should own components? If the entity does, I suggest you are (likely) just doing composition disguised as ECS, not really ECS (though the term isn't standardized enough to say definitively what is ""true"" ECS).
 
Composition is perfectly fine and often preferable!

Imagine you had something like this:

struct Entity
{
    //Variables common to all entities:
    int ID;
    std::string name;
    var whatever;
    
    //"Components" that not all entities have - nullptr if this instance doesn't have that component.
    MoveComp *move = nullptr;
    VisualComp *visual = nullptr;
    CollisionComp *move = nullptr;
};

//Check if a component exists, and then use that component.
if(myEntity.move) myEntity.move->FollowPath();

If your more complex code doesn't give you anything beneficial over this much simpler method, why use the more complex version?

Even that above example is move convoluted than needed, as you can just give all entities a visual component, and set a bool to "invisible", and similar for many other components. I question how many times people's *actual* game needs require invisible entities anyway. Not theoretical "maybe in the future it'd be cool to have ghost enemies" (which you'd probably want to still be visible, just translucent anyway!).

 

In my post above, I've linked to two posts I've written describing some of the habits I think are signs of potentially bad design - what some programmers call a "code smell" - i.e. it smells like something is likely bad, so your guard should be up. When it comes to ECS, because I've seen it abused so much, entity.GetComponent<Move>() is one of those smells to me.

I don't like my Systems or Components to be polymorphic or suffer from CRTP-itis. =)
(sometimes there are justifiable reasons for doing so - most cases I've seen just add abstraction for abstraction's sake, and not for actual benefit. Likewise, CRTP is useful in some cases, but is abused more often than not in ECS architectures)

A Graphic system isn't a physics system, nor should they be forced into the same interface. A graphic component and a physics component have virtually nothing in common other than the fact that they're both components. They don't need to be stored the same way, nor should they be mandated to have the same interface, unless the project genuinely requires it.

Unless your goal is to build **an ECS architecture**, you should probably just use composition in this case, and derive from Entity if the game requires it.

I'm a *fan* of ECS architectures, but my current projects don't use them because not every architecture fits every project, and in many cases they are the wrong tool for the job.

If you still want to make an ECS architecture (instead of actually making games =P), then I'd suggest writing out what *benefits* you hope to get out of it, because ECS adds complexity (not difficulty, just code-bloat complexity) in trade for specific benefits that likely don't line up with what you and your project actually require. What I'm saying is, different architectures have costs (code bloat, debug costs, development time, bad abstraction can increase mental reasoning costs, etc...), and offer something in trade for those costs, and what they offer in trade may not benefit your project.

Edited by Servant of the Lord

Share this post


Link to post
Share on other sites

It's worth mentioning that ECS and other "ad-hoc relational" designs and patterns (thanks for the term @Hodgman!) are implementation details that shouldn't be exposed to the user (in what would be known as a "leaky abstraction").

User code doesn't (or shouldn't) care if entities are collections of components, God objects, or something in-between. The purpose of a proper abstraction is to hide such irrelevant details. Unfortunately, allowing direct access to components breaks that abstraction, couples implementation details together, and makes it more difficult to refactor or extend functionality.

Perhaps today setting the position of an entity is universal and straightforward:

entity.component<Position>().set(10, 10);

But maybe tomorrow you have more complex entities with interdependent data:

entity.component<Position>().set(10, 10);
entity.component<Model>().setPosition(10, 10);

Or maybe you want to send events when the position changes, notify other parts of the engine, emit log messages, set dirty flags for networking, wrap the change in an event and apply it next frame, etc. You certainly don't want to expose those details at every call site where higher-level code interacts with your entity model. At the same time you also want to avoid internal coupling :

void Position::set(int x, int y)
{
  x_ = x;
  y_ = y;
  
  // Oh great, Position component now has a permanent code dependency on Model
  // even though this condition is only sometimes true. Model component probably
  // also needs a similar, defensive call to us... yay :|
  if (has<Model>())
    component<Model>().set(x, y);
  
  // Ugh, it also needs to know about event handling??
  eventEmitter().send(MoveEvent(x, y));
}

Lastly, let's not forget unit tests. It turns out that TDD is a powerful catalyst for building good abstractions because user code is the primary focus, and implementation secondary. And since test code is so highly contrived and designed to exercise specific functionality and use cases, you don't typically get a lot of reuse, so a poor abstraction will immediately and noticeably impede development by practically doubling your efforts and forcing you to maintain multiple, parallel code paths. The impedance alone forces you to spend the time designing good abstractions, just to make your code more maintainable and improve your overall quality of life :)

Now imagine something like this:

void move(int id, int x, int y)
{
  auto entity = getEntityById(id);
  if (!entity)
    throw std::runtime_error("bad entity id");

  if (entity->has<Position>())
    entity->component<Position>().set(x, y);
    
  // For those more complex entities
  if (entity->has<Model>())
    entity->component<Model>().set(x, y);
    
  // Send an event
  entity->eventEmitter().send(MoveEvent(x, y));
}

Position and Model components no longer depend on each other. User code doesn't depend on components. The only point of coupling is the "move" method, which is devoid of any implementation-specific details. Perfect. @Juliean mentioned focusing on the functional over the declarative, and I think that's a good analogy for how one should interact with entities.

And just for fun let's write the unit test mock method:

int testX = 0;
int textY = 0;

void move(int /*id*/, int x, int y)
{
  std::cout << "Moving test entity from " << testX << ", " << testY << " to " << x << ", " << y << std::endl;
  testX = x;
  testY = y;
}

Which not only supports the minimal data and functionality required for the test (tracking and logging position changes), but doesn't depend on the ECS framework at all. That's great, since this specific test doesn't need it anyway. Yet from the perspective of user code, it all looks the same:

move(id, 10, 10);
move(id, 10, 20);
move(id, 20, 30);

Of course these are all contrived examples, but the idea is the same. Don't get too focused on all the minor details of ECS, since in the grand scheme of things it's just another software black box. Get it working, make it easy to use, and circle back if/when there's a quantifiable problem.

Share this post


Link to post
Share on other sites

@Zipster 

1 hour ago, Zipster said:

User code doesn't (or shouldn't) care if entities are collections of components, God objects, or something in-between. The purpose of a proper abstraction is to hide such irrelevant details. Unfortunately, allowing direct access to components breaks that abstraction, couples implementation details together, and makes it more difficult to refactor or extend functionality.

This was exactly my concern earlier when @Juliean suggested to just grab the components and set them. I always felt as though components should not be exposed at a high level. But without enough experience it's hard to figure out ways to separate out an abstraction that will work for all systems I will eventually implement. All that you mentioned are some of the same reasons I hate using getters in code because they often expose implementation details and couple code. If a getter pulls out an int from say Transform.GetLocation() but then later on I decide to change location variable type to float, then I have to change that everywhere GetLocation() is called. Also, if for some reason I just wanted to get rid of a Transform component and do something else, then I also have to get rid of that component from all high level code as well. I like your example and will consider experimenting with it. 

Share this post


Link to post
Share on other sites
51 minutes ago, boagz57 said:

@Zipster 

This was exactly my concern earlier when @Juliean suggested to just grab the components and set them. I always felt as though components should not be exposed at a high level. But without enough experience it's hard to figure out ways to separate out an abstraction that will work for all systems I will eventually implement. All that you mentioned are some of the same reasons I hate using getters in code because they often expose implementation details and couple code. If a getter pulls out an int from say Transform.GetLocation() but then later on I decide to change location variable type to float, then I have to change that everywhere GetLocation() is called. Also, if for some reason I just wanted to get rid of a Transform component and do something else, then I also have to get rid of that component from all high level code as well. I like your example and will consider experimenting with it. 

There's really nothing wrong with accessing components directly.  It all just depends on what your architecture is trying to accomplish and how it's doing it.  In fact, the entire point of an ECS is that all (or most of) your functionality will be components.  So, for code to do anything useful it has to deal with components.  What's the alternative?  To put functionality into the entity and have that redirect calls to the appropriate component?  That's just going to give you unneeded complexity and problems.   Usually, code shouldnt even need to deal with an entity, since what it should be doing is dealing with the appropriate components.   The entities should mostly only be there as a way to get associated components.  

For example: lets say you have your AI_Component that wants to look around and find all the other AIs it sees which it can target.  All you need to do is have it do the appropriate visibility check with your Render_Components, then for all those you get the Entity and query it for an AI_Component, and finally you can set that AI component as your target.   Of course this is just an example, you could organize this in many ways.  But, the point is that the AI component just needs to check those components that it needs to, and then use the Entity only as a way to get other components that are associated together.

As far as the examples given of possible problems, usually those examples actually show that your components are not properly separated.  For example:

entity.component<Position>().set(10, 10);
entity.component<Model>().setPosition(10, 10);

The problem with the code above is not that you're exposing functionality, it's that you have 2 different components that have a position.  Why is that?  Of course if your components are not orthogonal you will have problems.   There should be no need to set a position on the Model component because it shouldnt have a position in the first place.  If it wants to render, then it should get the Position component by going through the Entity.

Share this post


Link to post
Share on other sites
3 hours ago, Zipster said:

void move(int id, int x, int y)
{
  auto entity = getEntityById(id);
  if (!entity)
    throw std::runtime_error("bad entity id");

  if (entity->has<Position>())
    entity->component<Position>().set(x, y);
    
  // For those more complex entities
  if (entity->has<Model>())
    entity->component<Model>().set(x, y);
    
  // Send an event
  entity->eventEmitter().send(MoveEvent(x, y));
}

 

I'm not at all a fan of this approach, or any approach where anything can query any entity for any kind of component from anywhere. It's not far removed from just having global state all over the place. If it's done poorly, it makes for shitty build times, too (if you're using C++).

An advantage to having "entities" be only simple identifiers - handles, really - is that you can make some component state inaccessible to other components and when you do need that access, the dependency is obvious because you have to tell the code that wants access where to find the components. That makes the code more verbose, but that's a feature, not a bug. Requiring explicit dependency specification is a feature, not a bug. Without that, it can become difficult to reason about what depend on which components. An inability to reason about dependencies can lead to a maintenance nightmare.

With a system where entities are only handles, I can make component types that only the code that cares about them needs to care about and as long as I ensure that some kind of mapping from entity to component state exists, I can arrange both the components AND the entities (eg. lists of entities by value!) in whatever the most useful way happens to be for what I'm going to do with them. I don't even need to allow anything outside whatever system deals with that component know what that component is - translation unit-local components can be a thing! For that matter, "components" don't even need to be monolithic objects, they can be "structures of arrays" or even more exotic things as long as there's a consistent interface presented to them that uses the entity handle.

Whereas in order to make this Unity-style ECS work, you're likely going to need some kind of mechanism to expose arbitrary component types to arbitrary code, which adds more complexity and probably global(ish) state (that can then be abused). Then to make has<T> and component<T> work you probably ALSO need some kind of RTTI mechanism for your components, which extra complexity that isn't always actually necessary for anything other than the code that glues the components together.

Requiring that components be objects that anyone can access imposes constraints on the implementation details of your components. That can lead to code that's suboptimal and awkward just to fit it in the architectural box. To answer the original question: ultimately a "component" is just an aggregation of related game state. Generally state that's used together most often should go in the same component.

Edited by Oberon_Command

Share this post


Link to post
Share on other sites
51 minutes ago, 0r0d said:

As far as the examples given of possible problems, usually those examples actually show that your components are not properly separated.  For example:


entity.component<Position>().set(10, 10);
entity.component<Model>().setPosition(10, 10);

The problem with the code above is not that you're exposing functionality, it's that you have 2 different components that have a position.  Why is that?  Of course if your components are not orthogonal you will have problems.   There should be no need to set a position on the Model component because it shouldnt have a position in the first place.  If it wants to render, then it should get the Position component by going through the Entity.

I had a discussion above with @Servant of the Lord concerning component granularity:

On 9/6/2017 at 10:22 PM, Servant of the Lord said:

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 ECS systems, 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 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).

So if I try and think of components more broadly then I'm sure other components will inevitably start needing the functionality of other components. @Servant of the Lord suggests passing in components to other components to help with dependent functionality. You were saying that components needing other components should just go through the Entity. I know you described this a little bit but would you be able to give a quick pseudo code example? And would this be any better than just adding components as parameters in other component functions/constructors?

Edited by boagz57

Share this post


Link to post
Share on other sites

@Oberon_Command

27 minutes ago, Oberon_Command said:

An advantage to having "entities" be only simple identifiers - handles, really - is that you can make some component state inaccessible to other components and when you do need that access, the dependency is obvious because you have to tell the code that wants access where to find the components. That makes the code more verbose, but that's a feature, not a bug. Requiring explicit dependency specification is a feature, not a bug. Without that, it can become difficult to reason about what depend on which components. An inability to reason about dependencies can lead to a maintenance nightmare.

 

Would you be able to provide a quick pseudo code example of what your talking about here? 

Edited by boagz57

Share this post


Link to post
Share on other sites
1 hour ago, boagz57 said:

@Oberon_Command

 

Would you be able to provide a quick pseudo code example of what your talking about here? 

void move_character(
  Transform& transform,
  const CharacterDefinition& definition,
  const CharacterInputState& input,
  float dt)
{
  const Vector desiredMoveDirection = input.get_desired_move_direction();
  const Vector movement = definition.movementParameters.compute_translation_vector(desiredMoveDirection, dt);
  transform.translate(movement);
}

void move_all_characters(
  SparseArray<Transform>& transforms, 
  const SparseArray<CharacterDefinition>& characters,
  const SparseArray<CharacterInputState>& inputStates,
  float dt)
{
  for (const CharacterInputState& input : inputStates.get_active_set())
  {
    const EntityHandle entity = input.get_handle();
    const CharacterDefinition* definition = characters.try_and_get(entity);
    Transform* transform = transforms.try_and_get(entity);
  
    if (definition && transform)
    {    
      move_character(*transform, *definition, input, dt);
    }
  }
}

Then we can call move_all_characters from a place where we have access to the transforms, characters and input states, which is probably glue code that binds all this stuff together. Of course, we don't actually need these to be sparse arrays, they could be some other kind of abstraction. Maybe even an opaque structure that lives off in memory somewhere and we don't actually know what it is, apart from the fact that we can call a function on it (or a reference or smart pointer pointing to it) that gives us a CharacterDefinition given an EntityHandle.

This also allows you to have entirely different sets of entities for different kinds of "game objects" if some entities have entirely disjoint sets of components, rather than forcing all components to exist in the same set of entities. Of course, you can do that with other frameworks, too, but here that idea becomes very easy to implement.

Edited by Oberon_Command

Share this post


Link to post
Share on other sites
3 hours ago, boagz57 said:

So if I try and think of components more broadly then I'm sure other components will inevitably start needing the functionality of other components. @Servant of the Lord suggests passing in components to other components to help with dependent functionality. You were saying that components needing other components should just go through the Entity. I know you described this a little bit but would you be able to give a quick pseudo code example? And would this be any better than just adding components as parameters in other component functions/constructors?

Components wouldnt necessarily need to go through the entity to access other components.  A component could very easily have a pointer to another component if needed.  This would avoid the process of getting the entity and then querying for the other component.  In that case, I'd suggest using std::shared_ptr and std::weak_ptr.  A component can keep an std::weak_ptr to another component if needed.

As far as pseudo-code for getting components through the entity, that's simple:

TransformComponent* T = GetEntity().GetComponent<TransformComponent>();
if(T)
{
  	// do stuff here
}

The thing is, this all depends on how you want to organize your code.  As you can see in this thread, some people will like to do things one way and others another way.   And, people tend to think if you do it a different way from what they're used it, it's going to be awful and full of problems.  But the reality is that each way has it's benefits and drawbacks. The problems, IMO, usually come because you either 1) chose an approach that's not well suited to your problem, or 2) dont follow that approach correctly and end up with messy and hacky code.  The example earlier of having a Model Component with a position, when you already have a Position component, is such a thing.   When architecting these systems, it's very important to carefully think through when you add new stuff, to make sure it follows the established rules and assumptions of the architecture.  A lot of times you end up with something you think cant be done, or something you think can be done but only if you break the rules or make exceptions.  But, that usually means you just havent thought about it in the right way.   If your architecture is clean and well thought out, you can almost always find ways to solve problems that fit within that architecture.

ECS is very powerful, but it requires you to really think about what will be components, what granularity you need, what problems you have and what features you will actually need.  You need to break down your game and features, and then think of logical ways to break them down into components.

Share this post


Link to post
Share on other sites

@0r0d

6 hours ago, 0r0d said:

The thing is, this all depends on how you want to organize your code.  As you can see in this thread, some people will like to do things one way and others another way.   And, people tend to think if you do it a different way from what they're used it, it's going to be awful and full of problems.  But the reality is that each way has it's benefits and drawbacks.

This is one of the major problems I'm having with trying to design my code base. Deciding when something needs to redesigned. I've heard all the horror stories about software going down a certain path and then after a while having problems that require massive amounts of work to try and fix. Every software article/book you read is almost entirely about how you should try and avoid these kinds of problems. Because of this I'm constantly second guessing architecture and spotting potential issues here and there. I guess programming is about finding the right balance between clean code and moving forward despite obvious issues. The problem is I rarely see more pragmatic discussions on developing and designing software architecture which can make it difficult to develop in my day to day software design.

If anyone has any advice on this aspect I would love to hear it. That is how do you typically design an architecture and balance the need to constantly refactor your code (due to obvious potential issues) and the need to move forward in a project? Does it make sense to treat refactoring like optimization? That is, only refactor when any of the 'potential issues' really do start effecting your code base? Or is that too much of a risk as this can leave your architecture a mess after too long.

Edited by boagz57

Share this post


Link to post
Share on other sites
12 hours ago, boagz57 said:

So if I try and think of components more broadly then I'm sure other components will inevitably start needing the functionality of other components. @Servant of the Lord suggests passing in components to other components to help with dependent functionality.

Just to clarify, since I kinda glossed over that earlier - in my mindset, components are almost pure data (albeit sometimes with minor functions), and *data* doesn't have dependencies, *code* does. A system can have dependency on more than one component.

(I count functors/function-pointers and scripts as pure data until executed - the components can store those for systems to execute)

If a *system* requires more than one component, you can pass them both in to that system. For example, let's say you had a Transform component, containing position/rotation/etc... And you had a Collision component, containing a bounding box relative to Transform, and a Animation component that is drawn at Transform's position.


There is no "Transform" system**. But PhysicsSystem can be handed the Collision comp and Transform comp, and GraphicSystem can be handed the Animation comp and the Transform comp.

**Depending on the needs of your game, there might be a transform system, especially if there are parent-child relative transforms. Though that might be done by a simple function, e.g. "ResolveFinalTransforms(&transformComponents);", and not need an entire "system" class.

In the ECS mindset I subscribe to, Entity is just a concept, identified by an EntityID, systems operate on components en-bulk, and so you'd pass in entire arrays of components to the systems that need that type of component. It'd look roughly something like this:

//Systems that only need to read a specific component type (e.g. GraphicSystem's need of Transform), can take a const reference to that array.
//This is benenficial for debugging, ofcourse, but also for paralization, if actually needed.
//Some systems may have private internal arrays of components mapped to IDs, for bookkeeping and optimizations and such.

PhysicsSystem physicsSystem(&transformComponents, &collisionComponents,
                                staticWorldGeometry /* or whatever other components or non-component stuff this system needs */);
GraphicSystem graphicSystem(&transformComponents, &animationComponents, &particleComponents, &textureCache, &shaders, &etc...);

This seems like a very different architecture then what you are doing, and is alot of boilerplate for benefits most games don't need. Simple entities and composition (and possibly inheritance) is much superior in most cases.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now


  • Forum Statistics

    • Total Topics
      628734
    • Total Posts
      2984444
  • Similar Content

    • By th3luck
      Hello everyone! I have decided to make a crane simulator (first person with view from cabin). Mostly, I aim on realism as I want this simulator to be used by real сrane operators to upgrade their skill. That's why I need an engine to easily work with physics (such as wind, rain, weight of cargo and etc). Have to mention that I don't really care about graphics quality. Maybe, but this has the least priority, after completing the whole project I would need a VR version of it, but for now I am planning just a PC version. 
      Here is main points of my project:
      Physics has the major priority Using tools suitable for a beginner developer Ability to make a VR version of the simulator without rewriting of the whole project My experience in programming is mostly in computer science, so I am familiar with C++ and Python, but only on a level of coding some cool algorithms. That's why I don't really depend on a specific language. 
      What engine and developer tools would you recommend to use? Easy to being with for a low-skilled like me, but suitable for my project.
      Thanks,
      Mike
    • By NeverSay
      I think I might be with a team that is deep within development hell and they are all in denial about it. I have been working on a project with a small group and we all have no previous experience or educational background. The guy who had pulled us altogether had an idea for a game but the story is so confusing and convoluted after 3 years of working on this project... I still have no idea whats going on with the story. All I can say is its a platformer with a large explorable map and systems and mechanics dreamed up so grand out of all the 100 things he wants in the game we still only have 2 mechanics down. Every time I have tried to throw my voice in and suggest maybe we should put a pin in this idea and focus on smaller experiences so we can build our skills up and tackle it later when we know more of what we are doing, I get shot down. I'm told, "oh it will only take a year to make this game" of which both I and the artist look at each other very doubtful each time he says that. 
      So leaving out the backstabbing power plays a certain member of the team kept using which blocked me from having access to the game so I couldn't test out my work for myself, phage requests on what they wanted animated which lead to me redoing animation more time then I think was needed. I mean I redid the run animation over 60 times and in the end, they chose to use the very first freaking one I made! Whenever I tried to think logically and ask for a group meeting to talk about characters and how they move so I could get a better idea of what to do I was told "No, no, no this back and forth is a faster way of working"....again run animation took 60 attempts before they used the first run cycle I made. 
      In the end, we weren't working towards making the whole game, it took us 2 years and 5 months to make a demo of the game. Then our programmer disappeared and also took with him the code for the game. We should have stepped away from that game and accepted, for the moment that it might be time to take a break from it and focused on crafting smaller experiences to build up our skills and our group's portfolio but no that wasn't to be. We where to start again from scratch, new programmer, new textures and level designs and new rigs and animations. 3 years of work has gone down the drain and now I have to start again... the only problem is I can't bring my self to do the work. 
      I have become stagnant where I am and I feel like its time I should move on. I want to try and do things my way and start small and work my way up and not plunge headfirst into a project that feels like an ocean in scale for a small team. I want to focus on my own skills and learn new ones and understand more than just one trait in the spectrum of game design. Though since I have been with the team for so long and put so much time and money into this venture...I'm scared to walk away. Though on the other hand how long are we gonna spend on this game, is it gonna be another year like the lead designer said (again and again) or will it be another 3 and all we will have to show for it is a shiny new demo and none of the full game...
      Should I suck it up and try and kick my butt back into gear or is this the time to step away and look into new possibilities?
    • By DogmaStudios
      What techniques do you use to help develop the plot alongside gameplay when you finally have an idea for a story down?
    • By Josheir
      void update() { if (thrust) { dx += cos(angle*DEGTORAD)*.02; dy += sin(angle*DEGTORAD)*.02; } else { dx*=0.99; dy*=0.99; } int maxSpeed = 15; float speed = sqrt(dx*dx+dy*dy); if (speed>maxSpeed) { dx *= maxSpeed/speed; dy *= maxSpeed/speed; } x+=dx; y+=dy; . . . } In the above code, why is maxSpeed being divided by the speed variable.  I'm stumped.
       
      Thank you,
      Josheir
    • By Benjamin Shefte
      Hey there,  I have this old code im trying to compile using GCC and am running into a few issues..
      im trying to figure out how to convert these functions to gcc
      static __int64 MyQueryPerformanceFrequency() { static __int64 aFreq = 0; if(aFreq!=0) return aFreq; LARGE_INTEGER s1, e1, f1; __int64 s2, e2, f2; QueryPerformanceCounter(&s1); s2 = MyQueryPerformanceCounter(); Sleep(50); e2 = MyQueryPerformanceCounter(); QueryPerformanceCounter(&e1); QueryPerformanceFrequency(&f1); double aTime = (double)(e1.QuadPart - s1.QuadPart)/f1.QuadPart; f2 = (e2 - s2)/aTime; aFreq = f2; return aFreq; } void PerfTimer::GlobalStart(const char *theName) { gPerfTimerStarted = true; gPerfTotalTime = 0; gPerfTimerStartCount = 0; gPerfElapsedTime = 0; LARGE_INTEGER anInt; QueryPerformanceCounter(&anInt); gPerfResetTick = anInt.QuadPart; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// void PerfTimer::GlobalStop(const char *theName) { LARGE_INTEGER anInt; QueryPerformanceCounter(&anInt); LARGE_INTEGER aFreq; QueryPerformanceFrequency(&aFreq); gPerfElapsedTime = (double)(anInt.QuadPart - gPerfResetTick)/aFreq.QuadPart*1000.0; gPerfTimerStarted = false; }  
      I also tried converting this function (original function is the first function below and my converted for gcc function is under that) is this correct?:
      #if defined(WIN32) static __int64 MyQueryPerformanceCounter() { // LARGE_INTEGER anInt; // QueryPerformanceCounter(&anInt); // return anInt.QuadPart; #if defined(WIN32) unsigned long x,y; _asm { rdtsc mov x, eax mov y, edx } __int64 result = y; result<<=32; result|=x; return result; } #else static __int64 MyQueryPerformanceCounter() { struct timeval t1, t2; double elapsedTime; // start timer gettimeofday(&t1, NULL); Sleep(50); // stop timer gettimeofday(&t2, NULL); // compute and print the elapsed time in millisec elapsedTime = (t2.tv_sec - t1.tv_sec) * 1000.0; // sec to ms elapsedTime += (t2.tv_usec - t1.tv_usec) / 1000.0; // us to ms return elapsedTime; } #endif Any help would be appreciated, Thank you!
  • Popular Now