What am I trying to accomplish: I want to be able to loop through a vector of base classes and reference an interface on the child class.
Where am I now: I am working on creating a system for a game where you have game objects, and you can add functionality to the game object by adding a component.
I've created a GameObject class with a list of components. This class loops through all of the components and calls three functions on the component: handleInput(), update() and draw().
GameObject.h
#include "IInitialize.h"
#include "IInput.h"
#include "IUpdate.h"
#include "IDraw.h"
#include "TransformComponent.h"
class Component;
class GameObject : public IInitialize, IInput, IUpdate, IDraw
{
public:
TransformComponent transform;
GameObject();
void addComponent(Component* component);
bool IInitialize::initialize()
{
return true;
}
void IInput::handleInput(sf::RenderWindow* window, sf::Event* event)
{
for (auto it = components.begin(); it != components.end(); it++)
{
IInput* component = dynamic_cast<IInput*>(*it);
if (component != nullptr)
{
component->handleInput(window, event);
}
}
}
void IUpdate::update(float deltaTime)
{
for (auto it = components.begin(); it != components.end(); it++)
{
IUpdate* component = dynamic_cast<IUpdate*>(*it);
if (component != nullptr)
{
component.update(deltaTime);
}
}
}
void IDraw::draw(sf::RenderWindow* window)
{
for (auto it = components.begin(); it != components.end(); it++)
{
IDraw* component = dynamic_cast<IDraw*>(*it);
if (component != nullptr)
{
component.draw(window);
}
}
}
private:
std::vector<Component*> components;
};
GameObject.cpp
#include "GameObject.h"
GameObject::GameObject()
{
transform = TransformComponent();
transform.position = sf::Vector2f(960, 540);
}
void GameObject::addComponent(Component* component)
{
component->parent = this;
components.push_back(std::move(component));
}
In the handleInput(), update() and draw() functions I am attempting to get the interfaces off the component to call them.
The base Component class does not implement the interfaces but the children class of the base Component classes do.
Component.h
#include "IInitialize.h"
class GameObject;
class Component : public IInitialize
{
public:
GameObject* parent;
Component();
virtual bool IInitialize:: initialize()
{
return true;
}
};
TextComponent.h
#include "Component.h"
#include "IUpdate.h"
#include "IDraw.h"
class TextComponent : public Component, IUpdate, IDraw
{
public:
std::string text = "Text";
sf::Color color = sf::Color::White;
int size = 25;
TextComponent(std::string text = "Text", sf::Color color = sf::Color::White, int size = 25);
void setPosition();
bool IInitialize::initialize() override
{
if (!_font.loadFromFile(_fontPath))
{
return false;
}
return true;
}
void IUpdate::update(float deltaTime)
{
_text.setString(text);
_text.setFillColor(color);
_text.setCharacterSize(size);
setOrigin();
setTextPosition();
}
void IDraw::draw(sf::RenderWindow* window)
{
window->draw(_text);
}
private:
sf::Font _font;
sf::Text _text;
std::string _fontPath = "Assets/Fonts/accidental_presidency.ttf";
void setOrigin();
};
My problem: My problem is that the objects I am storing in the components vector are children classes that derive from the Component class. I think that object slicing is occurring because the base component class doesn't implement any of the interfaces and when the GameObject tries to cast the Component class to a IInput, IUpdate or IDraw it fails because the base class doesn't implement these interfaces.
What I've tried:
I've considered trying to down cast the component into the right type in order to get the interface but this would require the gameobject to know about the different types of components and I am not sure that it needs to know about them. It would also require me to update the GameObject class each time I create a new component.
I've tried creating a middle man class that will always derive from the interfaces. This limits the uses of the interfaces and makes it so that sometimes a function goes unused because the component didn't need to use it.
I've added the interfaces to the base class and override them in the children classes. This also makes it so that sometimes some of the interface functions go un-used. My goal was to only add the interface to the objects that need to use them.
My question: How can I design the architecture so that I can use the interfaces on my child objects without having to give the GameObject too much information about the Component Types?
To elaborate on the comment by @user4581301, and to answer your concern:
You can define your interfaces as
pure virtualand provide the default implementation in the base class (it can do nothing, or log something, etc.).See Pure virtual function with implementation for more details.