6
\$\begingroup\$

I'm making a game that uses an Entity/Component architecture basically a port of Artemis's framework to c++,the problem arises when I try to make a PlayerControllerComponent, my original idea was this.

class PlayerControllerComponent: Component {
public:
    virtual void update() = 0;
}; 

class FpsPlayerControllerComponent: PlayerControllerComponent {
public:
    void update() {
        //handle input
    }
};

and have a system that updates PlayerControllerComponents, but I found out that the artemis framework does not look at sub-classes the way I thought it would. So all in all my question here is should I make the framework aware of subclasses or should I add a new Component like object that is used for logic.

\$\endgroup\$
5
  • 1
    \$\begingroup\$ Artemis Entity Framework does not encourage making functions in your components. Components are pure data collections. The Systems perform updates and what not. I've ported Artemis myself to C++ and in my tests I did all my logic is Systems. Your update shouldn't be there. \$\endgroup\$
    – Sidar
    Commented Oct 23, 2012 at 3:50
  • \$\begingroup\$ So do you think I should create a new System for each type of player be it first-person third-person or top-down \$\endgroup\$
    – aaron
    Commented Oct 23, 2012 at 3:52
  • \$\begingroup\$ I faced the same problem and didn't really got to it. The problem why it doesn't work is because each component is added with a new bit id. Your component could simply have an enumerator : FPS, TPS, TD. And a variable of that enumerator. In your system you simply check which enumerator value is set and perform logic. Systems are your bulk of updates. Try to separate logic from your component as much as possible. \$\endgroup\$
    – Sidar
    Commented Oct 23, 2012 at 3:55
  • \$\begingroup\$ Ya having the component with an update was to avoid the massive hard-coded switch statements.But thanks for helping. \$\endgroup\$
    – aaron
    Commented Oct 23, 2012 at 4:05
  • \$\begingroup\$ as long as you want just one component per class hierarchy (only one PlayerControllerComponent) its easy to rewrite artemis to do this. You just need to change Mapper to give you bits like this PlayerController = 000001, FspPlayerController = 0000011 (that way FpsPlayerController "contains" flag for PlayerControlle). And rewrite check for component to (Flags & desiredFlags != 0). But as Sidar say, artemis does not wantyou to do that, but artemis solution to have "data" components with no data and only as markers that some system should work with them is not good either... \$\endgroup\$
    – Kikaimaru
    Commented Oct 23, 2012 at 9:34

6 Answers 6

8
\$\begingroup\$

Components can have functions. What you want to try to stay away from is having a lot of functionality in them. The functions should be simple things. For example, my inventory component has a function for adding an item to the inventory:

public boolean addItem(ItemAttribute item) {
    if (items.size() < maxItems) {
        items.add(item);
        return true;
    }
    return false;
}

It's more than a setter function, but doesn't have anything sophisticated. I would leave the function creation up to the individual deploying the framework though. Don't force an update function on them, let them decide what to implement.

Make sure you check out the example games that are on the Artemis site. Those should help you see how components are employed.

Also, check out this answer I gave with Artemis, specifically, in mind.

So with your example. The player control component wouldn't need much data in it at all. Nor functionality. However, simply attaching it to an entity that also had a position would make the PlayerControlSystem pick up that component and process the incoming keyboard input. Sometimes you can think of components like flags for systems. The component is really only there so that the system adds that entity to its processing group.

Components should be objects that the system can access. When a system accesses a component it should be able to access its data and whatever simple functionality it has.

There should be a different system for each control type and a different component for each control type. If you want to compress that into a single system or a single component you can, but it'll just end up in a switch statement like described in Sidar's answer.

\$\endgroup\$
5
  • \$\begingroup\$ Not sure if you're also replying indirectly to what I said, but, by functions I meant functions that perform logic. Your function doesn't perform logic on a level where the system becomes nothing but a " update caller". Also his problem is component inheritance. Systems don't work with inheritance( because of bit flagging). A problem I faced myself. Btw I would just let the system add an Item. \$\endgroup\$
    – Sidar
    Commented Oct 23, 2012 at 7:29
  • 1
    \$\begingroup\$ +1 I really like the idea of seeing components as tags, in the sense that they are a way for the system identify which entities to process. \$\endgroup\$
    – Asakeron
    Commented Oct 23, 2012 at 12:47
  • \$\begingroup\$ @Sidar Somewhat. I believe we're mostly in agreement. I was just making it clear that they could have simple functions because your example doesn't show that. I wouldn't (and don't) have a system responsible for adding the item. Since adding an item to the inventory can happen in multiple systems, I'd just be repeating the same code over and over. \$\endgroup\$
    – House
    Commented Oct 23, 2012 at 13:56
  • \$\begingroup\$ I honestly still have to wrap my head around artemis, eventhough I ported it over. lol \$\endgroup\$
    – Sidar
    Commented Oct 23, 2012 at 14:18
  • \$\begingroup\$ Completely right, using components as essentially flags. I would also say that OP could put his two components together in to something like PlayerControlSchemeComponent that has some value which states if it's in 3rd or 1st person. Then have the PlayerControlSystem act accordingly. \$\endgroup\$
    – Mike Cluck
    Commented Oct 25, 2012 at 14:41
1
\$\begingroup\$

In my Artemis port it would look something like so:

My component:

 class PlayerControllerComponent : public artemis::Component{
       enum PlayerControl{
         TPS,FSP,TD
       };
      public:
       PlayerControl pc;
       PlayerControllerComponent(PlayerControl pc){
         this->pc = pc;
       }

    };

And the system would be something like so:

class PlayerControlSystem: public artemis::EntityProcessingSystem {

    private:

        artemis::ComponentMapper<PlayerControllerComponent > pcm;

    public:
        PlayerControlSystem() {

            setComponentTypes<PlayerControllerComponent>();
        };

        virtual void begin() {
            //before logic
        }

        virtual void end() {
            //after logic
        }

        virtual void initialize() {
            pcm.init(*world);
        };

        virtual void processEntity(artemis::Entity &e) {

            PlayerControllerComponent & ent = *pcm.get(e);

                   switch(ent.pc)
                   {
                      case TPS:
                        //call internal function for TPS
                       break;
                        case FPS:
                        //call internal function for FPS
                       break;
                          case TD:
                        //call internal function for TD
                       break;

                   };
        };
};

I have to admit that I'm still trying to wrap my head around Artemis completely. But since the bitset for components is so specific systems are more 1:1 with components. So this is how I would do it.

\$\endgroup\$
5
  • \$\begingroup\$ A component map and a processEntity function that has to look up keys is entirely opposite of good data-oriented design, and hence you're missing the entire point of entity systems. Read gamesfromwithin.com/data-oriented-design and then come back to your design to see where you're going wrong, and how you can make it better. You don't need entity systems for DOD (which is why I don't like the entity system fad), but if you're using an entity system, you better be thinking about DOD, or you're just making your job harder for yourself for no gain. :) \$\endgroup\$ Commented Oct 23, 2012 at 17:58
  • \$\begingroup\$ @SeanMiddleditch It's how Artemis Framework does it. Not sure what you mean by " look up keys". All it does is directly retrieve the component by entity id. Have you even looked at the framework? \$\endgroup\$
    – Sidar
    Commented Oct 23, 2012 at 20:25
  • \$\begingroup\$ @SeanMiddleditch The mapper is not a data structure such as a "map/hashmap". If that's what you were implying. It actually points to a bag containing the Entity component. Each index is preserved for each unique entity id and it's components respectively. No look up keys are performed. It simply reads the entity id and uses that as an index to retrieve the component. The bit flags in each entity guarantees that the system will register the entity( if the system bitflag are compatible) and ensures that the mapper will always get a non-null component object. \$\endgroup\$
    – Sidar
    Commented Oct 23, 2012 at 20:48
  • \$\begingroup\$ I had not previously looked at Artemis, and you're right that I shouldn't have just assumed the Mapper was a lookup table. Apologies. That said, now that I've looked at Artemis... you should still read that link on Data-Oriented Design. Java is not really capable of being used for proper DOD, and a straight C++ port without understanding the Java-imposed limitations of Artemis and the motivations for Entity Systems is likely to lead to a system that has all the limitations of ES with none of the benefits. \$\endgroup\$ Commented Oct 24, 2012 at 1:44
  • \$\begingroup\$ @SeanMiddleditch Of course a direct port isn't efficient. My goal was to see if i could make it work in C++. And I somewhat achieved that. Eventually my intentions were to rebuild some aspects. Just don't have the time. \$\endgroup\$
    – Sidar
    Commented Oct 24, 2012 at 10:49
1
\$\begingroup\$

Your input component doesn't need any logic. It needs a control scheme since that's going to differ among entities but that's all. The rest should be handled by the system. It's the same as it is for rendering and physics, just detect which entity has the component and act upon it. Putting input handling logic in the component is a dead end because there's only so much it can do before you start creating hacks.

Trying to avoid one switch statement at a cost of creating inheritance chains is a poor decision since you gain absolutely nothing. You need the same logic for handling the input. The switch statement was created for situations like this one, it doesn't make your application less robust. Also adding a new type of control is going to be easier with a switch statement; just add one more case and a function to handle it. Otherwise you're bloating your inheritance chains more and relying on run-time type checking, again, gaining nothing in return.

\$\endgroup\$
1
\$\begingroup\$

My suggestion is to use abstract pure virtual class as Interfaces OR template policy. Your system can accept only components that inherit from a specified interface OR that implements template policies. In this way your system will be aware of the fact that your component has an Update function in this way.

you could do something like this:

class Test
{
    std::vector<Interface *> items;
    public: 

        void AddComponent(T& param) 
        {
            items.push_back(&param);
        };

        void Update()
        {
            for (unsigned i = 0; i < items.size(); i++)
                items[i]->Update();
        }
};

OR

template <class T>
class Test
{
    std::vector<T *> items;
    public: 

        void AddComponent(T& param) 
        {
            items.push_back(&param);
        };

        void Update()
        {
            for (unsigned i = 0; i < items.size(); i++)
                items[i]->Update();
        }
};

The second one does not need the use of the interfaces, however the compiler will not compile the code if T does not implement a public Update function.

\$\endgroup\$
0
\$\begingroup\$

I would create the FpsPlayerControllerComponent as a sub-class of Component and create a system which process the entities that contain this component.

If you find you need another PlayerController, say, TpsPlayerController, just create a new subclass of Component and a new system, this way, your code will not need switchs or ifs.

Systems are generally more generic than that, but I think the controller system can be implemented that way, because you will probably not need FpsPlayerController and TpsPlayerController at the same time.

\$\endgroup\$
0
\$\begingroup\$

Logic in these systems? There is almost none. :) After chatting for some time about this problem with other GD.SE users, we came to a conclusion that many of us weren't really convinced by these systems that everyone's an expert with.

The original idea of components in programming was somewhere along these lines:

class MyRendererComponent { function MyFunction(){..}; data MyData; };
class MyOtherComponent { function MyOtherFunction(){..}; data MyOtherData; }
class MyEntity { MyRendererComponent C; MyOtherComponent C2; ... };

With this approach, components behave any way the entities want them to behave. Put logic into this the same way you'd put it into entities. There might appear to be some redundant glue code in this approach but all things considered, this one actually proves to require less work than any other system.

All of the other approaches are mainly meant for data-driving the engine heavily (enabling entity composition in game editors), which is what most people don't actually need (most don't even have editors). Even GTA3 didn't need it. And I'd most probably be right if I said that you don't have a bigger game in development...

\$\endgroup\$
6
  • \$\begingroup\$ well you still need something that calls MyFunction, and logic of that calling is in most cases complex (like sorting, frustum culling etc.) and when you're at that, there is not much difference between writing RenderingSystem.MyComponentMyFunction() and calling that function on component \$\endgroup\$
    – Kikaimaru
    Commented Oct 24, 2012 at 13:41
  • \$\begingroup\$ @Kikaimaru Yeah. That's inevitable in any system. It's called "glue code". And when I'm at it, I would like to keep the state where it is. In your example, the state is unnecessarily moved. In other words, you forgot about "MyData". \$\endgroup\$
    – snake5
    Commented Oct 24, 2012 at 13:45
  • \$\begingroup\$ All the data are in the component, but all the logic is in system. Thats what artemis' systems are used for. \$\endgroup\$
    – Kikaimaru
    Commented Oct 24, 2012 at 13:53
  • \$\begingroup\$ @Kikaimaru If you really want to implement something like that, all it takes is creating a simple function (it can be anywhere) that iterates through the data of available components. \$\endgroup\$
    – snake5
    Commented Oct 24, 2012 at 14:12
  • \$\begingroup\$ Well and? The points is that what you call "glue code" is called System in Artemis. So its not like you found easier way to do this, you just call it differently. But in your approach where logic is on components, your components needs to see code they really dont need (so changing your approach to rendering will be more complex then simply chaning one class - system), and in the rendering example, almost no code will be on component, since rendering is too complexe to be done like this - you will be left with some class that will like set vertexbuffer and call drawindexes... \$\endgroup\$
    – Kikaimaru
    Commented Oct 24, 2012 at 14:27

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .