Getting gameObject components to process in systems...

Started by
3 comments, last by haegarr 7 years, 6 months ago

Hi All,

I am learning to implement a component based gameObject design to my game engine. Currently, I have a TransformComponent which inherits from Component. My GameObjects's contain a map of Components, and AddComponent and GetComponent member functions. My Game class contains a std::vector<std::shared_ptr<GameObject>> m_gameObjects;

I have successfully created a 'Player' and added it to my vector of GameObjects like so:


	auto playerTransformComponent = std::make_shared<TransformComponent>();
	Vector2 v;
	v.x = 1;
	v.y = 1;
	playerTransformComponent->position = v;
	auto player = std::make_shared<GameObject>();
	player->AddComponent(COMPONENT_TRANSFORM, playerTransformComponent);
	m_gameObjects.push_back(player);

First of all, am I going about things the right way?

Now, I would like to add a System, that controls logic. Note, I do not want to make a full blown entity component system (ecs) design. I want to be able to code all of this myself, not use an already made framework.

What I have come up with is a MovementSystem that Inherits from System, and implements update as follows:


void MovementSystem::update(float dt)
{
	TransformComponent* playerTransformComponent = static_cast<TransformComponent*>(m_gameObjects[0]->GetComponent(COMPONENT_TRANSFORM).get());
	auto playerPosX = playerTransformComponent->position.x;
	auto playerPosY = playerTransformComponent->position.y;
}

Note that I am just testing it on ONE Game object (the player), hence I use m_gameObjects[0].

The problem is, my MovementSystem does not know of m_gameObjects, since that exists in the Game class. (identifier "m_gameObjects" is undefined).

So it seems I need some sort of GameObjectManager (yes... the dreaded manager). I am a bit stuck with options to proceed from here. Are there any design patterns that come to mind? Or will something like passing the MovementSystem a pointer to my gameObjects work fine?

Thanks

Advertisement

It is okay to pass a pointer to the pool of game objects (here given by the GameObjectManager). At some point there must be an interrelation between systems. And it is also okay to name that class GameObjectManager when it actually manages game objects.

However, I see other problems.

a) When each system fetches components at update time, it has no chance of managing belonging components itself in a way that is meaningful and efficient in the context of the respective sub-system, because the overall architecture prescribes how the components are stored.

b) The fact that a game object has a TransformComponent instance means that it has a spatial placement in the scene. It does not necessarily mean that it is able to move. Using a MovementSystem is one of several possibilities to alter the content of a TransformComponent. So the MovementSystem should not act on a game object just because it has a TransformComponent. (I personally use the class name "PlacementComponent" to make this clear.)

c) The player's game object is something special, of course. However, its movement is usually not, so that MovementSystem should not make a difference between the player's game object and an NPC game object.

To overcome these problems, think e.g. of the following approach:

A) There is a pool of game objects that should be added to the scene at the next opportunity. There is also a pool of game objects that should be removed from the scene at the next opportunity. It can be understood as a service of the game object management. Potentially any system located anywhere in the game loop can cause game object creation by adding a kind of job description "add game object of class X" to the belonging pool (analog for game removal). The said "opportunity to add/remove game objects" to the scene is somewhere at the very beginning of the game loop, at least before any scene related sub-system has done its update. It is nothing than the update() of the game management service. At this point the service will notify all linked sub-systems, and each of them can investigate the pools and manage their internal operational resources accordingly. So each sub-system can drive its own management structure as needed, e.g. holding pointers to all components of type Y and ignoring any game object that doesn't provide Y.

B) Sub-systems need not be all at the same level. It is more like a layered architecture, so that sub-systems on a higher layer may utilize sub-systems on a lower layer. So a MovementSystem is on a more lower layer. For example, a character is mainly controlled by the fact that a PlayerController component or AiAgentComponent is added. The sub-systems running such components may then instruct the MovementSystem when and how to handle the belonging TransformComponent data.

Hi haegarr, thanks for your replys. It is replies like these that really help me learn and iterate my design...

a) When each system fetches components at update time

Ahh, so so by default, it will have to loop over every GameObject weather or not it has or needs to update the component or not. I guess this is were ECS comes in? Ensuring the entities are processed in an efficient way.

b) The fact that a game object has a TransformComponent instance means

That is true, I will probably split it to TransformComponent and PlacementComponent to make that obvious, thanks for that!

c) The player's game object is something special, of course.

I see, so the movement system should update anything with a TranasformComponent. Once I add some enemies I will need to workout where InputComponent and AiComponent fit into this.

A) There is a pool of game objects that should be added to the scene at the next opportunity.

This sounds good. So all of this should happen in a gameObjectManager.update() step? I think I will just implement the simple one then go from there.

B) Sub-systems need not be all at the same level.

I do not quite understand what you mean by this? So each loop, i would call PlayerControllerSystem.update() and AiAgentControllerSystem.update(), and they in turn would call the 'lower' MovementSystem.update()?

Thanks again!

While I know what you probably mean with "the player's character", in most game engines there is no such thing.

There are cameras that are game objects. Generally one camera is used for the main display. Additional cameras can be used for other purposes, such as for a small sub-screen, or a mirror, or a portal, or whatever.

As game objects, you can attach other objects to them. One of those things might be hands or UI elements. They can also have a character controller component which happens to also be responsive to keyboard, mouse, and controller input.

Ahh, so so by default, it will have to loop over every GameObject weather or not it has or needs to update the component or not. I guess this is were ECS comes in? Ensuring the entities are processed in an efficient way.

This is not special to ECS. Look at discussions where people argue that the over-powered scene graph is a wrong approach.

The different sub-systems run different algorithms on the data (from components in this case). The algorithms usually work hand in hand with the structure how the data is stored. Hence it is natural that each sub-system manages its own structure(s) of data. Consequently, having all components in a single structure as provided by the GameObjectManager and the entity's component map cannot be suitable for all sub-systems.

That is true, I will probably split it to TransformComponent and PlacementComponent to make that obvious, thanks for that!

Not exactly what I meant. What meaning would the TransformCompnent have?

To explain my question:

Having a PlacementComponent (regardless of its name) means that at any time there is one position and rotation belonging to the object. When the collision sub-system runs it uses this placement; when the rendering sub-system runs it uses this placement. It is like a variable, read by those sub-systems, and hence with static value. Just when other sub-systems come into play, like e.g. AnimationSubsystem or CollisionResolverSubsystem or MovementSubsystem, there are activities to change the placement's value.

With this in mind, having a TransformComponent besides a PlacementComponent brings up the question which sub-system and how the component is used at all.

I do not quite understand what you mean by this? So each loop, i would call PlayerControllerSystem.update() and AiAgentControllerSystem.update(), and they in turn would call the 'lower' MovementSystem.update()?

Not really. Consider that the game loop is a prescription in which order things should happen.E.g. fFirst input is processed. Then player control is run, then AI, then animation and movement, then collision detection and resolution, then perhaps animation and movement again, then rendering. So, letting the player control invoke movement update would disturb this order.

Instead think of the PlayerController (or AI) to set variables, and of the motion sub-system to read these variables and act accordingly. This lets the PlayerController instruct the MotionSubsystem. The PlayerController actually does not need to know that a MotionSubsystem exists, because it deals with a set of variables.

Then later during processing the game loop, the MotionSubsystem's update is invoked from the game loop, and the MotionSubsystem does whatever the variables tell it.

This topic is closed to new replies.

Advertisement