Jump to content

  • Log In with Google      Sign In   
  • Create Account

Game entity/component system - creation argument list


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
16 replies to this topic

#1 Juliean   GDNet+   -  Reputation: 2606

Like
2Likes
Like

Posted 13 March 2013 - 04:21 AM

Hello,

 

I've started implement an Entitiy-System as described in the first article, using an implementation as offered in the topmost answer in the second link:

 

http://www.gamasutra.com/blogs/MeganFox/20101208/88590/Game_Engines_101_The_EntityComponent_Model.php

http://gamedev.stackexchange.com/questions/17746/entity-component-systems-in-c-how-do-i-discover-types-and-construct-component

 

Works unexepectably well on the first shot, but what bugs me is this:

 

class Entity { 

void AttachComponent(ComponentType, argumentList, name)

...

}

That code snippet from Gamasutra shows a similar method that I implemented:

 

void Entity::AttachComponent(LPCSTR lpComponentType, LPCSTR lpName)
{
    Component* pComponent = component::create(lpComponentType); //get component by type
    m_mComponents[lpName] = pComponent; //register component from name
    m_mTypeComponents[lpComponentType].push_back(pComponent); //register component from type
}

Now there is one important detail left in comparison to the Gamastura-code: The argumentList. Obviously I am going to need to pass some sort of arguments to the component on creation, be it a pointer to the render-queue or the filename of the mesh I want to render. How would I implement that?

 

The only thing I really can think about is to change the method to something like this:

 

 

void Entity::AttachComponent(Component& component, LPCSTR lpComponentType, LPCSTR lpName)
{
    m_mComponents[lpName] = component; //register component from name
    m_mTypeComponents[lpComponentType].push_back(component); //register component from type
}

and then create the component outside of the entity, using its "startup/init/whatever" method to initialize it. But I don't really think this is a good approach, is it?

 

The other thing I had in mind is to make an "argumentList" - object, inheriting it for every component, passing it to its "startup"-method and therefore to the AttachComponent-method, creating it before calling it. The "arguments" would therefore be data members, and each component would need to cast the passed argumentList. Would that be a better approach?

 

If not, what else could/should I do?


Edited by The King2, 13 March 2013 - 04:47 AM.


Sponsor:

#2 noizex   Members   -  Reputation: 866

Like
2Likes
Like

Posted 13 March 2013 - 06:13 AM

If you can use C++11, you could use variadic templates and std::forward your arguments to constructor. If you can't you can still template arguments, though it will be a bit more work (you have to define templates for all possible numbers of arguments, or use some macro magic - with variadic templates its just one line of code).

 

And I don't think there is anything wrong with constructing component object outside and attaching it to entity - you may want to do some sort of pre-processing and set various information so why not do it this way? As long as you have clear state of ownership of such component it won't be a problem. 



#3 JTippetts   Moderators   -  Reputation: 8491

Like
3Likes
Like

Posted 13 March 2013 - 06:19 AM

The approach I have used before (and currently use, though in a language like Lua it is very easy to do, given the dynamically-typed nature of the language) is to use some sort of variant class. A variant is a data object that can hold anything. (within reason). That is, something like boost::any. Typically, I'll write my variants to be more specific than boost::any, though. For the argument list, I pass a map keyed on string of variant properties.

An example of this is the YAML-cpp library. When a YAML file is loaded, the elements are converted to variant types and stored in a nested tree structure. Using a library such as this as an intermediary makes it easy to load your argument list from a data file, then just pass the tree structure to your various factories to build components.

#4 EWClay   Members   -  Reputation: 659

Like
2Likes
Like

Posted 13 March 2013 - 07:06 AM

An example of this is the YAML-cpp library. When a YAML file is loaded, the elements are converted to variant types and stored in a nested tree structure. Using a library such as this as an intermediary makes it easy to load your argument list from a data file, then just pass the tree structure to your various factories to build components.


I've done something like this using JSON as the data format. An entity can be defined by a set of components, each with its own data, all stored in the same JSON object. The entire entity can then be constructed from the parsed object.

It is necessary to be able to create entities in code as well, since not all entities can be defined before the game starts. I don't see a problem with any of the above approaches.

#5 landagen   Members   -  Reputation: 376

Like
0Likes
Like

Posted 13 March 2013 - 07:47 AM

You could also pass in a map object.  The map object would contain all required inputs and the initialize function would just read the map object for the values it needs. 





#6 larspensjo   Members   -  Reputation: 1540

Like
4Likes
Like

Posted 13 March 2013 - 07:58 AM

See example in the EntityX library how this can be done. It is C++11 based on variadic template arguments, which is a little difficult. But it also supports that you construct the component first, and then attach it.


Current project: Ephenation.
Sharing OpenGL experiences: http://ephenationopengl.blogspot.com/

#7 Juliean   GDNet+   -  Reputation: 2606

Like
0Likes
Like

Posted 13 March 2013 - 08:16 AM

Thanks for all the answers!

 

If you can use C++11, you could use variadic templates and std::forward your arguments to constructor. If you can't you can still template arguments, though it will be a bit more work (you have to define templates for all possible numbers of arguments, or use some macro magic - with variadic templates its just one line of code).

 

The varidic templates definitily sounded interesting, but unfortunately they are not supported by Visual Studio 2012... If I were to write templates for all the possible number of arguments given, would I also need to overload the function for each of these templates (Arguments1<>, Arguments2<>, ...) or is there some way to templatize (is this a word) the templates so that I can have "Component::Component(Arguments args)" for me to pass as many arguments as I want?

 

 

And I don't think there is anything wrong with constructing component object outside and attaching it to entity - you may want to do some sort of pre-processing and set various information so why not do it this way? As long as you have clear state of ownership of such component it won't be a problem.

 

I'll pick that approach if I fail to implement something else, yet I still prefer the more "automated" approach since I've already got a pretty interface around it using a string to identify the component...

 

 

The approach I have used before (and currently use, though in a language like Lua it is very easy to do, given the dynamically-typed nature of the language) is to use some sort of variant class. A variant is a data object that can hold anything. (within reason). That is, something like boost::any. Typically, I'll write my variants to be more specific than boost::any, though. For the argument list, I pass a map keyed on string of variant properties.

 

Sounds interesting too, is there any guide/tutorial or sample code on how to implement a variant? I googled a bit but didn't find anything related to my problem.

 

As for the file format, thats not really what I am looking for right now, I'll consider it some other time, but for beginning I think I'll stay code only.

 

 

You could also pass in a map object. The map object would contain all required inputs and the initialize function would just read the map object for the values it needs.

 

Only problem is that the arguments need to be of different type - one (Model) might need a pointer to my RenderQueue, and Material-ID and a Filename, the other one (ParticleEmitter) might want itself a map of force-fields, and a bunch of parameters regarding the particle generation/handling. A map with variant types would do, but simple a map isn't the solution...

 

See example in the EntityX library how this can be done. It is C++11 based on variadic template arguments, which is a little difficult. But it also supports that you construct the component first, and then attach it.

 

Pretty cool, I'll have a closer look to see how they implemented it (though like I said, variadic templates aren't an option for me, unfortunately). Speaking of which, in your example, they define components like Position and Direction. I though more about having something like a WorldComponent, RenderComponent, PhysicsComponent ... Therefore, I would also apply logic to the components, and not rely completely on systems like EntityX does. From what I've read some people tend to use components just as data containers, while others prefer more components with more logic like I wanted to have. Is there any huge advantage/disadvantage from any of those approaches or is it more a thing of preferrence? What would you recommend me to do starting out?


Edited by The King2, 13 March 2013 - 08:31 AM.


#8 noizex   Members   -  Reputation: 866

Like
1Likes
Like

Posted 13 March 2013 - 09:01 AM

Thanks for all the answers!

 

If you can use C++11, you could use variadic templates and std::forward your arguments to constructor. If you can't you can still template arguments, though it will be a bit more work (you have to define templates for all possible numbers of arguments, or use some macro magic - with variadic templates its just one line of code).

 

The varidic templates definitily sounded interesting, but unfortunately they are not supported by Visual Studio 2012... If I were to write templates for all the possible number of arguments given, would I also need to overload the function for each of these templates (Arguments1<>, Arguments2<>, ...) or is there some way to templatize (is this a word) the templates so that I can have "Component::Component(Arguments args)" for me to pass as many arguments as I want?

 

About MSVC2012 - it supports variadic templates and few more C++11 features when you install CTP_Nov2012 (http://www.microsoft.com/en-us/download/details.aspx?id=35515) toolset and set it in your project options. Its CTP so its for testing but I use it and it works without problems. IMO having variadic templates is worth it smile.png

 

If you want to pass it around with varied number of arguments, that will be hell lot of templating. I was thinking about making a component factory with varied number of arguments that are then passed to constructor, and this factory gives you component. Something like:

 

Component* comp = compFactory->CreateComponent(SPATIAL, arg1, arg2);
Component* comp = compFactory->CreateComponent(RENDERABLE, arg1);
Component* comp = compFactory->CreateComponent(COLLIDABLE, arg1, arg2, arg3); 

 

Above code could be achieved by templating factory function "CreateComponent" to support different numbers of arguments that are passed to constructor, but as I said without variadics its a bit of crazy template magic (check this article and source code for really nice factory implementation: http://archive.gamedev.net/archive/reference/articles/article2097.html)

 

This can happen on higher level than entity, in some class that creates entities out of components. Does entity really need to know how to construct components (and itself?) or it should be responsibility of class above entity, that picks needed components (probably out of templates later, so its more automated) and creates another instance of entity by attaching necessary components. In some Entity System implementations, there is no such thing as Entity class, but its just a tag or id under which certain components are glued together by some "EntitySystem/Manager". 

 

In my implementation, I kept GameObject class but its only responsibility is to be a container of component pointers that also keeps a bitmask of what component types are attached to that entity (I use it as for very fast lookups, as such bitmask works like a key that you can latet bit-and against some mask of required components and quickly filter entites that meet these criteria).


Edited by noizex, 13 March 2013 - 09:09 AM.


#9 JTippetts   Moderators   -  Reputation: 8491

Like
1Likes
Like

Posted 13 March 2013 - 09:28 AM

Sounds interesting too, is there any guide/tutorial or sample code on how to implement a variant? I googled a bit but didn't find anything related to my problem.
 
As for the file format, thats not really what I am looking for right now, I'll consider it some other time, but for beginning I think I'll stay code only.

YAML-cpp is sample code for how it is achieved, that's why I linked to it. You don't need to use a library to learn from it.

#10 Juliean   GDNet+   -  Reputation: 2606

Like
0Likes
Like

Posted 13 March 2013 - 10:36 AM

YAML-cpp is sample code for how it is achieved, that's why I linked to it. You don't need to use a library to learn from it.

 

Oh, my bad. Reading about JSON files in EWClays post actually before reading yours confused me from the real purpose of your link... I'll definitaley check it out, along with the variadic templates and decide which fits my need better afterwards

 

 

About MSVC2012 - it supports variadic templates and few more C++11 features when you install CTP_Nov2012 (http://www.microsoft.com/en-us/download/details.aspx?id=35515) toolset and set it in your project options. Its CTP so its for testing but I use it and it works without problems. IMO having variadic templates is worth it smile.png

 

Ah, that worked, thanks... so I've tried to implement it, but don't know how to go any further.

 

template<typename... args>
class Arguments {};

class Component
{
public:
    template<typename... Args>
    virtual void StartUp(Arguments<Args...>) = 0; //???
    virtual void Update(void) = 0;
    virtual void HandleMessage(const Message& message) = 0;
};
 

So what I tried to make as an "Argument" class the takes how much arguments are needed, which would then get passed to the StartUp()-Function (if in the Entity or somewhere else shouldn't matter for now...). Only problem is that I get this error:

 

1>t:\dark mountain engine\repo\editor\component.h(13): error C2898: 'void Component::StartUp(Arguments<args...>)' : member function templates cannot be virtual (Component.cpp)

 

Obviously I can't do what I wanted to achieve here (have a function that takes a variadic template and by overloading that function each component will then decide how to use the argumentes. Is there just some syntax thing I didn't do, or is this whole approach faulty? If so, could you give me a short code example of how I'd achieve that? (variadic) templates are fairly new to me, and most examples I found in the internet are (seem to be) dealing with complete different things than what I want to achieve here, or I probably just don't understand it well enough...

 

Thanks in advance!

 

Edit:

Does entity really need to know how to construct components (and itself?) or it should be responsibility of class above entity, that picks needed components (probably out of templates later, so its more automated) and creates another instance of entity by attaching necessary components.

 

Well the Entity wouldn't really construct the component. It would just request an component object and pass its "StartUp()"-function the parameters specified, and the component itself takes care of its construction.


Edited by The King2, 13 March 2013 - 11:12 AM.


#11 BeerNutts   Crossbones+   -  Reputation: 2944

Like
0Likes
Like

Posted 13 March 2013 - 01:08 PM

Well the Entity wouldn't really construct the component. It would just request an component object and pass its "StartUp()"-function the parameters specified, and the component itself takes care of its construction.

 

If you are hoping to create the components by reading a data file, then you may want to simply pass in some kind of property map instead of a specific argument list.


For example, if your player entity may define the components it uses, like this:

<Entity type=Player>
  <Component Vitals Health=100, Mana=1000 />
  <Component Renderable Mesh=PlayerMesh.mesh />
  ...
</Entity>

Then you maybe could simple create a properties type to pass into component creations, like this:

 

typedef std::map<std::string, std::string> TComponentProperties;
 
void Entity::AttachComponent(LPCSTR lpComponentType, LPCSTR lpName, TComponentProperties propeties)

 

Obviously, your Vitals component would then access the properties via Properties["Health"] and Properties["Mana"]

 

Just an idea.


My Gamedev Journal: 2D Game Making, the Easy Way

---(Old Blog, still has good info): 2dGameMaking
-----
"No one ever posts on that message board; it's too crowded." - Yoga Berra (sorta)

#12 larspensjo   Members   -  Reputation: 1540

Like
0Likes
Like

Posted 13 March 2013 - 01:44 PM

Speaking of which, in your example, they define components like Position and Direction. I though more about having something like a WorldComponent, RenderComponent, PhysicsComponent ... Therefore, I would also apply logic to the components, and not rely completely on systems like EntityX does. From what I've read some people tend to use components just as data containers, while others prefer more components with more logic like I wanted to have. Is there any huge advantage/disadvantage from any of those approaches or is it more a thing of preferrence? What would you recommend me to do starting out?

 

This is an interesting question, and I have some views, but I am not sure. I suppose there are no exact right or wrong here, and there are certainly special cases which can be used to show the advantages from all sides.

 

You can choose to see a component from the view of the producer, or the view of the consumer. Take position of an entity, for example. If it is seen from a producer, it would be a world position. If it is seen from a consumer, like a render system, then it would maybe want to use screen coordinates. However, an entity with a world position can be used by many consumers, e.g. collision detection, while a screen coordinate only can be used by the render system. From that point of view, I prefer to use the "raw" original data. Another example could be if you switch between "map" mode and normal play mode. You would maybe use two different render Systems, which both could use the same World Position data.

 

Now take RenderComponent. I am not sure exactly what the purpose would be, but I suppose this is a key to the Render system to make it know that this entity shall be rendered. The component could contain a pointer to a draw function. This sounds reasonable. Or, you could do it with specialized render systems. E.g. a render system that renders all monsters. This system thus knows that monsters shall be rendered, and what render function to use.

 

The key here is the principle of minimizing dependencies. That is, as few systems and logic as possible should need to know about other systems.

 

Regarding having logic or not in the component, I think the basic idea is to have as little as possible. Not even getters/setters. I would also be grateful for general guidelines here. I think a guideline is to keep components as simple as possible. It should contain lots of data, with states that depend on each other.


Current project: Ephenation.
Sharing OpenGL experiences: http://ephenationopengl.blogspot.com/

#13 EWClay   Members   -  Reputation: 659

Like
0Likes
Like

Posted 13 March 2013 - 03:59 PM

Two ways of looking at this if you aren't a gameplay programmer:

If I'm a game designer, and I want to create an entity by picking out components, chances are I don't care at all what data is stored on them. What I want is behaviour. Position, velocity and collision are too fine-grained. More likely I want a physics component, a model/sprite component, an AI component, or maybe a specialised component for that particular object (e.g a door).
From that perspective associating logic with components makes perfect sense.

From an engine programmers perspective, I want nothing to do with the whole thing. No way is my physics engine going to navigate a component system to find its data. The physics component can have a pointer or ID to something in the engine, and that's it. So you can remove such systems from the picture entirely. The physics code and probably the rendering code exists at a lower level.

#14 Juliean   GDNet+   -  Reputation: 2606

Like
0Likes
Like

Posted 14 March 2013 - 05:09 AM

If you are hoping to create the components by reading a data file, then you may want to simply pass in some kind of property map instead of a specific argument list.

 

No, I'm not really planning on reading a datafile, someone else came up with the idea but I don't find it particullary fitting for my needs.

 

Regarding having logic or not in the component, I think the basic idea is to have as little as possible. Not even getters/setters. I would also be grateful for general guidelines here. I think a guideline is to keep components as simple as possible. It should contain lots of data, with states that depend on each other.

 

That was my plan too. Like in the gamasutra article, each of my components would only consist of (public) methods from the Component-interface - therefore a StartUp(), Update() and CleanUp() - method + some simple messaging system - no getters, setters or complex external logic used. I'm just wondering whether I should e.g. have my rendercomponent contain generic renderdata and have a rendersystem extract and use that data or if the RenderComponent should contain that data as well as send it to the renderqueue. I think I'll go with the latter, it seems most attractive to me.

 

From an engine programmers perspective, I want nothing to do with the whole thing. No way is my physics engine going to navigate a component system to find its data. The physics component can have a pointer or ID to something in the engine, and that's it. So you can remove such systems from the picture entirely. The physics code and probably the rendering code exists at a lower level.

 

You are completely right, I'd have it that way, too. Rendering, physics etc.. exists at a lower level, my RenderComponent would simply issue renderdata to a queue. My physics component would have a pointer to any physic object, to apply state changes etc.. to it.

 

Well from all I get I'm not too offttrack. I'll go with the approach of not having systems but just components, if it doesn't work out, I'll change things later. Some help with the varidic templates would still be cool though, I can't quite wrap my head around it. Anyone?



#15 Juliean   GDNet+   -  Reputation: 2606

Like
0Likes
Like

Posted 16 March 2013 - 05:35 AM

So after studying the EntityX source code extensively I finally figured out how to use the varidic templates. But there is one problem now, which happens if I try to attach an component outside of the entity class. Let me show you how the AttachComponent()-method is set up in Entity.h:

 

#pragma once
#include "Components.h"

using namespace std;

class Entity
{
    typedef vector<BaseComponent*> componentVector;

public:

    Entity(void);
    virtual ~Entity(void);

    template<typename C, typename... Args>
    void AttachComponent(Args&& ... args);

private:

    componentVector m_vComponents;
};
 

 

in Entity.cpp

#include "Entity.h"

template<typename C, typename ... Args>
void Entity::AttachComponent(Args&& ... args)
{
    BaseComponent* pComponent = new C(args...);
    m_vComponents.push_back(pComponent);
}

 

now the component itself in Component.h:

#pragma once

struct BaseComponent
{
    typedef int Family;

protected:
    static Family family_count;
};

template <typename Derived>
struct Component : public BaseComponent {
    static Family family(void);
};

template<typename C>
BaseComponent::Family Component<C>::family(void) {
    static BaseComponent::Family Family = family_count++;
    return Family;
}
 

 

And my "PositionComponent" in Components.h:

struct PositionComponent : Component<PositionComponent>
{
    PositionComponent(int x, int y) { m_x = x; m_y = y; };

    int m_x, m_y;
};

 

Now everything works fine if I call "AttachComponent<PositionComponent>(1, 1)" in the constructor of Entity, or any other functions of entity. As soon as I create an actual entity and call "entity.AttachComponent<PositionComponent>(1, 1)" I get an linker error:

 

error LNK2019: unresolved external symbol "public: void __thiscall Entity::AttachComponent<struct PositionComponent,int,int>(int &&,int &&)" (??$AttachComponent@UPositionComponent@@HH@Entity@@QAEX$$QAH0@Z) referenced in function "public: virtual void __thiscall SceneEditor::Show(void)" (?Show@SceneEditor@@UAEXXZ)

 

Any ideas why this is happening and how I can resolve it? Ok, I got it. I actually had to define the AttachComponent<>()-function in the header instead of the cpp-file. I'd still be happy if someone explained to me why this was necessary? Is it a thing I generally need to do with templates? Or is it just a variadic-template-thingy? Or is it a bug from the CTP_Nov2012 toolset?


Edited by The King2, 16 March 2013 - 05:42 AM.


#16 EWClay   Members   -  Reputation: 659

Like
1Likes
Like

Posted 16 March 2013 - 06:36 AM

It's a thing you generally need to do with templates.

Sometimes, if you know what the template parameters are going to be, you can instantiate the functions you need in the cpp file. Usually you don't know, and neither does the compiler until you use the template. At that point it needs the full definition.

This can slow down compilation times since many translation units end up instantiating the same template. There were plans to allow template definitions to live in the implementation file using the export keyword, but these were dropped. Another option is to include the definition but tell the compiler not to instantiate a particular use of a template using extern, and explicitly instantiate it somewhere else.

#17 Juliean   GDNet+   -  Reputation: 2606

Like
0Likes
Like

Posted 16 March 2013 - 08:28 AM

Thanks, good to know. So I suppose that its an expected side-effect of templates that all intelli-sense error correcture tools fail for templated functions too? Sure, it makes sense since we don't know what "typename C" e.g. is gonna be, but I can write syntax garbage all the way and it won't tell me till compiling ... not a huge deal, just slightly irritating at first.






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