I recently came across the Entity Component System that is frequently used in game engines. I decided to implement it myself in C++, but quickly ran into a familiar problem. It all began innocent enough. I started with the following classes:
class Entity
{
private:
int entityId;
std::map<std::string, Component*> components;
public:
Entity();
~Entity();
void AddComponent(Component *component);
void RemoveComponent(std::string name);
bool HasComponent(std::string name);
Component* GetComponent(std::string name);
};
class Component
{
public:
int componentId;
};
I then decided to create some specific component types:
class Input : public Component
{
public:
void process();
}
class Physics : public Component
{
public:
void update();
}
I began to test this out. I created an entity:
Entity *entity = new Entity();
entity->AddComponent(new Input());
entity->AddComponent(new Physics());
This is where things started going wrong though. I then thought about how I would get components from an entity. What if I wanted to do something like:
Physics *physics = entity->GetComponent("Physics");
physics->update();
But GetComponent("Physics") returns the base class Component, not the derived class Physics! I did some searching on the internet but couldn't find an example in c++ that showed how to get around this problem. After taking a look at what Unity does, I discovered that they seem to just perform a down cast. For example in Unity the code (C#) would be:
Physics physics = entity.GetComponent("Physics") as Physics;
Isn't this down cast bad? How does one get around this in C++ when designing an Entity Component System? Or how does one perform the downcast?
The components that make up an entity are keyed on their type. If an ECS makes you write entity.get("Foo") as Foo then that's already a design weakness; it should make you write entity.get<Foo>().
In C++, you could write the code like this:
class Entity {
std::unordered_map<std::type_index, std::unique_ptr<Component>> components;
public:
template <typename C>
void AddComponent(std::unique_ptr<C> p) {
// can't remember if unique_ptr allows this conversion
components[typeid(C)] = std::move(p);
}
template <typename C>
C& GetComponent() {
// You *know* this cast has to work. You should handle null here though.
return *static_cast<C*>(components[typeid(C)].get());
}
};
Though note that it is rather unusual for an entity to hold its components at all. Usually you have a world, and it holds all components. An entity merely keys into the world's collection of components.
When your world has all components, then you can give it one collection per component type. (Indeed, that is a favored representation, since it often gives you better memory access patterns.) Then you don't need an abstract base class for your components anymore.
ECS revolves around casting and duck typing. But you can use a dynamic_cast for safety if you want (possibly just for debug builds), and hide it from the client so that you get this type of syntax:
Physics* physics = entity->get<Physics>();
And you got more safety than I do! Mine is implemented in C and provided through a C API to be used through plugins and scripts, so I have to actually cast from void pointers (but just in one central place).
It tends to not be a big deal in practice since the client code tends to ensure the integrity of what's going on since they're expressing the component type they want to retrieve. In C++ you can even do static checks at compile-time to make sure they are retrieving an actual component type and not some other type. I wouldn't recommend having them specify strings as you have though, since then a typo wouldn't lead to any type of compile-time error.
COM as used in DirectX also revolves around these casts to retrieve an interface. When it's unified through an interface query as in COM or a component query as in ECS, then it tends to not be a big deal in practice (people don't trip up over it as with a codebase that downcasts things sporadically with deep inheritance hierarchies).
A lot of the traditional practices revolving around OOP don't necessarily apply to ECS since it's such a radically different way of approaching architectures. For example, established OOP practices tend to strongly encourage information hiding, discourage downcasts, and SOLID suggests that dependencies flow towards abstractions.
Well, in an ECS, we have downcasts to retrieve components (but it's unified), we have components just exposing their data in raw form (but not a big deal since only a few systems access this data, so the scope of the data is narrow), and dependencies flow towards data, not abstractions (but somehow the design is much easier to keep stable anyway in at least game or game-like domains due to its enormous flexibility).
Or how does one perform the downcast?
One way is to store the key used to fetch the component as part of the component type, like:
struct Physics
{
enum {id = ...};
};
Then you can do like:
template <class Component>
Component* Entity::get()
{
const int id = Component::id;
// Find for the component from the id.
return component_ptr;
}
... or as Sebastian Redl points out, you can just use typeid (I forgot how to do this all statically due to that C API, since I have scripters and plugins adding new component types on the fly).
Related
I have a collection of classes and functions which can interact with one another in rich and complex manners. Now I am devising an architecture for the top-level layer coordinating the interaction of these objects; if this were a word processor (it is not), I am now working on the Document class.
How do you implement the top-level layer of your system?
These are some important requirements:
Stand-alone: this is the one thing that can stand on its own
Serializable: it can be stored into a file and restored from a file
Extensible: I anticipate adding new functionality to the system
These are the options I have considered:
The GOF Mediator Pattern used to define an object that encapsulates how a set of objects interact [...] promotes loose coupling by by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently.
The problem I see with mediator is that I would need to subclass every object from a base class capable of communicating with the Mediator. For example:
class Mediator;
class Colleague {
public:
Colleague(Mediator*);
virtual ~Colleague() = default;
virtual void Changed() {
m_mediator->ColleagueChanged(this);
}
private:
Mediator* m_mediator;
};
This alone makes me walk away from Mediator.
The brute force blob class where I simply define an object and all methods which I need on those objects.
class ApplicationBlob {
public:
ApplicationBlob() { }
SaveTo(const char*);
static ApplicationBlob ReadFrom(const char*);
void DoFoo();
void DoBar();
// other application methods
private:
ClassOne m_cone;
ClassTwo m_ctwo;
ClassThree m_cthree;
std::vector<ClassFour> m_cfours;
std::map<ClassFive, ClassSix> m_cfive_to_csix_map;
// other application variables
};
I am afraid of the Blob class because it seems that every time I need to add behaviour I will need to tag along more and more crap into it. But it may be good enough! I may be over-thinking this.
The complete separation of data and methods, where I isolate the state in a struc-like object (mostly public data) and add new functions taking a reference to such struct-like object. For example:
struct ApplicationBlob {
ClassOne cone;
ClassTwo ctwo;
ClassThree cthree;
std::vector<ClassFour> cfours;
std::map<ClassFive, ClassSix> cfive_to_csix_map;
};
ApplicationBlob Read(const char*);
void Save(const ApplicationBlob&);
void Foo(const ApplicationBlob&);
void Bar(ApplicationBlob&);
While this approach looks exactly like the blob-class defined above, it allows me to physically separate responsibilities without having to recompile the entire thing everytime I add something. It is along the lines (not exactly, but in the same vein) of what Herb Sutter suggests with regards to preferring non-member non-friends functions (of course, everyone is a friend of a struct!).
I am stumped --- I don't want a monolith class, but I feel that at some point or another I need to bring everything together (the whole state of a complex system) and I cannot think of the best way to do it.
Please advise from your own experience (i.e., please tell me how do you do it in your application), literature references, or open source projects from where I can take some inspiration.
Sorry for the lack of a better title; I couldn't think of a better one.
I have a class hierarchy like the following:
class Simulator
{
public:
virtual void simulate(unsigned int num_steps);
};
class SpecializedSimulator1 : public Simulator
{
Heap state1; Tree state2; // whatever
public:
double speed() const;
void simulate(unsigned int num_steps) override;
};
class SpecializedSimulator2 : public Simulator
{
Stack state1; Graph state2; // whatever
public:
double step_size() const;
void simulate(unsigned int num_steps) override;
};
class SpecializedSubSimulator2 : public SpecializedSimulator2
{
// more state...
public:
// more parameters...
void simulate(unsigned int num_steps) override;
};
class Component
{
public:
virtual void receive(int port, string data);
virtual void react(Simulator &sim);
};
So far, so good.
Now it gets more complicated.
Components can support one or more types of simulation. (For example, a component that negates its input may support Boolean circuits as well as continuous-time simulation.) Every component "knows" what kinds of simulations it supports, and given a particular kind of simulator, it queries the simulator (via dynamic_cast or double dispatch or whatever means are appropriate) to find out how it needs to react.
Here's where it gets tricky:
Some Components (say, imagine a SimulatorComponent class) themselves need to run sub-simulations inside of them. Part of this involves inheriting some properties of outer simulations, but potentially changing a few of them. For example, a continuous-time sub-simulator might want to lower its step size for its internal components in order to get better accuracy, but otherwise keep everything else the same.
Ideally, SimulatorComponent would be able to inherit from a class (say, SpecializedSimulator2) and override some subset of its properties as desired. The trouble, though, is that it has no idea whether the outer simulator's most-derived type is a SpecializedSimulator2 -- it may very well be the case that SimulatorComponent is running inside a more specialized simulator than that, like a SpecializedSubSimulator2! In that case, sub-components of SimulatorComponent would need to be able to somehow get access to the properties of SpecializedSubSimulator2 that they might need to access, but SimulatorComponent itself would not (and should not) be aware of these properties.
So, we see we can't use inheritance here.
Since the only means in C++ for "discovering" sub-interfaces like this is dynamic_cast, that means the sub-components must be able to directly access the outer simulator themselves, in order to run dynamic_cast on them. But if they do this, then SimulatorComponent can't intercept any of the calls.
At this point, I'm not sure what to do. The problem isn't impossible to solve, obviously -- I can think of some solutions (e.g. hierarchical key/value dictionary maintained at run-time) -- but the solutions involves some massive tradeoffs (e.g. less compile-time checking, performance loss, etc.) and make me wonder what I should be doing.
So, basically: how should I approach this problem? Is there a flaw in my design? Should I be solving this problem differently? Is there a design pattern for this that I'm just not aware of? Any tips?
I'll try to give a partial advice. For the situation in which you need to use a simulator inheriting properties from some parent then a cloning function could be the solution. This way you can ignore what actually the original simulation was, but anyway you end up with a new one with the same props.
It may just require some basic properties (like the simulation time step) which means you need to dynamic_cast to some intermediate class in your simulator hierarcy, but not exactly spot the right one.
dynamic_cast is pure evil. Everybody knows it. Only noobs use dynamic_cast. :)
That's what I read about dynamic_cast. Many topics on stackoverflow say "use virtual functions in this case".
I've got some interfaces that reflect capabilities of objects. Let's say:
class IRotatable
{
virtual void set_absolute_angle(float radians) =0;
virtual void rotate_by(float radians) =0;
};
class IMovable
{
virtual void set_position(Position) =0;
};
and a base for a set of classes that may implement them:
class Object
{
virtual ~Object() {}
};
In GUI layer I would like to enable/disable or show/hide buttons depending on which features are implemented by the object selected by the user:
Object *selected_object;
I would do it in such a way (simplified):
button_that_rotates.enabled = (dynamic_cast<IRotatable*>(selected_object) != nullptr);
(...)
void execute_rotation(float angle)
{
if(auto rotatable = dynamic_cast<IRotatable*>(selected_object))
{
rotatable->rotate_by(angle);
}
}
but as other (more experienced ones) say, it is obvious evidence of bad design.
What would be a good design in this case?
And no, I don't want a bunch of virtual functions in my Object. I would like to be able to add new interface and new classes that implement it (and new buttons) without touching Object.
Also virtual function like get_buttons in by Object doesn't seem good for me. My Object knows completely nothing about GUI, buttons and such things.
A function like get_type that returns some enum could also solve a problem, but I don't see why self-implemented substitute of RTTI should be better than the native one (ok, it would be faster, but it doesn't matter in this case).
You've already hit the nail on the head: you're trying to get type information from an "opaque" Object* type. Using dynamic_cast is just a hack to get there. Arguably your problem is actually that C++ doesn't have what you want: good type information. But here's some thoughts.
First, if you're going to a lot of this sort of thing, you may find that you are actually shifting away from typical inheritance and your program may be better suited to a component based design pattern, as is more common in video games. There you often have a somewhat opaque GameObject at the root and want to know what "components" it has. Unity does this sort of thing and they have nice editor windows based on components attached to the GameObject; but C# also has nice type info.
Second, some other part of the might know about the concrete type of the object and can help build your visual display, causing the Object* to no longer be a bottleneck.
Third, if you do go with something like the option you're talking about, I think you will find having type id of some sort vs. the use of dynamic_cast to be more helpful, since you can then build tables to look up types to say, visual builders.
Also, you were wondering why a self-rolled type info vs. RTTI? If you are quite concerned about performance, RTTI is on for all types and that means everything could take a hit; the self-rolled option allows for opt-in (at the cost of complexity). Additionally you won't need to push this onto others if you're writing a library pulled in via source, etc.
(Refer Update #1 for a concise version of the question.)
We have an (abstract) class named Games that has subclasses, say BasketBall and Hockey (and probably many more to come later).
Another class GameSchedule, must contain a collection GamesCollection of various Games objects. The issue is that we would, at times, like to iterate only through the BasketBall objects of GamesCollection and call functions that are specific to it (and not mentioned in the Games class).
That is, GameSchedule deals with a number of objects that broadly belong to Games class, in the sense that they do have common functions that are being accessed; at the same time, there is more granularity at which they are to be handled.
We would like to come up with a design that avoids unsafe downcasting, and is extensible in the sense that creating many subclasses under Games or any of its existing subclasses must not necessitate the addition of too much code to handle this requirement.
Examples:
A clumsy solution that I came up with, that doesn't do any downcasting at all, is to have dummy functions in the Game class for every subclass specific function that has to be called from GameSchedule. These dummy functions will have an overriding implementation in the appropriate subclasses which actually require its implementation.
We could explicitly maintain different containers for various subclasses of Games instead of a single container. But this would require a lot of extra code in GameSchedule, when the number of subclasses grow. Especially if we need to iterate through all the Games objects.
Is there a neat way of doing this?
Note: the code is written in C++
Update# 1: I realized that the question can be put in a much simpler way. Is it possible to have a container class for any object belonging to a hierarchy of classes? Moreover, this container class must have the ability to pick elements belonging to (or derive from) a particular class from the hierarchy and return an appropriate list.
In the context of the above problem, the container class must have functions like GetCricketGames, GetTestCricketGames, GetBaseballGame etc.,
This is exactly one of the problems that The "Tell, Don't Ask" principle was created for.
You're describing an object that holds onto references to other objects, and wants to ask them what type of object they are before telling them what they need to do. From the article linked above:
The problem is that, as the caller, you should not be making decisions based on the state of the called object that result in you then changing the state of the object. The logic you are implementing is probably the called object’s responsibility, not yours. For you to make decisions outside the object violates its encapsulation.
If you break the rules of encapsulation, you not only introduce the runtime risks incurred by rampant downcasts, but also make your system significantly less maintainable by making it easier for components to become tightly coupled.
Now that that's out there, let's look at how the "Tell, Don't Ask" could be applied to your design problem.
Let's go through your stated constraints (in no particular order):
GameSchedule needs to iterate over all games, performing general operations
GameSchedule needs to iterate over a subset of all games (e.g., Basketball), to perform type-specific operations
No downcasts
Must easily accommodate new Game subclasses
The first step to following the "Tell, Don't Ask" principle is identifying the actions that will take place in the system. This lets us take a step back and evaluate what the system should be doing, without getting bogged down into the details of how it should be doing it.
You made the following comment in #MarkB's answer:
If there's a TestCricket class inheriting from Cricket, and it has many specific attributes concerning the timings of the various innings of the match, and we would like to initialize the values of all TestCricket objects' timing attributes to some preset value, I need a loop that picks all TestCricket objects and calls some function like setInningTimings(int inning_index, Time_Object t)
In this case, the action is: "Initialize the inning timings of all TestCricket games to a preset value."
This is problematic, because the code that wants to perform this initialization is unable to differentiate between TestCricket games, and other games (e.g., Basketball). But maybe it doesn't need to...
Most games have some element of time: Basketball games have time-limited periods, while Baseball games have (basically) innings with basically unlimited time. Each type of game could have its own completely unique configuration. This is not something we want to offload onto a single class.
Instead of asking each game what type of Game it is, and then telling it how to initialize, consider how things would work if the GameSchedule simply told each Game object to initialize. This delegates the responsibility of the initialization to the subclass of Game - the class with literally the most knowledge of what type of game it is.
This can feel really weird at first, because the GameSchedule object is relinquishing control to another object. This is an example of the Hollywood Principle. It's a completely different way of solving problems than the approach most developers initially learn.
This approach deals with the constraints in the following ways:
GameSchedule can iterate over a list of Games without any problem
GameSchedule no longer needs to know the subtypes of its Games
No downcasting is necessary, because the subclasses themselves are handling the subclass-specific logic
When a new subclass is added, no logic needs to be changed anywhere - the subclass itself implements the necessary details (e.g., an InitializeTiming() method).
Edit: Here's an example, as a proof-of-concept.
struct Game
{
std::string m_name;
Game(std::string name)
: m_name(name)
{
}
virtual void Start() = 0;
virtual void InitializeTiming() = 0;
};
// A class to demonstrate a collaborating object
struct PeriodLengthProvider
{
int GetPeriodLength();
}
struct Basketball : Game
{
int m_period_length;
PeriodLengthProvider* m_period_length_provider;
Basketball(PeriodLengthProvider* period_length_provider)
: Game("Basketball")
, m_period_length_provider(period_length_provider)
{
}
void Start() override;
void InitializeTiming() override
{
m_period_length = m_time_provider->GetPeriodLength();
}
};
struct Baseball : Game
{
int m_number_of_innings;
Baseball() : Game("Baseball") { }
void Start() override;
void InitializeTiming() override
{
m_number_of_innings = 9;
}
}
struct GameSchedule
{
std::vector<Game*> m_games;
GameSchedule(std::vector<Game*> games)
: m_games(games)
{
}
void StartGames()
{
for(auto& game : m_games)
{
game->InitializeTiming();
game->Start();
}
}
};
You've already identified the first two options that came to my mind: Make the base class have the methods in question, or maintain separate containers for each game type.
The fact that you don't feel these are appropriate leads me to believe that the "abstract" interface you provide in the Game base class may be far too concrete. I suspect that what you need to do is step back and look at the base interface.
You haven't given any concrete example to help, so I'm going to make one up. Let's say your basketball class has a NextQuarter method and hockey has NextPeriod. Instead, add to the base class a NextGameSegment method, or something that abstracts away the game-specific details. All the game-specific implementation details should be hidden in the child class with only a game-general interface needed by the schedule class.
C# supports reflections and by using the "is" keyword or GetType() member function you could do these easily. If you are writing your code in unmanaged C++, I think the best way to do this is add a GetType() method in your base class (Games?). Which in its turn would return an enum, containing all the classes that derive from it (so you would have to create an enum too) for that. That way you can safely determine the type you are dealing with only through the base type. Below is an example:
enum class GameTypes { Game, Basketball, Football, Hockey };
class Game
{
public:
virtual GameTypes GetType() { return GameTypes::Game; }
}
class BasketBall : public Game
{
public:
GameTypes GetType() { return GameTypes::Basketball; }
}
and you do this for the remaining games (e.g. Football, Hockey). Then you keep a container of Game objects only. As you get the Game object, you call its GetType() method and effectively determine its type.
You're trying to have it all, and you can't do that. :) Either you need to do a downcast, or you'll need to utilize something like the visitor pattern that would then require you to do work every time you create a new implementation of Game. Or you can fundamentally redesign things to eliminate the need to pick the individual Basketballs out of a collection of Games.
And FWIW: downcasting may be ugly, but it's not unsafe as long as you use pointers and check for null:
for(Game* game : allGames)
{
Basketball* bball = dynamic_cast<Basketball*>(game);
if(bball != nullptr)
bball->SetupCourt();
}
I'd use the strategy pattern here.
Each game type has its own scheduling strategy which derives from the common strategy used by your game schedule class and decouples the dependency between the specific game and game schedule.
When I was younger, I used a tool called Game Maker a lot. I started to learn to program from it. I'm far beyond that now, but looking back on it, some of it's features and designs are quite interesting. I'm wondering- How would I implement functionality similar to this tool using C++?
I'm wondering about:
Objects/classes
Game Maker had a list of 'Objects' that you would create which were essentially just different classes all derived from the same base class (I'll call it GameObject for now) amd a system function called 'instance_create' that would take an object type as a paramater.
In c++ this would look something like this (Though syntatically very incorrect):
class MyGameObject : GameObject
{
//...
}
GameObject instance_create(class objecttype)
{
objecttype newinstance = new objecttype();
return newinstance
}
GameObject* gameobjectinstance = instance_create(MyGameObject);
How would I go about implementing that?
system variables/functions
Game Maker had system variables and functions that could be accessed from anywhere. Period. Anywhere. I'm thinking globals, but I know that's bad design. I'm thinking Having a global class, and have the variables/functions as static, but then they cannot be altered. How would I do that?
var
Game Maker had only one data type- a var. It could be a string, an integer, a decimal, anything. And there were system functions for conversion between those.
Lastly, how could I define the object types in some kind of script? Like, if I want to add a new type of object, create a new script? I don't think C++ can create object types at runtime, so how would I do this?
Using a template.
template<typename T> GameObject* instance_create()
{
return new T;
}
GameObject* gameobjectinstance = instance_create<MyGameObject>();
However, the design you have specified is highly questionable (at best) and definitely not suited to C++. You should strive to implement a well-designed system, and one appropriate to the language, not re-create a system from the past.
I especially think that since you mention run-time interpretation of scripts, that in fact the GameMaker classes and C++ classes have nothing to do with each other. And you definitely cannot create C++ classes at run-time, nor can you pass types around at run-time, nor can you instantiate templates at run-time.
You would be best suited simply whipping out a scripting language, such as Lua, and writing only the necessary high-performance components in C++.
Game Maker enables us to control game functionality through "objects", each composed out of "events", which are triggered at certain times during a game. Within events are "actions". Firstly, it's worth noting that comparing Game Maker development with C++ is like comparing chalk and cheese. However, theoretically speaking, I'd imagine you could mirror GM functionality (albeit very inefficiently) in C++ as follows:
The base object class could look something like this:
class CObjectBase
{
public:
CGameSprite* sprite;
int x, y;
...
virtual void onEventCreate( void ) {};
virtual void onEventDestroy( void ) {};
...
virtual void onEventKeyPressedUp( void ) {};
virtual void onEventKeyPressed...
...
... (there are lots of these)
// The draw event in GM (from memory) had in-built functionality:
virtual void onEventDraw( void )
{
CGameEngine::getSingleton()->DrawSpriteAtLocation( sprite, x, y );
}
};
You'd derive from this class and override the functions ("events") that are relevant to your object (the statements that compose these functions are your "actions"). Then there would be some sort of object instance manager singleton class which holds a list of all object instances in the current "room" and loops through each every frame (and handles instancing), triggering relevant events by calling their respective functions.
Interestingly, this actually roughly demonstrates why a system like Game Maker lacks a degree of efficiency. There is additional, unnecessary overhead that exists in order to keep options open for the developer. The bloated base object that all objects derive from is often overkill for specific situations. For example, imagine an object with just two events used out of 50 - the object manager still blindly checks for all these other events even if they aren't utilised. Obviously optimisations can be made, but overall, the breadth of the engine ultimately results in reduced performance.
As for your query relating to a single 'var' type, as has been stated already, this is more a characteristic of scripting, not C++. This proves that Game Maker cannot simply be modeled by C++ alone.