1
\$\begingroup\$

I have been trying to get my head around using an entity component system for a game. But I am struggling to understand how I should do 'game-logic'.

What I have so far:

  1. Components are essentially just dumb data holders, no logic beyond getters and setters.
  2. Systems do processing on groups of components. E.g a PhysicsSystem might process all entities containing Transform and Velocity components.
  3. Entities are just integers.

My question is, how do I add 'entity-specific' or 'game' logic? For example, I will have an entity which represents the player. How do I go about moving the player entity?

Couples ideas I had, not sure if any are good?

  1. Create a 'MovementSystem' which uses a component (e.g PlayerMovementComponent). Then give just my player entity the PlayerMovementComponent. This is basically just a system which only ever processed the one entity... seems a big fudgey.
  2. Create a Script or Behaviour component which holds a std::function (or maybe could reference a lua script), could then have lambdas which allow unique game logic to be given to entities. This seems to go against the data-orientated approach I’m trying to follow?
  3. Create a Player class, have it reference the entity Id then give the player class an update method hooking into the game loop. This update method can edit its referenced entities components before the systems process. This is basically disconnecting myself entirely from the ECS for game-logic... not a fan of this idea (also will make my game loop bloat as more unique entities are required).

Hopefully, I can get some comments on the 3 ideas above and some new ideas or thoughts I can pursue.

\$\endgroup\$
  • 2
    \$\begingroup\$ You might find this previous Q&A on systems & components useful. Basically, the strict separation of components as PODS to be batch-processed by systems is just one way to approach component based development. You're not required to adhere to this more rigidly than what makes sense for you. I would caution against making a single "player" class though, as these tend to attract a lot of overlapping responsibilities - that's one of the reasons devs turned to components to disentangle. ;) \$\endgroup\$ – DMGregory Nov 15 '17 at 18:04
2
\$\begingroup\$

Not everything in your game must be based on the ECS pattern you're using and the game logic might be a good candidate not to.

It could be a global script.
Or, as you mention, you can implement the logic as a system not to "pollute" your main loop with different classes: this game_logic_system, during its update phase, could callback on script(s) or implement the logic in a more hard-coded way, or via plugin, configuration file, etc...

Whichever way you implement the game logic, it has to be aware of the ECS: it has to identify (for instance) the player entity (by either a unique-id assigned to it, or by looking for entities with a dedicated Player component as you propose) and most of the logic will result in setting some component properties or calling ES methods to obtain the desired behavior.

\$\endgroup\$
  • \$\begingroup\$ That is fine, I guess the main point of my question is where do I put the logic? If I was to call this logic in my game loop, what form should this take? I can't picture how to do this? A struct containing a lambda or something? I'd also need the entity id referenced somehow, how will which script know what the correct entity id is etc? The idea of "just have scripts called" makes sense logically but I can't see how it will fit together practically. \$\endgroup\$ – TomShar Nov 15 '17 at 19:44
  • \$\begingroup\$ I guess what I'm asking for is a design pattern? \$\endgroup\$ – TomShar Nov 15 '17 at 19:48
  • \$\begingroup\$ With respect to retrieving the correct entity id, I covered that in the answer: 1. create a component to assign names or unique ids to the entity, and retrieve the particular entity representing the player by unique id, or 2. create a component dedicated to the player, which you assign only to the player entity to recognize it. As for details on how to implement the logic into the game loop, there is no one "correct" pattern to do that. Callback into a script, lambda function in c++ or function loaded from a plugin are all possible ways to implement it. \$\endgroup\$ – rickyviking Nov 16 '17 at 13:58
1
\$\begingroup\$

All three of your suggestions are fine. Use all of them that fit your needs.

You don't have to (and should not) try to shove everything into the "entity component" model. That just creates the same basic shape of problem you have when you shove everything into the "inherit all the things" model: you've just swung the pendulum the other way.

I usually represent the player as a thing that gets a reference to some controlled entity, and recieves actions representing the (human) player's requests from the input system. It interprets those actions according to whatever logic the game needs and makes the appropriate changes to the referenced entity. It's not a "system." It's just what it is. There is one of them (usually), and it just sits in the main Game class the whole time.

You could just as well implement it as a component you attach to entities, owned by a "system" that collects player input and feeds it to every component. Letting the player drive multiple entities, if that's what you really want.

Your "script" component might be useful on its own, either to implement this player control or do handle other one-off or game-specific logic that doesn't deserve its own system or module or component or whatever you're using.

\$\endgroup\$
  • \$\begingroup\$ Ok, so a script system where I can write a script (c++ lambda or lua) and attach it to an entity is someway would be a good option by the sounds of it if I understand you correctly? \$\endgroup\$ – TomShar Nov 15 '17 at 20:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.