I'm trying to implement some sort of 'just-for-me' game engine and the problem's plot goes the following way:
Suppose I have some abstract interface for a renderable entity, e.g. IRenderable
.
And it's declared the following way:
interface IRenderable {
// (...)
// Suppose that Backend is some abstract backend used
// for rendering, and it's implementation is not important
virtual void Render(Backend& backend) = 0;
};
What I'm doing right now is something like declaring different classes like
class Ball : public IRenderable {
virtual void Render(Backend& backend) {
// Rendering implementation, that is specific for
// the Ball object
// (...)
}
};
And then everything looks fine. I can easily do something like std::vector<IRenderable*> items
, push some items like new Ball()
in this vector and then make a call similiar to foreach (IRenderable* in items) { item->Render(backend); }
Ok, I guess it is the 'polymorphic' way, but what if I want to have different types of objects in my game and an ability to manipulate their state, where every object can be manipulated via it's own interface?
I could do something like
struct GameState {
Ball ball;
Bonus bonus;
// (...)
};
and then easily change objects state via their own methods, like ball.Move(...)
or bonus.Activate(...)
, where Move(...)
is specific for only Ball
and Activate(...)
- for only Bonus
instances.
But in this case I lose the opportunity to write foreach IRenderable*
simply because I store these balls and bonuses as instances of their derived, not base classes. And in this case the rendering procedure turns into a mess like
ball.Render(开发者_Python百科backend);
bonus.Render(backend);
// (...)
and it is bad because we actually lose our polymorphism this way (no actual need for making Render
function virtual, etc.
The other approach means invoking downcasting via dynamic_cast
or something with typeid
to determine the type of object you want to manipulate and this looks even worse to me and this also breaks this 'polymorphic' idea.
So, my question is - is there some kind of (probably) alternative approach to what I want to do or can my current pattern be somehow modified so that I would actually store IRenderable*
for my game objects (so that I can invoke virtual Render
method on each of them) while preserving the ability to easily change the state of these objects?
Maybe I'm doing something absolutely wrong from the beginning, if so, please point it out :)
Thanks in advance!
The fact that you are storing the different objects as members of the class shouldn't prevent you from storing pointers to them in the IRenderable*
vector.
struct GameState {
Ball ball;
Bonus bonus;
vector<IRenderable*> m_items;
GameState() // constructor
{
m_items.push_back(&ball);
m_items.push_back(&bonus);
}
};
This way you also don't have to worry about freeing the pointers in m_items
. They will be automatically destroyed when the GameState
is destroyed.
Also, your syntax of foreach is funky.
You should really have separate subsystems in your game engine. One for rendering and another one for game logic. Your graphics subsystem should have a list of pointers to IRenderable objects. Your game logic subsystem should have its own list of objects with another interface.
Now you can easily have entities that have state but cannot be rendered and so on. You might even have a separate subsystem for audio. This should also help keep your code clean and modular.
I'm not a c++ programmer but hopefully faux-code will help...
Class Renderable has the function Render
Class Movable inherits from Renderable and also has properties such as x, y, z and functions like Move
Class Ball inherits from Movable
Now your game state can have a list of Renderable objects and, when the time comes to Render, it can just loop through them.
Other actions, like a tick of the world clock, might loop through looking for objects which are instances of Moveable and tell them to update.
If your Bonus is renderable (I couldn't tell for sure from your question) then it could subclass Collectable which subclasses Moveable.
精彩评论