Hi everyone,
Thanks for your advice.
Your second solution could use a templated function instead of void*, so that you don't need those ugly static_casts and you get compiler errors if the passed argument does not inherit from sf::Drawable or sf::Transformable.
That's a good idea! Maybe something like this is the best solution:
class GameObject {
public:
template <typename T>
GameObject::GameObject(T* drawformable) {
drawable_ = drawformable;
transformable_ = drawformable;
}
template <typename T>
T* GameObject::GetDrawformable() {
return dynamic_cast<T*>(drawable_);
}
private:
sf::Drawable* drawable_;
sf::Transformable* transformable_;
};
And why do you need to manage different types in the same container?
This is a good question. I've been developing my game using a component architecture, where a collection of GameObjects own a bunch of components. Initially, the GameObject owned a sf::RectangleShape instance, as this provided all the functionality I wanted. Each component has an Update(GameObject* o, ...) function, where it can operate on the parent GameObject (and thus the sf::RectangleShape if it chooses so).
However, I'm interested in adding text and sprites, so I would like the GameObject to be able to store sf::Text or sf::Sprite too. I'd like to keep the GameObject / Component architecture, so I've been investigating ways to add sf::Text and / or sf::Sprite capabilities to the GameObject class without major changes.
To me this design of everything must fit into one container is flawed, as you only ever want to be that generic on the interface for a specific purpose, for example when you pass all your objects for rendering, then you could have vector of pointers to drawables and then you also don't need access to the transformable interface.
Instead you probably want classes that specifically deal with a certain type of drawable/transformable, then you can also have interfaces to deal with changing the text of sf::Text or the texture rect for a sf::Sprite (e.g. for animation), etc
I agree it is not ideal. However, if I had containers for specific sf::Drawable instances, such as RectGameObject : GameObject and SpriteGameObject : GameObject, or GameObject<sf::RectangleShape> and GameObject<sf::Sprite>, I would still have to handle the different GameObject types in the Component class! To me, this only moves the problem.
What do you do if you want to change the text of a sf::Text? Or change the texture rect of a sf::Sprite?
This is a great point. I may need to access those interfaces at some point. And if I'm storing pointers to a base class but I want the unique (non-inherited) interface of a derived class, I need to cast
somewhere. Either I cast from sf::Drawable to the derived type in the GameObject, or I cast from a base GameObject to a specialised GameObject (that contains the derived type) in the Component.
I think using a template parameter to cast the underlying type (shown in the code above) might be the way to go.
Cheers,
Jeremy