🔒 Locked
🎮 Unity
pointers to dead game-objects
Advertisement
Original Post
Hi! I have a problem. When one of the gameobjects in my game dies, other objects might have pointers to it. Like a when a unit is following another unit, it has a pointer to it. The problem is when the leader unit dies, and its game-object is removed, the follower points to a who-knows what. Pointers to all the gameobjects are stored in a array, and all gameobjects have a gameobject-ID, which is the position in the array. This array of gameobjects is processed each gameturn. Is there any standard solutions for this problem? Smartpointers? Expand the gameobject-ID with another ID? //thx T
Quote:
Original post by TechnoCore
Smartpointers?
in-deed it is a decent solution, there are a couple of ways to doing it both involve reference counted representations, first is using non-intrusive reference counted smart pointers which basically means the smart pointer deals with the reference counting system and any type can be used with the scheme with no modifications even built-in primitive types such as int.
The other methods is to have the reference counting system in a common base class that all classes inherit the scheme where you have methods that de/increment the reference count etc then you have an intrusive smart pointer that automatically call these methods for you.
The advantage of the latter is its a little more efficent but the disadvantage is only classes that have reference counting sheme built-in can be used with intrusive smart pointers, the best thing to do is use both together.
Boost libraries have a package of rock solid smart pointers that include intrusive & non-intrusive reference counted smart pointers, you can find out more here.
The things you need to look out for when using this scheme is cyclic dependenciess because the memoery will never be deleted so you need to introduce a so called *weak* reference (weak smart pointer) that breaks the cycle, boost also have weak smart pointers.
Another idea which can also add is to combine the nice things about smart pointers into a *handle* type end up with "smart handles" you can make this scheme generic where you can hook up with all kinds of resource managers that actually hold the instances and give out handles etc, the smart handle maintains a handle (not raw pointer) so when you do "->" internally it will pass the handle to the resource manager to recieve the object and pass the address back to the caller.
Using smart pointers will help you avoid access violations, but it's not a solution to problems that could arise with game mechanics. When you have objects which heavily depend upon the state of other objects, you need a way to notify the dependents of state changes. I don't know how you've designed your following system, but if it's a push system (i.e. the object being followed maintains a list of followers and notifies each one when it moves), then it's just as easy to notify each follower when the object dies. If it's a pull system (i.e. each object maintains a list of the objects it is following and fetches the object's position each frame), then you need to check for null pointers and, in that case, remove the dead object from the list.
The former is, IMO, a better approach than the latter. But neither is very flexible. If you have several cases where an object depends on another object's death, or you intend to reuse your code in future projects, you might want to look into using an event or message system to send state change (or interesting events) notifications between interested objects. For ideas, read up on the design patterns that the Gang of Four call 'Observer'(aka 'Listener'), 'Decorator', or even the 'Command' pattern, any of which could be quite useful for this situation.
The former is, IMO, a better approach than the latter. But neither is very flexible. If you have several cases where an object depends on another object's death, or you intend to reuse your code in future projects, you might want to look into using an event or message system to send state change (or interesting events) notifications between interested objects. For ideas, read up on the design patterns that the Gang of Four call 'Observer'(aka 'Listener'), 'Decorator', or even the 'Command' pattern, any of which could be quite useful for this situation.
You can reclaim most of the memory immediately with a handle-body idiom:
class MyGameObjectWrapper { MyGameObject* contents; public: MyGameObjectWrapper(args) { contents = new MyGameObject(args); } ~MyGameObjectWrapper() { destroy(); } void destroy() { delete contents; contents = null; } // Add a bunch of delegation methods like: void wibble() { if (contents) contents->wibble(); // else do some reasonable default action }}// Now for any given MGO object, there is a corresponding MGOW which holds the// sole pointer to the MGO - this makes the deletion safe. Of course, you now// end up with containers full of "destroyed" MGOWs, which have to do something// sensible for all requests.// At the end of program, you're still responsible for deleting all allocated// MGOWs exactly once, and worry about dangling pointers to the one you just// deleted, but it may be easier to do things all in one pass like this. (You// might also have a manager which knows about all allocated MGOWs to make this// easier - it would delete all of them at once, and then other client objects// would just forget about them.)
Personally I'd stick the "dead" objects on to a "dead" list which is not cleaned out immediately - they should stay around for at least one frame, allowing anybody who has pointers to them to realise they are dead and unhook them.
Objects on the dead list won't be moved, have collision detection or rendering.
That's what I've always done. Other options are:
- Smart pointers
- Observer pattern - have things looking at an object get notified when it dies so that they can unhook their pointers.
Because I find that this actually happens fairly infrequently, I can manually code around each case. Common situations are:
- Missiles etc, which "belong" to a player (or enemy) - these remember whose they are for scoring and C/D purposes
- Guided missiles - these remember who they're targetting
- Things like doors / switches which remember which door / switch they belong to - although in my previous games these are indestructible
- "View" objects which track things (for split-screen views etc)
Mark
Objects on the dead list won't be moved, have collision detection or rendering.
That's what I've always done. Other options are:
- Smart pointers
- Observer pattern - have things looking at an object get notified when it dies so that they can unhook their pointers.
Because I find that this actually happens fairly infrequently, I can manually code around each case. Common situations are:
- Missiles etc, which "belong" to a player (or enemy) - these remember whose they are for scoring and C/D purposes
- Guided missiles - these remember who they're targetting
- Things like doors / switches which remember which door / switch they belong to - although in my previous games these are indestructible
- "View" objects which track things (for split-screen views etc)
Mark
Have an array containing pointers to all GameObjects. Give each object a unique serialno or id on creation. When an object dies, deallocate it from memory and zero its pointer in the array.
Give each follower an ObjectReference to its target:
Example of an object's think method, where enemy is an ObjectReference:
Use the ptr method of ObjectReference to get a pointer to the target object, and to verify that it points to the right target. The unique id ensures that, if the location in the array is reused, followers will not follow the wrong target.
Give each follower an ObjectReference to its target:
class ObjectReference { int index; int id; public: GameObject* ptr() { GameObject* obj = game::objects.ptr( index ); if( obj && obj->id() == id ) return obj; else return 0; } void set( GameObject* target ) { index = game::objects.index( target ); id = target->id(); }}Example of an object's think method, where enemy is an ObjectReference:
void Soldier::think( ... ) { GameObject* e; if(( e = enemy.ptr()) == 0 ) // Forget it, enemy is dead... //... else use e-> directly}Use the ptr method of ObjectReference to get a pointer to the target object, and to verify that it points to the right target. The unique id ensures that, if the location in the array is reused, followers will not follow the wrong target.
Quote:
Original post by Aldacron
When you have objects which heavily depend upon the state of other objects, you need a way to notify the dependents of state changes.
I totally agreed with him, and I think the pointing to ... deleted memory spaces is not symtom of the problem.
imo, the good way to fix the case is, in addition of follower units keep track of unit being followed(leader unit), your leader unit should also keep a list of followers, then before leading unit is removed, send message to all followers to stop follow.
hope this help,
To the anonymous poster:
you will always have *some* sort of dependance, since *something* needs to make the relation between the leader and the followers. What you don't like is the dependance in both directions.
There is always the possibility to have an "external" observer that keeps track of things.
And, you can always have the leader be the observer, but when a follower dies, the follower doesn't notifes the leader (you're right, leader shouldn't care). Instead, when next time the leader does something and it goes and updates all its subscribers (followers), you check if each follower is still alive (if you use handles or smart pointers) and if it isn't, you unsubscribe it.
There are many ways to skin the cat. I find that in such situations, you need to ask yourself many questions, and sometimes break few rules to get speed or break few rules to get more flexibility:
1) does the observer pattern allow me to be super flexible? - Yes
2) does the observer introduce perhaps too much interdependance? - Maybe.
Let's see now how that interdependance affects us:
- what happens more often? Will the leader be notifying its followers more often then the followers be notifying the leader? If the followers will notify the leader only when they die (and let's say that happens relatively rarely), then the dependance from the follower to the leader could be removed (theoretically). But if we remove this dependance, suddenly, when the leader needs to notify it's followers (which happens ALL the time, for example when the leader moved) it needs to also check if the pointer still points to something (perhaps using masrt pointers). So now for operations that happen often, we have an extra if, slowing things down a bit.
So we have traded, once again, a bit of de-coupling for performance.
There is never a silver bullet. Things always depend on the situation. In this case, I would definitely use the observer pattern the way it was intended - by having followers notifying the leader. There is actually nothing wrong with this. The observer can be, if necessary an outside class. This pattern works beautifuly.
Finally, it could be that the leader WANTS to know about what is going on with his followers. This would be in a case of a NPC, not when the leader is the player. For example, if the leader has a band of hoodlums following him, the leader might feel more "bold" and attack units he would not attack if he knows he has one follower. So you see, in this case it's very important the followers notify it's leader, since it will affect the state transitions.
Anyone see any flaw in my logic, please go ahead and destroy my arguments (i mean it)
my 2 cents
you will always have *some* sort of dependance, since *something* needs to make the relation between the leader and the followers. What you don't like is the dependance in both directions.
There is always the possibility to have an "external" observer that keeps track of things.
And, you can always have the leader be the observer, but when a follower dies, the follower doesn't notifes the leader (you're right, leader shouldn't care). Instead, when next time the leader does something and it goes and updates all its subscribers (followers), you check if each follower is still alive (if you use handles or smart pointers) and if it isn't, you unsubscribe it.
There are many ways to skin the cat. I find that in such situations, you need to ask yourself many questions, and sometimes break few rules to get speed or break few rules to get more flexibility:
1) does the observer pattern allow me to be super flexible? - Yes
2) does the observer introduce perhaps too much interdependance? - Maybe.
Let's see now how that interdependance affects us:
- what happens more often? Will the leader be notifying its followers more often then the followers be notifying the leader? If the followers will notify the leader only when they die (and let's say that happens relatively rarely), then the dependance from the follower to the leader could be removed (theoretically). But if we remove this dependance, suddenly, when the leader needs to notify it's followers (which happens ALL the time, for example when the leader moved) it needs to also check if the pointer still points to something (perhaps using masrt pointers). So now for operations that happen often, we have an extra if, slowing things down a bit.
So we have traded, once again, a bit of de-coupling for performance.
There is never a silver bullet. Things always depend on the situation. In this case, I would definitely use the observer pattern the way it was intended - by having followers notifying the leader. There is actually nothing wrong with this. The observer can be, if necessary an outside class. This pattern works beautifuly.
Finally, it could be that the leader WANTS to know about what is going on with his followers. This would be in a case of a NPC, not when the leader is the player. For example, if the leader has a band of hoodlums following him, the leader might feel more "bold" and attack units he would not attack if he knows he has one follower. So you see, in this case it's very important the followers notify it's leader, since it will affect the state transitions.
Anyone see any flaw in my logic, please go ahead and destroy my arguments (i mean it)
my 2 cents
Thank you all for your input & help!
I have finally choosen Machinoid solution.
The game is pretty large now (rts/rpg crossover), and there are so many places where gameobjects refer to eachother in the code that i sometimes wake up screaming in the middle of the night.
I really need as little interdependance as possible :)
I have finally choosen Machinoid solution.
The game is pretty large now (rts/rpg crossover), and there are so many places where gameobjects refer to eachother in the code that i sometimes wake up screaming in the middle of the night.
I really need as little interdependance as possible :)
I second (or third/forth) the Observer pattern suggestion. It allows the followers to be notified when the followee dies etc.
As for creating too much interdependance I wholeheartedly disagree. All the object being followed needs to know is that it mave have *something* observing it (an object that implements the observer interface) and it doesn't have to know what specific type of object it is. Likewise for a object following something else all it knows or cares is that it is following something observable. It seems like the perfect solution to me. The only dependencies are on the observer/observable classes and you can easily swap out the specific classes with others so long as they implement the required interfaces/extend from the right class.
BTW, Machinoid's solution actually creates more interdependance than using the Observer pattern. All of your game objects will be dependent on the class "game", which may or may not be that much of a big deal. Do your objects really need to know which "game" they belong to? Maybe they do.. but the problem is that if you want to reuse some of your classes in another game you'd have to make sure that you also reuse the game class (or a hacked up version of it). In real life that might not be a problem but from a Software Engineering stand point it's bad.
[Edited by - necromancer_df on February 20, 2005 11:53:51 AM]
Quote:
Original post by nikdo
1) does the observer pattern allow me to be super flexible? - Yes
2) does the observer introduce perhaps too much interdependance? - Maybe.
As for creating too much interdependance I wholeheartedly disagree. All the object being followed needs to know is that it mave have *something* observing it (an object that implements the observer interface) and it doesn't have to know what specific type of object it is. Likewise for a object following something else all it knows or cares is that it is following something observable. It seems like the perfect solution to me. The only dependencies are on the observer/observable classes and you can easily swap out the specific classes with others so long as they implement the required interfaces/extend from the right class.
Quote:
Original post by TechnoCore
I really need as little interdependance as possible :)
BTW, Machinoid's solution actually creates more interdependance than using the Observer pattern. All of your game objects will be dependent on the class "game", which may or may not be that much of a big deal. Do your objects really need to know which "game" they belong to? Maybe they do.. but the problem is that if you want to reuse some of your classes in another game you'd have to make sure that you also reuse the game class (or a hacked up version of it). In real life that might not be a problem but from a Software Engineering stand point it's bad.
[Edited by - necromancer_df on February 20, 2005 11:53:51 AM]
Just to back up what others have said about the Observer approach, it does not create too much interdependence, although there is some memory overhead of keeping track of other objects (but you have to do that anyway).
I decided to have a UniversalObserver and a UniversalSubject class that are inheritable. Subjects can get linked up with Observers and still not know of the actual class of the Observer (all it knows is that it is being observed).
I chose to have an enum for the messages, but you could easily use an int or a string or anything else you could imagine to make your job easier.
Having this system is so flexible, i can use it for my game objects AND my GUI system. Anything that needs to be a subject or an observer simply needs to inherit from one class or the other (or both).
Feel free to use my fairly simple code. I've tested it, am using it in my project, and there are no apparent bugs:
HEADER:
BODY:
I decided to have a UniversalObserver and a UniversalSubject class that are inheritable. Subjects can get linked up with Observers and still not know of the actual class of the Observer (all it knows is that it is being observed).
I chose to have an enum for the messages, but you could easily use an int or a string or anything else you could imagine to make your job easier.
Having this system is so flexible, i can use it for my game objects AND my GUI system. Anything that needs to be a subject or an observer simply needs to inherit from one class or the other (or both).
Feel free to use my fairly simple code. I've tested it, am using it in my project, and there are no apparent bugs:
HEADER:
#ifndef UNIVERSAL_OBSERVER_H#define UNIVERSAL_OBSERVER_H#include <list>/*Observer signals are signals that are broadcast by a Subject and received by an Observer. The signal reflects the status or change in status of the Subject, not the Observer. The Observer may deal with the Subject's broadcast signals however it wishes. Observers respond to this "stimulus" in a generic fashion. When they receive a signal, they perform an action. The signal is not tied to a specific object but the response to that signal may be.*/enum observer_signal { // PLAYER SIGNALS SIGNAL_UP = 0, SIGNAL_DOWN, SIGNAL_LEFT, SIGNAL_RIGHT, SIGNAL_FIRE, SIGNAL_CHARGE, SIGNAL_CHARGE_RELEASE, SIGNAL_JUMP, SIGNAL_DEATH, SIGNAL_DAMAGE, SIGNAL_CONTACT, SIGNAL_MOVE, // doubles as a GUI signal SIGNAL_STOP, // GUI SIGNALS SIGNAL_BUTTONCLICK, SIGNAL_MOUSECLICK, SIGNAL_FOCUS, // ANYTHING ELSE SIGNAL_CUSTOM_1 = 101, SIGNAL_CUSTOM_2 = 102, SIGNAL_CUSTOM_3 = 103, SIGNAL_CUSTOM_4 = 104, SIGNAL_CUSTOM_5 = 105, SIGNAL_CUSTOM_6 = 106, SIGNAL_CUSTOM_7 = 107, SIGNAL_CUSTOM_8 = 108, SIGNAL_CUSTOM_9 = 109 }; // predecclass UniversalSubject;class UniversalObserver {public: UniversalObserver(); virtual ~UniversalObserver(); // report unregister to subject // This is where all the real signal-response meat goes: virtual void ReceiveSignal( observer_signal sig ) = 0; // Use these functions so that the class will record the Subject. // When we die, we automatically unregister ourself. void RegisterAsObserverWith( UniversalSubject* subject ); void UnregisterAsObserverWith( UniversalSubject* subject ); private: std::list<UniversalSubject*> subjects; typedef std::list<UniversalSubject*>::iterator subject_iter; }; class UniversalSubject {public: UniversalSubject(); virtual ~UniversalSubject(); void RegisterObserver( UniversalObserver* obs ); void UnregisterObserver( UniversalObserver* obs ); protected: void BroadcastSignal( observer_signal sig ); private: std::list<UniversalObserver*> observers; typedef std::list<UniversalObserver*>::iterator observer_iter; }; #endifBODY:
#include "UniversalObserver.h"UniversalObserver::UniversalObserver() { subjects = std::list<UniversalSubject*>(); }UniversalObserver::~UniversalObserver() { // unregister with all subjects for (subject_iter i = subjects.begin(); i != subjects.end(); i++) { (*i)->UnregisterObserver( this ); } } void UniversalObserver::RegisterAsObserverWith( UniversalSubject* subject ) { if (!subject) { return; } subject->RegisterObserver( this ); subjects.push_back( subject ); } void UniversalObserver::UnregisterAsObserverWith( UniversalSubject* subject ) { if (!subject) { return; } subject->UnregisterObserver( this ); for (subject_iter i = subjects.begin(); i != subjects.end(); ) { if (*i == subject) { i = subjects.erase(i); } else { i++; } } } UniversalSubject::UniversalSubject() { observers = std::list<UniversalObserver*>(); }UniversalSubject::~UniversalSubject() { // notify observers that we are dead // NOTE: doing this modifies our own list because UnregisterAsObserverWith() // also calls our own UnregisterObserver() via the other object. while ( observers.size() != 0 ) { (*observers.begin())->UnregisterAsObserverWith( this ); } }void UniversalSubject::RegisterObserver( UniversalObserver* obs ) { if (!obs) { return; } observers.push_back( obs ); }void UniversalSubject::UnregisterObserver( UniversalObserver* obs ) { if (!obs) { return; } for (observer_iter i = observers.begin(); i != observers.end(); ) { if (*i == obs) { i = observers.erase(i); } else { i++; } } }void UniversalSubject::BroadcastSignal( observer_signal sig ) { for (observer_iter i = observers.begin(); i != observers.end(); i++) { (*i)->ReceiveSignal( sig ); } }Quote:
Original post by Anonymous Poster Quote:
Original post by rai_chu
imo, the good way to fix the case is, in addition of follower units keep track of unit being followed(leader unit), your leader unit should also keep a list of followers, then before leading unit is removed, send message to all followers to stop follow.
This is not very clean as it causes all sorts of cross-dependencies, one for each type of relation between objects. Not only needs the tracked unit know of all followers it has - that's none of its business. But this also requires some complicated communication protocol between objects. What if a follower dies? It must then message the leader so that the leader knows not to send messages about its own death to a follower who is no longer alive, and so on...
The best approach is one of little interdependence.
I agreed that create cross-dependencies is not a neat things to do, however I consider my point is still valid, a good solution shouldn't have anything to do with deleted memory spaces, that would be a curing solution, not preventing.
as nikdo, necromancer_df and leiavoia pointed out, observer pattern is undoubtedly a better solution. and it's also a prevent-type of solution.
imo, if one want to implement the solution that is not fixed right at the symtom, why not just check if pointer to leader object is NULL before execulte follow-functions or any other functions that has leader dependencies?
Hmmm. No one's suggested using a generalised messaging system. Can be quite useful. For all objects, create a common base class (I'm assuming C++):
So, deleting an object automatically broadcasts a message to all other objects that it has been deleted. The derived class' HandleMessage need to respond by updating their own internal states. You can also use the system to send other messages as well - the exact messages being dependant on your application.
I've put the active list handling in the base object. You can put it in its own class if you want.
Skizz
class BaseObject{public: BaseObject (void); // add object to active list virtual ~BaseObject (void); // remove object from active list and broadcast a deletion message virtual HandleMessage (/*some message data*/) = 0; static void BroadcastMessage (/*message data*/);private: static BaseObject *m_list_of_active_objects [SOME_MAXIMUM]; // or use a list/vector/whatever};So, deleting an object automatically broadcasts a message to all other objects that it has been deleted. The derived class' HandleMessage need to respond by updating their own internal states. You can also use the system to send other messages as well - the exact messages being dependant on your application.
I've put the active list handling in the base object. You can put it in its own class if you want.
Skizz
skizz,
what you outlined is precisely what leiavoia did - with his observer pattern.
If you noticed, with his system, you have two interfaces "UniversalSubject" and "UniversalObserver". You could, if you wanted, take your BaseGameEntity and have this base class implement these two interfaces and voilà, you have the same system you proposed.
Furthermore, what I like more (no offense) about his system is that you have a choice to decide if a particular class will be just an abserver or just a subject or both.
what you outlined is precisely what leiavoia did - with his observer pattern.
If you noticed, with his system, you have two interfaces "UniversalSubject" and "UniversalObserver". You could, if you wanted, take your BaseGameEntity and have this base class implement these two interfaces and voilà, you have the same system you proposed.
Furthermore, what I like more (no offense) about his system is that you have a choice to decide if a particular class will be just an abserver or just a subject or both.
leiavoia
thank you for your post (and the code) for the observer and the subject.
I looked at it a lot and came to understand why you do what you do the way you do (hehe).
I liked it a lot and I am currently fitting it into my design, since I am thinking of a rather complicated messaging system and your classes fit in beautifuly.
So, once again, thanks!
thank you for your post (and the code) for the observer and the subject.
I looked at it a lot and came to understand why you do what you do the way you do (hehe).
I liked it a lot and I am currently fitting it into my design, since I am thinking of a rather complicated messaging system and your classes fit in beautifuly.
So, once again, thanks!
Is this how you would do it with boost?
typedef boost::shared_ptr<Object> ObjectHandle;typedef boost::weak_ptr<Object> ObjectReference; // When a missile is fired ... ObjectHandle missile( new Missile ); ObjectReference missileReference( missile ); gameObjects.Add( missile ); target.AddIncoming( missileReference ); radar.AddBogey( missileReference ); targeting.AddMissile( missileReference ); // When the missile hits and destroys the target ... gameObjects.Remove( missile ); gameObjects.Remove( target );class OtherObject{ void DoSomethingWithReferencedObjects() { ... ObjectHandle pObject = referencedObject.lock(); if ( pObject ) { // Do something with the referenced object } else { // Remove reference } ... }};
One thing i might add about Observer systems is that there are a few ways to construct them. Mine is pretty simple. One thing you could add to it is a sender. When an observer receives a signal, it's like a stimulus. But it's not directed at anything in particular. Often, the Observer is observing a specific object or group of objects, so it might be good to also send a pointer to yourself when broadcasting a signal to others so they know who to reply to. Otherwise it's like sending mail to people with no return address. If they were expecting your letter, that's fine, but if they have lots of friends they talk with...
Quote:
Original post by JohnBolton
Is this how you would do it with boost?
That's how I'd do it, yes :). Also, you probably want to use [ source ] tags for longer snippets - so they get pruned out and not supporting quote-zillas.
Topic Locked
This topic has been locked by a moderator. New replies are not allowed.
Sign in to reply to this topic.
Advertisement