my engine, mutually nested objects- bad?

Started by
15 comments, last by Kelly G 16 years ago
What relationship does Monster have with the Monster Manager? What methods does Monster call?

I suppose that a monster tells its manager to remove it from its list, yes?


There are a couple approaches one could take for this problem --

First, each monster could have an "active" tag which is set to active when the monster is alive or not when it isn't. When the manager goes through it's list, it only processes the active monsters, and it places inactive monsters on a free list so that they can be re-used as a different monster later (here's where your run-time behavior binding comes in handy) -- actually, you could just remove inactive monsters from the list and, assuming no other references to them are kicking around, they should get garbage-collected soon enough.

Another approach is to implement a messaging and events system, which is designed to loosen the coupling between classes. Essentially, instead of calling Manager.RemoveMonster(me), you place a message in a queue for the manager to process eg PostMessage(MONSTER_MANAGER, REMOVE, me) -- So the Monster knows that something exists which manages it, but it doesn't know any of the specifics of the Manager itself, which is what encapsulation is all about.

Unlike your original "problem" this one actually is an interdependence you should strive to get rid of. I would also recommend that you try to separate the Factory functionality from the Manager functionality, and that you be very careful about what all the Manager functionality includes. Its very easy to just throw a bunch of stuff into a "manager" class, but if it takes on more than one responsibility you are violating correct OO design. A good place to start is to consider whether your manager does anything more than acting as a collection of monsters -- if it does, separate additional functionality into another class and rename it to MonsterCollection, if it is only a collection, just rename it MonsterCollection.

throw table_exception("(? ???)? ? ???");

Advertisement
Actually I just want the monster to have access to the monster object manager so that it can reference other instances.

Example- you could define for a monster's time slice event:
void time_slice_event (monster_object self, monster_object_manager MM ){  if (MM["monster leader"].exists())  {     if (self.location.distance(MM["monster leader"].location)>30)    self.location = self.location.stepTowards(MM["monster leader"].location);  }}


The situation you describe, Ravyne, I would like to avoid so I'll probably do like you were saying in your last paragraph. i think it would be too weird for an object to be able to delete itself through the monster object manager.
Why don't you just pass the other instances to the time_slice_event function?
In case of a leader you could add that one as an attribute. That way different monsters can have different leaders. Or better: Introduce a monster group and make one monster the leader and the others its followers. Thus you just change the leader of the group without having to iterate over every monster.

Just as a side note: in your example you call MM["monster leader"] 3 times, in order to get the same object. That would require you to look it up 3 times, which is likely to be somewhat slow (string comparisons, string-to-int conversions, lookup routines, etc.). If you really need to query MM directly, just get the monster instance you want and store a reference or pointer for reuse during the execution of the function.
If I was helpful, feel free to rate me up ;)If I wasn't and you feel to rate me down, please let me know why!
Quote:
Why don't you just pass the other instances to the time_slice_event function?
In case of a leader you could add that one as an attribute. That way different monsters can have different leaders.

That would be good but the function call (the parameters) is going to have to be the same for all game objects. This is because there is an encapsulated routine that calls the event handling methods. So each type of monster could have a different leader but that is up to the code body of the function.

Quote:
Or better: Introduce a monster group and make one monster the leader and the others its followers. Thus you just change the leader of the group without having to iterate over every monster.

Maybe, as you say, I should implement some other layer of the architecture that handles the interdependency between objects?

Quote:
Just as a side note: in your example you call MM["monster leader"] 3 times, in order to get the same object. That would require you to look it up 3 times, which is likely to be somewhat slow (string comparisons, string-to-int conversions, lookup routines, etc.). If you really need to query MM directly, just get the monster instance you want and store a reference or pointer for reuse during the execution of the function.


That's true. I was just using this as an illustrative example of how I intend to use MM rather than real code. I agree your way would be more optimal.
I've been absent minded! The system I exemplified would work for data inherent to all monsters but not user-defined that is specific to a certain class of monsters (unless each monster had some kind of keymap array for that). Maybe a message system would be more appropriate. I vaguely recall reading about such systems for game entities, .

So, for the example before- the leader monster would call a method to send a message to every monster of a particular class.

void time_slice_event (monster_object self){  // Do whatever to move around   MyMessage M = new MyMessage("follow_me")  M.addparameter(self.location);  M.send_to_all(M, "minion");}


and then each entity would have a receive_message event. the minion would
do this

void receive_message (monster_object self, MyMessage Message_received) { if  ((Message_received.message=="follow_me") && (Message_received.senderclass == "monster leader"))    {     if (self.location.distance((Vector3D)Message_received.parameters[0])>30)         self.location = self.location.stepToward (Vector3D) Message_received.parameters[0];    } }


So I like that a little better even though it requires more code and, once again, optimization.
In your last post you moved the movement code outside of the event where the rest of the movement is placed (from the time_slice_event to receive_message). This might complicate tracing errors.

A better approach might be to send a 'follow_me' message once and passing the sender as a pointer. This would trigger a 'watch leader' state within the receiver of the message which then monitors the leader (passed as the sender) every time_slice_event and updating its position accordingly. Add a 'stop_following_me' message to reset the receiver to a state with its own movement AI.
This is kind of a hybrid of the two approaches. Direct referencing + messaging.

This topic is closed to new replies.

Advertisement