Game entity/component system - creation argument list

Started by
15 comments, last by Juliean 11 years, 1 month ago

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)

Advertisement

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.

[size=2]Current project: Ephenation.
[size=2]Sharing OpenGL experiences: http://ephenationopengl.blogspot.com/
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.

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?

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?

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.

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.

This topic is closed to new replies.

Advertisement