Jump to content

  • Log In with Google      Sign In   
  • Create Account

We're offering banner ads on our site from just $5!

1. Details HERE. 2. GDNet+ Subscriptions HERE. 3. Ad upload HERE.


Component based game engine architecture


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
15 replies to this topic

#1 Dominik2000   Members   -  Reputation: 249

Like
3Likes
Like

Posted 26 July 2013 - 01:01 AM

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



Sponsor:

#2 haegarr   Crossbones+   -  Reputation: 4437

Like
4Likes
Like

Posted 26 July 2013 - 02:24 AM

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.



#3 Dominik2000   Members   -  Reputation: 249

Like
0Likes
Like

Posted 26 July 2013 - 03:43 AM

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.

 



#4 haegarr   Crossbones+   -  Reputation: 4437

Like
1Likes
Like

Posted 26 July 2013 - 05:42 AM


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



#5 Norman Barrows   Crossbones+   -  Reputation: 2205

Like
0Likes
Like

Posted 26 July 2013 - 09:34 AM

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.

Norm Barrows

Rockland Software Productions

"Building PC games since 1988"

 

rocklandsoftware.net

 


#6 FBMachine   Members   -  Reputation: 309

Like
2Likes
Like

Posted 26 July 2013 - 05:45 PM

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.



#7 phil_t   Crossbones+   -  Reputation: 3948

Like
0Likes
Like

Posted 27 July 2013 - 11:16 AM


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.



#8 Norman Barrows   Crossbones+   -  Reputation: 2205

Like
0Likes
Like

Posted 28 July 2013 - 03:33 PM


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.


Norm Barrows

Rockland Software Productions

"Building PC games since 1988"

 

rocklandsoftware.net

 


#9 Dominik2000   Members   -  Reputation: 249

Like
0Likes
Like

Posted 29 July 2013 - 02:08 AM

 


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.



#10 haegarr   Crossbones+   -  Reputation: 4437

Like
1Likes
Like

Posted 29 July 2013 - 05:17 AM

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).



#11 Dominik2000   Members   -  Reputation: 249

Like
0Likes
Like

Posted 30 July 2013 - 04:19 AM

I like your architecture very much!

 

I like the idea with the model in behind the entity and the component type as initializer. Is your componentType a static class?

 

What I don't get is who is responsible for the notfiy that an entity should be created? My scene manager should to this, but is this a subsystem also? How do you store your entities? Is this a vector?

 

Dominik 



#12 haegarr   Crossbones+   -  Reputation: 4437

Like
0Likes
Like

Posted 30 July 2013 - 09:06 AM


Is your componentType a static class?

ComponentType isn't a static class. It is ... well ... something complicated. Here comes an explanation what I'm doing ... you've asked for it ;)

 

ComponentType is derived from Extension. Extension is the mechanism used to, well, extend a basic functionality. The base class Extension provides methods mainly for editor purposes, e.g. what version, designation, description, and so on, i.e. stuff to tell a user for what a concretization of the Extension is good for. The base class also defines an inner template class Extension::Registry. Now, the first level of inheritance are the concrete extensions like said ComponentType (but also my sub-systems Video, Audio, Input, Graphics, Sound, Feedback, and FileSystem are concrete Extensions, and a couple of other, platform independent classes like ProjectType, ResourceType, FileFormat, CompressionScheme, ...).

 

Each concrete Extension class has a static member instance of Registry. Such a registry is populated with derivatives of the belonging concrete Extension class, e.g. ComponentType::registry is populated with derivatives of ComponentType. This population may be done at application start-up or later on; it may also be done by static objects called a PlugIn which is itself an Extension (just to make things more interesting ;)) However, the static registry must be instantiated before a static PlugIn must be instantiated, or else things goes terribly wrong. Hence I use PlugIn only for dynamic libraries. BTW, registering Extensions goes a similar way of dependency resolution as Entity instantiation described above, so that the order in which Extensions are registered plays no role.

 

Each registry can be searched for an attached Extension by its Extension::kindName, a 4CC, or perhaps some other criteria. The FileFormat::registry, for example, allows also to search by MIME type, and it also allows to search by competence of the FileFormat to handle a given file. So accessing a specific Extension means to ask the registry of its concrete Extension class to look it up. Iterating all registered Extensions is possible, of course, too.

 

Now, the functionality of an Extension returned from a registry isn't allow to alter members, because the Extension is a shared class. This may or may not be a problem, depending on what concrete Extension we're speaking. To solve this "problem", such concrete Extensions declare some factory methods for specific abstract, inner classes. E.g. FileFormat declares the Importer and Exporter classes. On the other hand, ComponentType doesn't need to do so, because it isn't a worker class in this sense.

 

You may ask "what the hell is that man doing there", and you'll a bit right. To justify myself, my engine and my editor are very data driven. Even the functionality becomes somewhat data driven with the above approach. This is because I'm able to iterate e.g. the ResourceType::registry with a set of Profile instances belonging to the ProjectType of the current project, display the Extension::designation of each resulting Extension, allow the user to context click on each of them, so picking e.g. the "create" menu item, instantiate a ResourceCreateCommand that, when finally overhanded to the CommandProcessor, just invokes the ResourceType::runCreation() method. Well, editor-like, isn't it? ;)

 


What I don't get is who is responsible for the notfiy that an entity should be created?

Instantiation of an Entity during running the game may have the following causes: Loading of level, or else an in-game trigger has fired. Both causes belong to the scene. If your SceneManager is responsible for loading the scene, then it has to start instantiation of Entities, yes. Triggers (regardless of being timed, due to collision detection, or whatever) are used to start a script (a nodal script in my case); one node has the functionality "create entity". So in this case the script processor does the job; however, it does so by invoking the Scene.

 

 


My scene manager should to this, but is this a subsystem also?
In my opinion, the scene manager is a (subsystem) Services, right you are. It manages the world by knowing all the entities inside. Please notice that this understanding of a scene is by far different from a scene graph.
 


How do you store your entities? Is this a vector?
I'm not sure whether I understand you correctly. An Entity in my sense is a set of allocated structures distributed over a couple of Services'. Many Services' store their data in one or more AoS (a.k.a. Array-of-Structures). Some use dynamic, doubly linked lists with indexing. Whatever is suitable for the specific task.


#13 Dominik2000   Members   -  Reputation: 249

Like
0Likes
Like

Posted 31 July 2013 - 02:20 AM

Ok wow, very interesting. But it seem for my "small" project it's a little bit overkill ;) The game engine grows with time, and I think, many things changes after time.

 


I'm not sure whether I understand you correctly. An Entity in my sense is a set of allocated structures distributed over a couple of Services'. Many Services' store their data in one or more AoS (a.k.a. Array-of-Structures). Some use dynamic, doubly linked lists with indexing. Whatever is suitable for the specific task.

You've said that your entity is nothing more than an id and a reference to the model class. Anywhere you have to save this entity?



#14 Dominik2000   Members   -  Reputation: 249

Like
0Likes
Like

Posted 31 July 2013 - 07:29 AM

I want to thank for the many posts on my thread. Because I'm a newbie I have been afraid to take a "one-way" road and can't get back... I post here the link to my repository: https://github.com/Dominik2000/Cantara/tree/CO/src

 

Please could you look a little bit over the code, if anything is wrong with it?

 

Some classes (e.g. BaseMessage) are not used at this time.

 

A little explanation: The Core class is responsible for the main loop, and it initializes the window in qt. The Engine update method will be called in every frame and calls an update for every subsystem registered. It implements the SceneManager also, which is not a subsystem in the list, but has it's own property. Every subsystem registers itself on the SceneManager.

If the SceneManager creates a new entity it defines the Model for the entity, which has a list with all required components. Then every subsystem will be notified to create the components it needs (RenderManager has two lists one for the MeshComponent and one for the PositionCompnent).

 

In simple words that's all. I have implement the model class because I think I need default values in the near future, and maybe this class can do this, and not only store a list of components.

 

Is there a problem with this design?

Thank you

Dominik



#15 haegarr   Crossbones+   -  Reputation: 4437

Like
0Likes
Like

Posted 31 July 2013 - 12:05 PM


You've said that your entity is nothing more than an id and a reference to the model class. Anywhere you have to save this entity?

Okay, now I understand. A bit of clarification, because I haven't expressed exactly what I wanted to say: Some people implement an Entity itself as an object with some responsibility, e.g. as a message dispatcher, as a collection of its components, as carrier of the placement in world, or whatever. My implementation of Entity is, from the functional point of view, distributed over the Services', so there is not a single / root object of an entity. To identify distributed parts to belong to the same entity, the Scene allocates an identifier as first step when the entity gets instantiated. This identifier is a positive integer number with the possibly smallest value currently not yet allocated. In general, this identifier can effectively used as index into an array or as key into a search tree or similar. Each Services' gets the identifier along with the Component when it is invoked during instantiation. The Scene itself uses an array of pointers to Model.



#16 Dominik2000   Members   -  Reputation: 249

Like
0Likes
Like

Posted 01 August 2013 - 03:24 AM

Ok, now I understand. I hope it doesn't bother you, that I have took your model idea. I have implemented this, because this is a good point, for setting default values, I think.

 

So I think, this part of the engine is enough for the moment.






Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS