Jump to content
  • Advertisement
Sign in to follow this  
Dominik2000

Component based game engine architecture

This topic is 1812 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I want to create a component based game engine with subsystems which do the work, entities which are simply a list of components and position and rotation properties (because every object will have that), and components which hold data (e.g. mesh component which holds the filename of the mesh and the name).

In addition I want the subsystem to create the components for the entity (the entity has a list of component types and the subsytem creates them.) Is that an acceptable approach?

The subsystem has to be notified if an entity is added to the world. I think the best way is the observer pattern, my scene manager notify every subsystem. And here I get stuck: When the scene manager notifies, should this be a function pointer or a abstract system class with handle message? In the first case, how can I get this work with a variable count of parameters?

I tried to implement this but I am very confused, on google there is too much information, so I can't find whats right for me. Here a example how I tried this:

 

My component class:

/*
* Component.hpp
*
* Created on: Jul 15, 2013
* Author: dominik
*/

#ifndef COMPONENT_HPP_
#define COMPONENT_HPP_

#include "System.hpp"

#include <string>
#include <vector>

class Entity;

typedef enum
{
MESH_COMPONENT,
POSITION_COMPONENT,
} m_ComponentTypes;

class Component
{
public:
Component(m_ComponentTypes type, Entity *parent) { m_Type = type; m_pParent = parent; };
virtual ~Component();

void addObserver(System *system) { m_Observers.push_back(system); };
void removeObserver(System *system)
{
std::vector<System*>::iterator itr;

for(itr = m_Observers.begin(); itr != m_Observers.end(); itr++)
{
if((*itr)->getType() == system->getType())
{
m_Observers.erase(itr);
break;
}
}
};

void notify()
{
std::vector<System*>::iterator itr;

for(itr = m_Observers.begin(); itr != m_Observers.end(); itr++)
{
(*itr)->handleMessage(this);
}
};
protected:
Entity *m_pParent;
m_ComponentTypes m_Type;

std::vector<System*> m_Observers;
};

#endif /* COMPONENT_HPP_ */

And my system class:

/*
* System.hpp
*
* Created on: Jul 15, 2013
* Author: dominik
*/

#ifndef SYSTEM_HPP_
#define SYSTEM_HPP_

#include <string>

class Entity;
class Component;

class System
{
public:
virtual ~System() {};

virtual void initialize() = 0;
virtual void update(double timeSinceLastFrame) = 0;

virtual void handleMessage(Component *component) = 0;

std::string getType() { return m_sType; };
protected:
std::string m_sType;

std::map<Entity*, Component*> m_Components;

System() {};
};


#endif /* SYSTEM_HPP_ */

And my entity class:

/*
* Entity.hpp
*
* Created on: Jul 15, 2013
* Author: dominik
*/

#ifndef ENTITY_HPP_
#define ENTITY_HPP_

#include "Component.hpp"

#include <string>
#include <vector>

class Entity
{
public:
Entity(int uniqueId) { m_UniqueId = uniqueId; };
~Entity();

void addComponent(Component *comp);
void removeComponent(Component *comp);

Component *getComponent(m_ComponentTypes type);

int getID() { return m_UniqueId; };
protected:
std::vector<Component*> m_Components;
int m_UniqueId;
};

#endif /* ENTITY_HPP_ */

This approach with an abstract class is not my favorite, I think function pointers are better.

I think of this case: My position component will be edited from the position system, so my system or my component have to notify the render system.

There are more solutions:

1.) I can create a message with Vector parameters

In this case I need a observer pattern and a message system.

2.) I can have a list of observers in the component

Good approach I think, but if the position component notifies I have 3 float parameters(x, y, z). The mesh component needs only one string parameter. How should I handle this?

3.) I can have a list of observers in the system

Is there a advantage to 2? What is better?

Do I really need the component list in the entity?

Thank you, I hope everything is clear, if not ask, my english is not so good.

Dominik

Share this post


Link to post
Share on other sites
Advertisement

There are 1001 ways, approximately, of doing component based entities ... here are some thoughts about your way of doing it:

 


In addition I want the subsystem to create the components for the entity (the entity has a list of component types and the subsytem creates them.) Is that an acceptable approach?

A list of types isn't sufficient. Many components need to be fed with initial data. So you need to have a mechanism to read and interpret the initial data, too. This, of course, can be implemented inside the respective sub-systems, too.

 


The subsystem has to be notified if an entity is added to the world. I think the best way is the observer pattern, my scene manager notify every subsystem. And here I get stuck: When the scene manager notifies, should this be a function pointer or a abstract system class with handle message? In the first case, how can I get this work with a variable count of parameters?

A variable count of parameters isn't necessary at all (letting aside that different notification types may need different count of parameters, because this can be handled simply by using more than a single notification). The scene managers notifies the sub-systems and overhands the entity. The entity is the thing of interest when adding and removing one. Let the sub-systems themselves inspect the entity and determine what components are important in their respective purpose.

 


My position component will be edited from the position system, so my system or my component have to notify the render system.

So the position component is a storage for the position, or why else may the position system alter it? If so, why has the render system to be notified? The render system may just read the position at a time where all changes to the position are settled for the current cycle (i.e. after animation, physics, collision detection and correction has taken place). Or do you want each system to have its own copy?

 


2.) I can have a list of observers in the component

Good approach I think, but if the position component notifies I have 3 float parameters(x, y, z). The mesh component needs only one string parameter. How should I handle this?

Perhaps by passing the component, because the component has already the suitable structure.

 


3.) I can have a list of observers in the system
Is there a advantage to 2? What is better?

Looking at the (not so good) example of notifying the render system on position changes: The system is interested in every position change. The render system has either to register with n hundred components or with a single system. Of course, the registration with the system is much more efficient in this case. Although there are counter examples for sure, it seems me that the system should be observed in general.

Share this post


Link to post
Share on other sites

Thank you haegarr for this very good answer. There are very useful things, which I can implement or make easier.

 

So the position component is a storage for the position, or why else may the position system alter it? If so, why has the render system to be notified? The render system may just read the position at a time where all changes to the position are settled for the current cycle (i.e. after animation, physics, collision detection and correction has taken place). Or do you want each system to have its own copy?

 

 

Hmm, thats interesting, in the render system I can loop over all and update the position, no notify is needed, only if I build a queue, which entities have been updated.

 

Share this post


Link to post
Share on other sites


Hmm, thats interesting, in the render system I can loop over all and update the position, no notify is needed, only if I build a queue, which entities have been updated.

I'm not sure what position you want to update from inside the render system. I currently cannot think of any situation where this is needed. The render system does nothing else than rendering. It must not alter the position of an entity, because the position as is at the moment of rendering is already gone through a complex process of user/AI control, animation, physics, and collision resolution. Hence it is perfectly adapted to the current situation, and any subsequent alteration outside this process makes things worse.

 

Perhaps I'm missing something...

Share this post


Link to post
Share on other sites
two things i notice:
 
1. you're storing a mesh filename, but not a pointer to the mesh. the filename can eliminate duplicate loads, but you'll want to do a lookup of filename to mesh data structure pointer just once at load time, and save the pointer for drawing later, instead of looking up the mesh pointer from the filename every time you draw. strcmp() is slow.
 
2. since you're keeping both static and mobile targets in the same list (scene graph), you may want an easy way to determine if the target is mobile and needs position update at all. perhaps something as simple as:  
 
if (unit.maxspeed >0) unit.move() 
 
or 
 
if (unit.is_mobile) unit.move()
 
when doing games, i often ponder one list for all targets vs one for mobile, and one for immobile targets. In the latest engine i did, i went with one list, and do a check similar to the ones above to determine if a unit needs to be moved at all.

Share this post


Link to post
Share on other sites

Feel free to disregard my message since it isn't a direct answer to your question, but I just want to offer a little advice.

 

Having worked with a number of different component systems in a few different engines, I've formed the opinion that component systems are a knee jerk reaction to the realization that "is a" leads to more problems than "has a". I don't feel like it's a pattern that needs a generic solution - in fact, the more generic you try to make your component system, the more clunky it will be to use it. I once worked on a game with no less than 4 different component systems being used. Each was an attempt at a generic solution, but only served a subset of use cases well. I myself added another component system for another use case (async batch update).

 

The funny thing about going with a generic component system, is that it isn't immune to the "is a" problem itself when everything derives from some base Component, and you bend the interface to fit as many uses as possible. I've since taken to creating subsystem specific components (eg. steering behavior components, targeting components, and so on). A simple GameObject/Entity with no inheritance tree, and nothing but components is a hard road to travel in practice. The important part is using aggregation where possible, and stick to shallow inheritance where it makes the most sense. At least that's how I see it.

Share this post


Link to post
Share on other sites


In addition I want the subsystem to create the components for the entity (the entity has a list of component types and the subsytem creates them.) Is that an acceptable approach?

 

How would you determine which subsystem is responsible for creating which component? Multiple subsystems may require the same components.

Share this post


Link to post
Share on other sites


How would you determine which subsystem is responsible for creating which component? Multiple subsystems may require the same components.

 

i think he means one subsystem for each type of component, which also does the the init and destroy work for that component type

 

so the mesh subsystem (mesh database) would include create and release methods.

 

multiple systems/modules/object types requiring the same components/data access to do their job is a constant problem in the organization of code.

 

"data and the code that acts on it" is a nice way to organize things in general, until you get to the point where you need to access multiple types of data structures simultaneously. my code for "apple + bananna = orange" belongs neither in "apple" nor "bananna".

 

i've recently taken an interest in the "proper" way to structure a c++ oo game. which has led to a fair amount of recent research, and some surprising results, such as the lack of consensus on the subject, and the plethora of design methods, practices, and principles espoused. none of which seem to be from a "games only" point of view. if one were to follow the design practices of any type of non-game software, i would think it would be those used in real time modeling and simulation apps, not things like business apps.

 

while i have yet to complete my research, on casual reflection it appears that game code in general tends to take the form of data structures, helper code that acts on those data structures, or multiple ones simultaneously, and flow control code. the acting on multiple data structures simultaneously and flow control seem to be the parts that many folks using OO C++ are never sure where they should go or how they should be organized.

 

i plan to start a thread on the topic soon.

Share this post


Link to post
Share on other sites

 


Hmm, thats interesting, in the render system I can loop over all and update the position, no notify is needed, only if I build a queue, which entities have been updated.

I'm not sure what position you want to update from inside the render system. I currently cannot think of any situation where this is needed. The render system does nothing else than rendering. It must not alter the position of an entity, because the position as is at the moment of rendering is already gone through a complex process of user/AI control, animation, physics, and collision resolution. Hence it is perfectly adapted to the current situation, and any subsequent alteration outside this process makes things worse.

 

Perhaps I'm missing something...

 

 

I mean my scene manager, because I use Ogre and the scene manager has it's own list of entities, so i have to update the position.

 

 


How would you determine which subsystem is responsible for creating which component? Multiple subsystems may require the same components.

 

i think he means one subsystem for each type of component, which also does the the init and destroy work for that component type

 

so the mesh subsystem (mesh database) would include create and release methods.

 

multiple systems/modules/object types requiring the same components/data access to do their job is a constant problem in the organization of code.

 

"data and the code that acts on it" is a nice way to organize things in general, until you get to the point where you need to access multiple types of data structures simultaneously. my code for "apple + bananna = orange" belongs neither in "apple" nor "bananna".

 

i've recently taken an interest in the "proper" way to structure a c++ oo game. which has led to a fair amount of recent research, and some surprising results, such as the lack of consensus on the subject, and the plethora of design methods, practices, and principles espoused. none of which seem to be from a "games only" point of view. if one were to follow the design practices of any type of non-game software, i would think it would be those used in real time modeling and simulation apps, not things like business apps.

 

while i have yet to complete my research, on casual reflection it appears that game code in general tends to take the form of data structures, helper code that acts on those data structures, or multiple ones simultaneously, and flow control code. the acting on multiple data structures simultaneously and flow control seem to be the parts that many folks using OO C++ are never sure where they should go or how they should be organized.

 

i plan to start a thread on the topic soon.

 

 

Yes, that is one problem that I have, I think, if a subsystem need more components, I have to search for it in the entity (my components have enum types) and dynamic_cast it. It's not beautiful, but I think, it's acceptable. The first subsystem creates all components required for it, but may not initialize the components. I'm very unsure which way I go.

 

I'm very interested in other approaches.

Share this post


Link to post
Share on other sites

As said, there isn't "the right way".

 

My approach is similar but ... a bit different ;) During editing there a Model instances, what are a kind of resource and hence managed. A Model has Components attached to it. Such a Model is a description (with default parameters) of how an object will be. At runtime, when the system decides to instantiate a Model, an Entity comes to life. An Entity lives in the Scene. As such, an Entity is just a unique identifier (with a reference back to its Model). However, the Components are not part of an Entity. Instead, the Components were used to instruct so-called Services' (in fact analog to your sub-systems, but those term is already used by something else in my engine; please notice that a single object is named with the plural form "Services") to create and initialize data inside the Services'.

 

An example: A PlacementComponent is attached to the Model. Such a PlacementComponent means that a belonging Entity can be placed in the world. It contains a position and an orientation that show default values. When being instantiated, the SpatialServices detect the existence of the PlacementComponent, and therefore allocates and initializes a Placement slot for the Entity. The Placement contains a position vector, an orientation quaternion, a transformation matrix build from position and orientation, and a flag denoting whether or not matrix and position/orientation are in sync. Hence, a Services may hold (slightly) more / different data than the original Component.

 

A Services is not only responsible for storing the data but also for "global" operations on them. These operations are mainly queries. Of course, granting access to the Placement of a particular Entity is one of them. Queries for vicinity of other Entities is another. I also don't restrict Services' to be responsible for a single Component type but closely related ones. The SpatialServices, for example, also manage bounding volumes. Hence queries on collisions can be made, too.

 

My engine includes support for editing. That implies some stuff not necessarily needed in a pure gaming engine. In this case, however, it supports the transition from Model to Entity in the following way. For each type of Component a ComponentType exists. Effective ComponentType classes are instantiated just once, i.e. they are a kind of singleton (although not implemented with the singleton pattern, but that is another story). Originally a ComponentType is a factory for its type of Components and, due to this close relation, serves also as its type identifier at runtime. As such they are used to instantiate the Model as Entity, too. This could be understood so that a ComponentType is an extension plugged into a Services'.

 

So the instantiation process works as follows: The Scene is instructed to create an identifier for the new Entity, and to memorize that those Entity comes from the given Model. Then the Model's Components are iterated, and for each component the ComponentType is fetched and invoked. The ComponentType instructs the belonging Services to do whatever is necessary. On return, the ComponentType tells not only whether their part was concluded successfully, but also whether a post-initialization is needed. After all Components are iterated, all ComponentTypes that told they need post-initialization are invoked again. This is repeated until either any ComponentType finally denies success, or else all ComponentTypes announce success, or else none ComponentType announces success (this latter condition detects configuration errors).

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!