Let's say I'm creating an OpenGL game in C++ that will have many objects created (enemies, player characters, items, etc.). I'm wondering the best way to organize these since they will be created and destroyed real-time based on time, player position/actions etc.
Here's what I've thought of so far: I can have a global array to store pointers to these objects. The textures/context for these objects are loaded in their constructors. These objects will have different types, so I can cast the pointers to get them in the array, but I want to later have a renderObjects() function that will use a loop to call an ObjectN.render() function for each existing object.
I think I've tried this before but I didn't know what type to initialize the array with, so I picked an arbitrary object type, then cast anything that wasn't of that type. If I remember, this didn't work because the compiler didn't want me dereferencing the pointers if it no longer knew their type, even if a given member function had the same name: (*Object5).render() <-doesn't work?
Is there a better way? How to commercial games like HL2 handle this? I imagine there must be some module etc. that keeps track of all the objects.
I'm not sure I fully understand the question but I think you are wanting to create a collection of polymorphic objects. When accessing a polymorphic object, you must always refer to it by a pointer or a reference.
Here is an example. First you need to set up a base class to derive your objects from:
class BaseObject
{
public:
virtual void Render() = 0;
};
Then create the array of pointers. I use an STL set because that makes it easy to add and remove members at random:
#include <set>
typedef std::set<BaseObject *> GAMEOBJECTS;
GAMEOBJECTS g_gameObjects;
To add an object, create a derived class and instantiate it:
class Enemy : public BaseObject
{
public:
Enemy() { }
virtual void Render()
{
// Rendering code goes here...
}
};
g_gameObjects.insert(new Enemy());
Then to access objects, just iterate through them:
for(GAMEOBJECTS::iterator it = g_gameObjects.begin();
it != g_gameObjects.end();
it++)
{
(*it)->Render();
}
To create different types of object, just derive more classes from class BaseObject. Don't forget to delete the objects when you remove them from the collection.
For my soon-to-be personal game project, I use a component-based entity system.
You can read more about it by searching "component based game development". A famous article is Evolve Your Hierarchy from the Cowboy programming blog.
In my system, entities are just ids - unsigned long, a bit like in a relational database. All the data and the logic associated to my entities are written into Components. I have Systems that link entity ids with their respective components. Something like that:
typedef unsigned long EntityId;
class Component {
Component(EntityId id) : owner(id) {}
EntityId owner;
};
template <typename C> class System {
std::map<EntityId, C * > components;
};
Then for each kind of functionality, I write a special component. All entities don't have the same components. For example you could have a static rock object that has the WorldPositionComponent and the ShapeComponent, and a moving enemy that has the same components plus the VelocityComponent. Here's an example:
class WorldPositionComponent : public Component {
float x, y, z;
WorldPositionComponent(EntityId id) : Component(id) {}
};
class RenderComponent : public Component {
WorldPositionComponent * position;
3DModel * model;
RenderComponent(EntityId id, System<WorldPositionComponent> & wpSys)
: Component(id), position(wpSys.components[owner]) {}
void render() {
model->draw(position);
}
};
class Game {
System<WorldPositionComponent> wpSys;
System<RenderComponent> rSys;
void init() {
EntityId visibleObject = 1;
// Watch out for memory leaks.
wpSys.components[visibleObject] = new WorldPositionComponent(visibleObject);
rSys.components[visibleObject] = new RenderComponent(visibleObject, wpSys);
EntityId invisibleObject = 2;
wpSys.components[invisibleObject] = new WorldPositionComponent(invisibleObject);
// No RenderComponent for invisibleObject.
}
void gameLoop() {
std::map<EntityId, RenderComponent *>::iterator it;
for (it = rSys.components.iterator(); it != rSys.components.end(); ++it) {
(*it).second->render();
}
}
};
Here you have 2 components, WorldPosition and Render. The Game class holds the 2 systems. The Render component has an access to the position of the object. If the entity doesn't have a WorldPosition component, you can choose default values, or ignore the entity. The Game::gameLoop() method will only render visibleObject. There is no waste of processing for non-renderable components.
You can also split my Game class into two or three, to separate display and input systems from the logic. Something like Model, View and Controller.
I find it neat to define my game logic in term of components, and to have entities that only have the functionality that they need - no more empty render() or useless collision detection checks.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With