Jump to content
  • Advertisement
Sign in to follow this  
Structural

Object management

This topic is 5470 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm currently trying to get a rough object management system going in C++, and I'd like some advise. So far I have the following properties: - Physics: is is affected by physics as collision detection, movement, gravity. - Drawable: it can be drawn to the screen - Hearable: It can be heard - Controllable: It can be controlled by actors like AI, network input or keyboard input. A few examples: - An explosion would be hearable and drawable (and perhaps even effected by physics) - A menu item would be drawable and controllable (not 100% sure on the last one) - An asteroid in the game (taking a simple example) would be drawable and be effected by physics. - The player would be all four, effected by physics, drawable, hearable and controllable. As you see there are quite a few combinations possible, and I'd like to split the functionality of every property in seperate classes because I have a nr of subsystems that use objects with a certain property. - Graphics subsystem: using drawable objects only - Physics subsystem: using physics objects only - Sound subsystem: using hearable objects only - Input and AI subsystem, using controllable objects only. I've been thinking about a few things: In order to combine the functionality, one would require multiple inheritance. As far as I know anyway. That arises a problem if I'm correct. Memory offsets and casting. One can't just cast a Object* to a DrawableObject* because in MI the offset of every parent class varies. Am I correct? So keeping a central list is not possible because a subsystem can not safely retrieve an object. I've been thinking about interfaces (pure virtual classes) to counter this, but that would require me to recode all functions. So what on earth am I to do? I have experimented with every specialised class register itself in the constructor:
DrawableObject::DrawableObject()
{
	addType(OBJ_TYPE_DRAWABLE);
	ObjectManager::INSTANCE()->addObject(this, OBJ_TYPE_DRAWABLE);
	drawType = OBJ_TYPE_NONE;
}

Then I could store this pointer in a special list for drawable objects. This works but when removing the object... I'd need the offsets to find them back, which means extra overhead. I'm quite stuck on this. How do you real professionals do this? This is not quite a trivial problem. This is as far as I got today:
#ifndef OBJECT_H
#define OBJECT_H

#include "../kernel/memorymanager.h"
#include "../maths/3DMaths.h"

#include <list>

#define OBJ_TYPE_NONE 0
#define OBJ_TYPE_MENU 1

#define OBJ_TYPE_PHYSICS		2
#define OBJ_TYPE_DRAWABLE		8
#define OBJ_TYPE_HEARABLE		16
#define OBJ_TYPE_CONTROLLABLE	32


class Object : public MMObject
{
public:
	Object() { types = OBJ_TYPE_NONE; };
	~Object() { };

	int is(int type);

protected:
	void addType(int type);	

private:
	Vector3D pos;
	Quaternion rot;
	int types;
};


class PhysicsObject : public Object
{
public:

};


class DrawableObject : public Object
{
public:
	DrawableObject();
	~DrawableObject();

	int getType();
	void setType(int type);

private:
	int drawType;
};


class HearableObject : public Object
{
public:

};


class CtrlObject : public Object
{
public:

};



class ObjectManager
{
public:
	static ObjectManager* INSTANCE();		
	void addObject(Object* o);				// adds an object to the overall list of objects
	void addObject(Object* o, int type);	// adds an object to one of the specialised lists (ONLY TO BE USED IN CONSTRUCTOR OF SPECIALISED OBJECTS)
	void removeObject(Object* o);			// removes an object from all lists

	void moveFirst();						// moves iterator to beginning of overall-list
	void moveFirst(int type);				// moves iterator of specialised list to beginning of list
	Object* getNext();						// retrieves next object in overall-list
	Object* getNext(int type);				// retrieves next object in specialised list

private:
	static ObjectManager* instance;

	std::list<Object*> objects;
	std::list<Object*>::iterator it;

	std::list<PhysicsObject*>		physicsObjects;
	std::list<DrawableObject*>		drawableObjects;
	std::list<HearableObject*>		hearableObjects;
	std::list<CtrlObject*>			controllableObjects;

	std::list<PhysicsObject*>::iterator		physicsIt;
	std::list<DrawableObject*>::iterator	drawableIt;
	std::list<HearableObject*>::iterator	hearableIt;
	std::list<CtrlObject*>::iterator		controllableIt;
};

#endif

Thank you for your help.

Share this post


Link to post
Share on other sites
Advertisement
Quote:
Original post by Structural
I've been thinking about a few things:
In order to combine the functionality, one would require multiple inheritance. As far as I know anyway.
That arises a problem if I'm correct. Memory offsets and casting. One can't just cast a Object* to a DrawableObject* because in MI the offset of every parent class varies. Am I correct? So keeping a central list is not possible because a subsystem can not safely retrieve an object.

You're right, kind of. While MI does cause the pointers to different parent class to vary, but it's not the major problem you're seeing it as. All it means is that you shouldn't use C-style casts (or reinterpret_cast either, I think), but using static_cast and dynamic_cast is fine. However, if you were to keep one central list what type would the elements in that list be? They would *have* to be of some base class that all of your 'property' classes derives from, which means diamond-shaped inheritence. This can (but note, not always) be an indication that your design is flawed.

Quote:
I've been thinking about interfaces (pure virtual classes) to counter this, but that would require me to recode all functions.

Hmm, I would have thought your Physics, Drawable, etc. class were already pure abstract classes. Otherwise you could have some real fun sharing data between them, eg position.

Quote:
So what on earth am I to do?

I'm guessing that this 'central list' would be your scenegraph? If so, one possibility would be to have SceneGraphNode as another base class to derive from. You would end up with something like this:

class MyGameObject : public SceneGraphNode, public Drawable, public Physics
{
//...
};


You could then register the instance of MyGameObject with each respective system, the scene graph, the renderer and the physics simulator. Another option is to use aggregation instead of inheritence, although you could again run into problems with sharing data.

Quote:
I have experimented with every specialised class register itself in the constructor:
*** Source Snippet Removed ***

EVIL!!!!!!
Firstly, PhysicsObject, DrawableObject, etc, should all be deriving from Object virtually. As it stands if you derive from more than one of these with a class then there will be multiple copies of the Object base class (since each of PhysicsObject, etc. has it's own copy). To do this, use 'class PhysicsObject : public virtual Object { ... };'. Secondly, storing multiple lists that are effectively the same is just asking for trouble, also if you can determine which list(s) to insert the object into why would you need to have the lists in the first place (since you can already determine the type without them). And thirdly, what your doing is a bastardisation of run-time type information (RTTI), and IMHO is a bad hack trying to get around a bad design (not meaning to have a go at you, just being honest).

The main thing to ask yourself with your current design is why do you want a central list?

Share this post


Link to post
Share on other sites
Quote:

You're right, kind of. While MI does cause the pointers to different parent class to vary, but it's not the major problem you're seeing it as. All it means is that you shouldn't use C-style casts (or reinterpret_cast either, I think), but using static_cast and dynamic_cast is fine. However, if you were to keep one central list what type would the elements in that list be? They would *have* to be of some base class that all of your 'property' classes derives from, which means diamond-shaped inheritence. This can (but note, not always) be an indication that your design is flawed.

I was just cursing my lungs out when you made me see I had a Dreaded Diamond shape. I was totally not paying attention there.
Quote:

Hmm, I would have thought your Physics, Drawable, etc. class were already pure abstract classes. Otherwise you could have some real fun sharing data between them, eg position.

What you say about pure virtual classes and sharing data has crossed my mind too. I was thinking of putting that data in the base class. I'm lazy when it comes to duplicating code. However, I'm now thinking this is silly because I think it's possible that objects have no position or orientation. Timers or triggers for example. So, using pure virtual classes seems a better solution.

Quote:

You could then register the instance of MyGameObject with each respective system, the scene graph, the renderer and the physics simulator.

This sounds particulary good. I hadn't thought about that yet. Register with each subsystem instead of using a central object management component.

Quote:

Another option is to use aggregation instead of inheritence, although you could again run into problems with sharing data.

I've given this a thought, but the question that arises immediately is where to put data like position/orientation that are used by multiple subsystems.

Quote:

EVIL!!!!!!
Firstly, PhysicsObject, DrawableObject, etc, should all be deriving from Object virtually. As it stands if you derive from more than one of these with a class then there will be multiple copies of the Object base class (since each of PhysicsObject, etc. has it's own copy). To do this, use 'class PhysicsObject : public virtual Object { ... };'

Correct. My bad. I didn't know about this feature in C++ BTW.

Quote:

Secondly, storing multiple lists that are effectively the same is just asking for trouble, also if you can determine which list(s) to insert the object into why would you need to have the lists in the first place (since you can already determine the type without them). And thirdly, what your doing is a bastardisation of run-time type information (RTTI), and IMHO is a bad hack trying to get around a bad design (not meaning to have a go at you, just being honest).

I appreciate your honesty, really. Every mistake I make now will give me the experience not to make them anymore in the future. The whole idea behind the seperate lists was to speed things up. For example, there are 100 objects in the game, yet only 2 are HEARABLE. It would save the sound subsystem looping through 100 objects. That was the idea behind it. It was actually your registration-by-subsystem idea, yet put in another place.

Quote:

The main thing to ask yourself with your current design is why do you want a central list?

My answer would be to make it easier to keep track of objects. It would make it easier to register and remove objects. Instead of removing them with every subsystem I'd just remove them from the central list and that would take care of the rest.
That's the idea anyway...


I am convinced that my initial idea sucked big time. Registration with the individual subsystems seems the best way to go at this moment with pure virtual functions.

Share this post


Link to post
Share on other sites
You might try having all objects manage themselves internally. Start with a base class that keeps track of the basics: x/y coords, movement, updating functions, etc. Specialized objects like Drawables, Collisionables, etc can be their own class and then you can make a conglomerate object down the road that inherits from BasicObject and CollisionObject and DrawingObject, or something to that effect. That eliminates the diamond pattern. Drawing, collision, hearable, drawable objects should ONLY provide the facilities to perform that particular task (drawing, colliding, hearing, etc), not repeat what other base classes have already done.

Anyway, one cool trick you *might* try is object self-management. Have a static list of pointers to objects privately owned by the class of objects itself. Every time an object is created, the constructor adds a pointer to the new object to the central list. Every destructor similarly removes a pointer. Then provide a simple static function to perform an action on all the objects in that list:

class DrawingObject {
private:
static std::list<DrawingObject*> registry;
static DrawAllObjects() {
foreach (object in registry) {
object->Draw();
}
}
}

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!