How to avoid virtual functions in C when designing an Entity System

521 views Asked by At

I want to design an entity system for my game using just C and a small subset of C++. Many people do this using inheritance, but I stumbled across tagged unions and learned that you can achieve a similar result this way, because virtual functions are slow for games(so I heard from Casey Mouratori and Jon Blow, two game devs from which I draw a lot of inspiration).

struct Unit {
   int type;
   int hp;
   union {
      struct {
        int kingID; 
      } soldier_data;

      struct {
        string name;
      } king_data;
   };
}

Jon Blow avoids virtual functions using some features from his new language Jai which is not yet released, so the example above is my only idea.

For example I could write only one Update function and use the type to differentiate between entities. This would be a very large function but hey, we avoid virtual functions.

The things is my game contains lets say soldiers and trees. Trees are static and do mostly nothing, so I would write another lighter struct for trees entities, to save some memory, and use a unions to store different types of trees:

struct TreeEntity {    
      Texture* texture;    
      union {
        struct {
           int height;
        } pineTree_data;
        struct {
           string name;
        } coconutTree_data;
   }
}

The problem I'm facing is what if both soldiers and trees are clickable? If I had used inheritance I would simple have a function Entity* selectEntity(), and check the instance type after, but with my approach I'm a kind of lost.

Is my approach bad? Should I stick to virtual functions or there is a way to handle this?

1

There are 1 answers

0
John Zwinck On

You ask how to create a generic Entity* selectEntity() function which can return either a Unit* or a Tree* (or perhaps other things).

You can do it by returning a base-class pointer. Unit and Tree inherit from Entity, and Entity can contain a small enum like:

enum class EntityType : uint8_t {
    Unit, Tree, // ...
};

If you don't want to use inheritance, you can instead just have a common initial subsequence of member variables in every type of Entity, like this:

struct Unit {
    EntityType type;
    // ...
};

This is equivalent to the inheritance version in terms of memory layout, and you could return an EntityType* from selectEntity(), which will actually point to the first member of a Unit or Tree.