Component based architecture c++ - c++

I'm having trouble figuring out a way to make a component based engine architecture in c++. But i cant figure out a way to combine a vector of components with a class that derives from component.
I want to override components virtual function. But the only way i can make it call the overrided function is to make the component-derived class a pointer, but i want every gameobject to contain its own components in a vector and not outside the class as a pointer.
I tried to remove as much unnecessary code as possible.
My structure:
//GameObject class, contains components and other objects
class GameObject
{
public:
GameObject(){}
~GameObject(){}
void AddChild(GameObject child)
{
children.push_back(child);
}
void AddComponent(Component component)
{
components.push_back(component);
}
void Input(){}
void Update(){}
void Render()
{
for(unsigned int i = 0; i < components.size(); i++)
components[i].Render();
}
private:
std::vector<GameObject> children;
std::vector<Component> components;
};
//base class component
class Component
{
public:
Component(){}
~Component(){}
virtual void Input(){}
virtual void Update(){}
virtual void Render(){ cout << "Component -> Render()" << endl; }
};
class MeshRenderer : public Component
{
public:
MeshRenderer(Mesh _mesh, Material _material)
{
mesh = _mesh;
material = _material
}
~MeshRenderer(){}
//override components virtual Render()
void Render(Transform transform)
{
mesh.Render(material);
cout << "MeshRenderer -> Render()" << endl;
}
private:
Mesh mesh;
Material material;
};
GameObject* root = new GameObject();
MeshRenderer meshRenderer(mesh, material);
root->AddComponent(meshRenderer);
//GameLoop
while(!quit)
{
root->Render();
}

It would be the best if you could use unique_ptr:
void AddComponent(std::unique_ptr<Component> component) {
components.push_back(std::move(component));
}
std::vector<std::unique_ptr<Component>> components;
Thus by calling AddComponent() you transfer ownership of the component to containing GameObject.

Looks like you want to pass your objects by reference, use
void AddComponent(Component& component);
to avoid any slicing.
For proper usage with std::vector<>'s and polymorphic inheritance, you'll need smart pointers, e.g. std::unique_ptr<Component> to preserve ownership, or std::shared_ptr<Component> for shared ownership (raw pointers as Component* might work as well, but are far harder to manage correctly).
void AddComponent(std::unique_ptr<Component> componentPtr); // Unique ownership
or
void AddComponent(std::shared_ptr<Component> componentPtr); // Shared ownership
and accordingly
std::vector<std::unique_ptr<Component>> components;
or
std::vector<std::shared_ptr<Component>> components;
It depends on your actual use cases if these Component instances should be uniquely owned by their aggregating parent GameObject class, or not.
To use std::shared<> pointers, that could expire outside their usages scope you may consider using std::weak_ptr<>.
As mentioned, it totally depends on your use cases, and how you want these aggregated components being accessible from outside of the GameObject class.

Related

How to create vector to store addresses of all children?

I am trying to create a basic game engine in C++ and I want an Engine object to be able to loop through all the GameObject children to run their update methods, to do this I want to use a vector of all children within the Engine class.
For example:
This is similar to what I have been trying to do:
Parent Engine Class
class Engine {
public:
Engine() {};
std::vector<GameObject> GameObjects; //Vector to store all GameObjects
void AddNewObject(GameObject& obj) {
GameObjects.push_back(obj); //Add new object to the vector array
}
void Run() {
for (int i = 0; i < GameObjects.size(); i++) {
GameObjects[i].Update(); //Run the update method of each GameObject
}
}
}
GameObject Child Class
class GameObject : public Engine {
public:
GameObject() {}
void Update() {
//Do stuff
}
}
Main code loop
int main(void) {
Engine engine;
GameObject object;
engine.AddNewObject(object); //Add object
engine.run();
}
Any help would be greatly appreciated, Thanks.
There are a few issues here. First, your vector needs to be a vector of references or pointers, otherwise GameObjects.push_back(obj); makes a copy of obj to place into the vector (unless you move it) and polymorphism won't work (you can't hold subclasses of GameObject).
How you approach this depends on which object you want to own the memory associated with each GameObject. A trivial fix is is by using a vector of pointers:
class Engine {
public:
Engine() {};
std::vector<GameObject*> GameObjects; //Vector to store all GameObjects
void AddNewObject(GameObject& obj) {
GameObjects.push_back(&obj); //Add new object to the vector array
}
void Run() {
for (int i = 0; i < GameObjects.size(); i++) {
GameObjects[i]->Update(); //Run the update method of each GameObject
}
}
}
However, with modern, C++ you would want to probably use a smart pointer like unique_ptr:
class Engine {
public:
Engine() {};
std::vector<std::unique_ptr<GameObject>> GameObjects; //Vector to store all GameObjects
void AddNewObject(std::unique_ptr<GameObject> obj) {
GameObjects.push_back(std::move(obj)); //Add new object to the vector array
}
void Run() {
for (int i = 0; i < GameObjects.size(); i++) {
GameObjects[i]->Update(); //Run the update method of each GameObject
}
}
}
By using a unique pointer, you would have to change your other code:
int main(void) {
Engine engine;
std::unique_ptr<GameObject> object = std::make_unique<GameObject>();
// Transfers memory ownership of `object` into `engine`
engine.AddNewObject(std::move(object)); //Add object
engine.run();
}
Lastly, your class hierarchy looks invalid. You probably don't need or want GameObject to be a subclass of Engine since they don't share anything in common. GameObject may likely be a good base class itself and other game objects would inherit it.

C++ visitor pattern multiple components

I got an object tree. In the object tree I store SceneNodes. A SceneNode is usually the base class for other classes.
I want to implement different behavior for the objects that represent the SceneNodes.
The correct Pattern for this problem should be the visitor pattern. I want to iterate over the SceneNodes and want to call different functions based on the objects stored behind the SceneNodes.
But I not only want to allow one object in the object tree to be one component of the visitor pattern but to share functionality.
For example: I have a BaseObject. I can update this BaseObject (e.g. to a new position) and I can draw a BaseObject (OpenGL stuff).
But I also have a Camera object. The camera object can be updated but not drawn.
Here is the implementation of the Visitor Stuff:
class Visitor
{
public:
virtual void VisitUpdate(ComponentUpdate* element) = 0;
virtual void VisitDraw(ComponentDraw* element) = 0;
virtual void VisitOverlay(ComponentOverlay* element) = 0;
};
Visitor Component:
class Component
{
public:
virtual ~Component() { }
virtual void accept(Visitor* visitor) = 0;
};
Concrete Component:
class ComponentUpdate : public Component
{
public:
void accept(Visitor* visitor) override {
visitor->VisitUpdate(this);
}
virtual void update() = 0;
};
class ComponentDraw : public Component
{
public:
void accept(Visitor* visitor) override {
visitor->VisitDraw(this);
}
virtual void draw() = 0;
};
And finally a concrete visitor:
class SceneNodeVisitor : public Visitor
{
void VisitUpdate(ComponentUpdate* element) override {
element->update();
}
void VisitDraw(ComponentDraw* element) override {
element->draw();
}
};
Now I'd like to do something like this:
class Camera : public ComponentUpdate
{
void update() override { std::cout << "Camnera update" << std::endl; }
};
class ObjectBase : public ComponentDraw, public ComponentUpdate
{
void update() override { std::cout << "ObjectBase update" << std::endl; }
void draw() override { std::cout << "ObjectBase draw" << std::endl; }
};
Ok, so far so good. The problem I have now is that the compiler says "base class is ambiguous". I think this is not correct because ObjectBase is ambiguous because it has two different accept() functions, am I right?
Is there a way to use the visitor pattern so that I can freely anoint the classes with the functionality I need for them?
Here the main function:
int main() {
ObjectBase ob;
Camera cam;
SceneNodeVisitor visitor;
std::vector<Component*> components;
components.push_back(new Camera);
components.push_back(new ObjectBase);
components[0]->accept(&visitor);
components[1]->accept(&visitor);
}
Strange is that I can create the ObjectBase on the stack. I only get the error if I try to create the object on the heap (via new).
Pastebin is down at the moment, I can give you this example code as soon as it's up again.
Okay, I'm not entirely sure, but I think you should separate out some of the concepts you're doing.
As soon as you inherit from two classes that both inherit from the same base class, you need to start looking at virtual inheritance. That might solve your problem. But the path from ObjectBase to Component is either through ComponentDraw or ComponentUpdate. In effect, you probably have two copies of Component because you're not using virtual inheritance.
I would strongly consider using the concept of interfaces. While C++ technically doesn't have them, you can make them, anyway.
And look at virtual inheritance.

Can I somehow call the derived class method while looping through vector<shared_ptr<BaseClass>>?

I have the following problem:
class Component
{
public:
virtual void update(){};
};
class TestComponent : public Component
{
void update()override;
};
class GameObject
{
public :
void addComponent(Component& comp)
{
std::shared_ptr<Component> test = std::make_shared<Component>(comp);
components.push_back(test);
}
void GameObject::update()
{
for(auto comp : components)
{
//I want to call the derived update here without casting it to the derived class if possible
comp->update();
}
}
private:
std::vector<std::shared_ptr<Component>> components;
};
Somewhere else in my code:
GameObject go;
TestComponent comp;
go.addComponent(comp);
I would just assume that when I add an object to the vector of Components that I can simply call update on all of the vectors elements and it uses the overridden update of the object I passed into addComponent. So for my example above I expect the forloop to call the update of the TestComponent I added and not the baseclass update. But thats not whats happening so I assume I am missing something.
Or maybe my approach is just wrong in general. I am not really sure about my usage of a sharedpointer for this?
Any hints in the right direction would be appreciated.
There are no TestComponent objects in your vector. They are all Components.
void addComponent(Component& comp)
{
std::shared_ptr<Component> test = std::make_shared<Component>(comp);
components.push_back(test);
}
In this function, you create a new Component object that s a copy of the Component sub-object of the TestComponent object you passed in. This is known as object slicing.
You will need to either avoid copying the objects or implement some sort of cloneable interface.
To avoid copying the object, you can do something like this:
class GameObject
{
public:
void addComponent(std::shared_ptr<Component> comp)
{
components.push_back(comp);
}
// ...
};
int main() {
GameObject go;
std::shared_ptr<TestComponent> testComponent = std::make_shared<TestComponent>();
go.addComponent(testComponent);
}
In this case, main and go share ownership of a single TestComponent object. If you want to avoid that, you could implement a clonable interface so that objects know how to copy themselves:
class Component
{
public:
virtual void update(){};
virtual std::shared_ptr<Component> clone() const
{
return std::make_shared<Component>(*this);
}
};
class TestComponent : public Component
{
void update() override;
std::shared_ptr<Component> clone() const override
{
return std::make_shared<TestComponent>(*this);
}
};
class GameObject
{
public:
void addComponent(const Component& comp)
{
components.push_back(comp.clone());
}
// ...
};
int main()
{
GameObject go;
TestComponent comp;
go.addComponent(comp);
}
In this case, you still make a copy, but every class has to override the clone method.
As for the question about shared_ptr: std::shared_ptr is a smart pointer that shares ownership of an object between multiple owners. An object owned by one or more std::shared_ptrs is only destroyed when all of the std::shared_ptr objects sharing ownership of it are destroyed. If you don't need this behavior, then std::unique_ptr exists and will be somewhat more performant. std::unique_ptr models unique ownership. Only one std::unique_ptr object can ever reference an object at a time, and the object is destroyed when that std::unique_ptr is destroyed.
Either type of smart pointer could be used in this situation:
Use std::shared_ptr if you want a GameObject to be able to share ownership of its components with other owners (perhaps other GameObjects).
Use std::unique_ptr if you want a GameObject to have exclusive ownership of its components. In this case the GameObject could still allow other objects to access its components, but the components' lifetimes would be tied to the lifetime of the GameObject
To make your code compile just add another method, rest are fine . Since update method is virtual and the base class is non-abstract, both can call update without any issue.
void TestComponent::addComponent(const TestComponent & tcomp)
{
std::shared_ptr<Component> test = std::make_shared<TestComponent >(tcomp);
components.push_back(test);
}
Edited: For adding any component, derived or base class, use this way:
void TestComponent::addComponent(std::shared_ptr<Component> comp)
{
components.push_back(comp);
}

What is the best way to dynamically check the types of a class that uses variadic inheritance?

I am writing an entity component system for a 2D game engine that uses variadic templates to construct game objects. Here is the object class which is just the container for all components. I removed the unrelated stuff.
template<class ... Components>
class Object : public Components...{
public:
Object(Game* game) : Components(game)...{
}
};
The components are inherited by the object but I am trying to find the best way to check the type of these components so that they can correctly communicate with each other. For example, the Physics component would contain the updated position of the object. The Drawable component needs to get that position so it can be drawn in the correct spot in the world. I would like to add an update function to Object that updates each component and transfers whatever information can/needs to be transferred between present components.
polymorphism is what you want .
Simply make all components like that :
public Object
{
enum TYPE{
COMPONENT,,
GAMEOBJECT,
ETC,,,
};
Object(TYPE id){m_id = id;}
protected:
TYPE m_id;
virtual void Update(void) = 0;
virtual void Render(void) = 0;
public:
void GetTypeOf(void)const{return m_id;}
};
class Component : Object
{
enum COMPONENT_TYPE
{
COLLIDER,
RENDERER,
ETC,,,,
};
Component() : Object (COMPONENT){/**/}
virtual void Update(void){return;}
virtual void Render(void){return;}
};
class BoxCollider : Component
{
BoxCollider(void) : Component(BOXCOLLIDER){/**/}
void Update(void)
{
//to do
}
void Render(void)
{
//to do
}
};
then you can simply have a data structure of Object* or Component*
and you can iterate through that way :
std::vector<Component*>::iterator components = ComponentVector.begin();
for(;components != ComponentVector.end() ; ++components)
{
*(component)->Update();
*(component)->Render();
std::cout << "id" << *component->getTypeOf() << std::endl;
}
The Object class inherits from all its Components, that means that actually it is all its components.
You can use this information to design your update method.
As an example:
#include <cassert>
struct Game { };
struct Physics {
int position{0};
Physics(Game *) { }
void update(void *) { }
};
struct Drawable {
Drawable(Game *) { }
void update(Physics *physics) {
physics->position = 42;
}
};
template<class ... Components>
class Object: public Components... {
public:
Object(Game* game) : Components(game)... { }
void update() {
int a[] = { (Components::update(this), 0)... };
}
};
int main() {
Game game;
Object<Physics, Drawable> object{&game};
assert(object.position == 0);
object.update();
assert(object.position == 42);
}
Here you can see as Drawable receives Physics when its update method is called.
The drawbacks of this solution are:
The update methods of the components must get a pointer argument even if they do not need to refer to any other component.
If there exists a component that needs to refer to more than one component, you have either to get two or more pointers as arguments of the update method or to cast a void * around.

How to properly use virtual member functions in c++

I am having a problem with the following code, the overriden virtual functions are not executing. Not sure i'm doing wrong here probably a silly mistake. Anyway this is a game project and I have an array of objects which looks like this (the core::array is an irrlicht array, similar to the vector array)
core::array<GameObject> gameTargets;
This is the GameObject and Zombie definition
class GameObject {
protected:
scene::ISceneNode* node;
public:
int ID;
int hitpoints;
GameObject() {
...
};
void setNode(scene::ISceneNode* inode) {
...
}
virtual void shot(int dmg) {
... [BREAKPOINT HERE]
}
scene::ISceneNode* getNode() {
return node;
}
};
class Zombie : public GameObject {
public:
static const enum Animation {
ZOMBIE_WALK,
ZOMBIE_HURT,
ZOMBIE_DIE,
ZOMBIE_TWITCH,
ZOMBIE_ATTACK,
ZOMBIE_IDLE
};
//We only want to accepted animated mesh nodes for this object
Zombie(int hp, scene::IAnimatedMeshSceneNode* inode) {
...
}
//Override the shot function
void shot(int dmg) {
... [BREAKPOINT HERE]
}
//Animate the zombie
void setAnimation(Animation anim) {
...
}
};
The member functions of the derived classes is never called, I am creating the objects like this
Zombie target(hp, (scene::IAnimatedMeshSceneNode*)node);
and calling the virtual function like this
for(int i = 0; (u32)i<level->gameTargets.size(); i++) {
if(selectedNode == level->gameTargets[i].getNode()) {
level->gameTargets[i].shot(b->damage);
}
}
where b is a pointer to a bullet with a int variable damage and gameTargets contains GameObject
I suspect that you're experiencing slicing because the gameTargets array contains values. I can't tell for sure because I don't know how the core::array template works. See What is object slicing? for a discussion about what slicing is.
To fix this problem, store either raw pointers as in
core::array<GameObject *> gameTargets;
Or use some sort of reference-counted pointer like
core::array<std::shared_ptr<GameObject>> gameTargets; // only available in C++11
array<GameObject> is a container of objects, not a container of pointers. Every object you add to it will be a GameObject and not one of the derived classes (if you add a derived class object, then it'll be "sliced").
Without knowing exactly what your core::array does, I suspect what you really intended to create is an array of std::unique_ptr<GameObject> (smart pointers) along the lines of
core::array< std::unique_ptr<GameObject> > gameTargets;
std::unique_ptr<GameObject> my_zombie(new Zombie);
gameTargets.push_back( my_zombie );
a quick solution would be to make those parent functions as pure virtual functions, as in:
virtual void shot(int dmg) { } = 0;
// edit
and use array of pointer as suggested by Frerich Raabe