Sign in to follow this  
dzeligman

Entities talking with their director

Recommended Posts

I just recently redid a lot of my design and am currently puzzled about the better way of accomplishing my current task. My world contains a list of all my game objects.(Currently my gameObjects contain both the view and the model since this is kind of a small game). It updates them like so :
            foreach (GameObject gameObject in gameObjects)
            {

                    gameObject.Update(gameTime);
         
            }
I use double dispatch for collision handling :
            foreach (GameObject gameObject in gameObjects)
            {
                foreach (GameObject gameObject2 in gameObjects)
                {
                   //...do detection stuff
                                    gameObject.Collide(gameObject2);
                                    gameObject2.Collide(gameObject);
                    
                }

            }
And I remove them like so...
   foreach (GameObject entity in gameObjects)
            {
               //... if entity is dead or marks itself as needing to be removed, or isnt on the screen
                    gameObjectsToRemove.Add(entity);
                
            }

            foreach (GameObject entity in gameObjectsToRemove)
            {
                gameObjects.Remove(entity);
            }
Now my dilemma is how I dynamically create new objects that needed to be added to the gameObjects list when another object dies. For instance, when a alienship is destroyed there needs to be a chance that a powerup is created. I was thinking that one way would to have some sort of alien death event that the alien notifies the world of, but wasnt exactly sure on how to perform this. Currently as a hack way of doing this I pass a reference to the world to each alien and within the collision handler I call createPowerUP on the world object. Down the line I will have a separate system for creating and intializing the gameObjects in some sort of level class, but I will need to support the dynamic addition of new objects.

Share this post


Link to post
Share on other sites
Hey dzeligman

First of all I find alot of things about your post a bit confusing. You post a lot of, what seams to be, unrelated code to the actual problem or dilemma as you call it. I'm not sure if you want us to comment on the code or if it's there to provide us with an overview of what you're doing. Ok well that aside here goes.

The way you're doing you're collision detection I guess would work out as long as you don't deal with two many game objects. I just want to point out that the way you're doing it does alot of superfluous checking which would result in double handling of collisions plus it's O(x^2) speed which doesn't scale well with increased amounts of game objects. I also want to point out that what you're doing is not a double dispatch (atleast the part that you've shown) but rather a normal single dispatch. Nothing wrong with that, it just doesn't provide typesafety the way a double dispatch does. More information can be found here. http://www.gamedev.net/community/forums/topic.asp?topic_id=501210.

Regarding your question. One way you could implement this is by creating an event dispatch mechanism. I don't know which language you're using since it doesn't say in your post. But seeing your coding style and the language keywords you're using could imply C# (or rather XNA because of the gameTime) but again I don't know. C# does have a very neat and easy to use multicast delegate event dispatcher for which I'm sure google can give you a bunch of tutorials on how to use. Have your world object register with all newly created objects OnDeath event dispatchers and the world object will get notified everytime your alienships (or anything else that you registered) dies. This post is not ment as an tutorial on how to use events to communicate between objects, so bear with the slim introduction. There's plenty of those out there! In case you're using C++ there's a neat fast singlecast delegate to be found here http://www.codeproject.com/KB/cpp/FastDelegate.aspx.

I sure hope this helps out a bit. If not, feel free to either correct me or ask again!

Share this post


Link to post
Share on other sites
Thanks for the response.

Quote:
I just want to point out that the way you're doing it does alot of superfluous checking which would result in double handling of collisions plus it's O(x^2) speed which doesn't scale well with increased amounts of game objects.


Yeah its a relatively small 2d shooter in XNA. I have code that makes sure it doesnt do detections if the two objects are the same, not active, etc. I could probably minimize tests using quad trees or some other optimization, but I'm not hurting at all in performance so I dont think I should worry about optimization.

I originally had separate lists for the different kinds of objects, but I like having all of them in one because it makes maintaining them easier.

Quote:
I also want to point out that what you're doing is not a double dispatch (atleast the part that you've shown) but rather a normal single dispatch. Nothing wrong with that, it just doesn't provide typesafety the way a double dispatch does. More information can be found here.

I guess it really isnt since C# doesnt support it, but what I do exactly is I use reflection to put all the collide methods and their methodinfo in a static hash for the gameObject so at runtime the proper collide method is called, so code like this works :
        public void Collide(Shot shot)
{

HitPoints -= (int)(100 * shot.Damage);
if (HitPoints <= 0)
{
isDead = true;
world.doEnemyKillAction(this);
}
}

public void Collide(PlayerShip playerSet)
{
isDead = true;
world.doEnemyKillAction(this);
}


My original question has to deal with not having to perform the world.doEnemyKillAction. I guess I will just look into C#'s event handlers with delegates and use some form of the observer perhaps with the OnDeath event.

I included all the original code to attempt to give context such that I wouldnt have to change that code ever if I added new kinds of objects.

Share this post


Link to post
Share on other sites
Quote:
Original post by dzeligman
I guess it really isnt since C# doesnt support it, but what I do exactly is I use reflection to put all the collide methods and their methodinfo in a static hash for the gameObject so at runtime the proper collide method is called, so code like this works :
*** Source Snippet Removed ***


Well you're wrong about that. There's a difference between multimethods (only supported natively in a few select languages like Perl) and double dispatch. Double dispatch is very much possible in C#. One example people on gamedev like to use is the visitor GoF pattern http://www.dofactory.com/Patterns/PatternVisitor.aspx. I understand what you're doing using reflection to get the correct method to call, but it seams like a really cumbersome way to do it, and I believe the visitor pattern would give you a cleaner design. Again without having you're implementation it's hard to tell - but that's my gut feeling.

Regarding the collision checking algoritm. Yes you could use some sort of spatial structure to help you speed up things - But that wouldn't really be needed for this. I think you missed my point before. I'm not only talking about objects getting collision handled with themselves, but with every other object twice because of the way you're iterating the lists. Oh and btw there's nothing wrong with keeping all the objects in one list for the type of game you're making!

And since you ARE using C#, event delegates are definitly a healthy way to go. It provides a nice decoupling between the event dispatcher and event listener in terms of code and logic.

Share this post


Link to post
Share on other sites
It sounds like your dilemma is that your World knows about your GameObjects, but your GameObjects don't know about the world, presumably because you don't want to get ugly circular dependency stuff going. Which is a laudable goal, but the truth of GameObjects is that their interaction with the world really is bidirectional and needs to be modeled as such. If you're not happy with the idea of your GameObjects holding a reference to the World, have them instead hold a reference to an interface exposed by an inner class of World, which gives them only the information and capabilities that they need.

Share this post


Link to post
Share on other sites
I originally tried a more actual visitor pattern approach, but I didnt like the results mostly because regardless of whatever I tried I still had to check against the type of the passed in gameObject.

For example, if I were to make a Enemy vistor it would either need to check if the passed in gameObject to its visit method is a projectile, player, etc.
OR the alternative I can think of right now would to create a specific visitor for each individual collision case which is similar to what I have now with the multiple methods I guess. That approach would probably be a little more clean and better for a multi-man project, but I'm still not sold.


I'll def redo my collision detection, thanks.

edit: @Sneftel,
Thanks for the reply.

I guess the relationship really is bi-directional, just trying to experiment and see what I like. I will probably try the inner interface approach first and then experiment with some event handling.

Share this post


Link to post
Share on other sites
Quote:
Original post by Sneftel
... their interaction with the world really is bidirectional and needs to be modeled as such. If you're not happy with the idea of your GameObjects holding a reference to the World, have them instead hold a reference to an interface exposed by an inner class of World, which gives them only the information and capabilities that they need.


I would argue that a simple function, such as std::sqrt, has a bidirectional interaction with its caller (after all, the caller sends the data to be processed, and the function sends back the computation result). Therefore, one must provide the function with a reference to its caller !

There are many ways of implementing bidirectional interactions without having to provide an explicit reference to a class. One of these ways is simply to use a return value: an idiom which I find interesting is to have the "update" function return the list of external effects an object wishes to apply (leaving it up to the world to actually apply these effects). This makes testing a whole lot easier (because the entity is stand-alone and does not need to be provided with what is ultimately a callback), reduces dependencies just as much (if not more) and improves the localization of the world-altering code (a callback can be stored and called anytime, a return value can only happen when a certain function call ends).

Share this post


Link to post
Share on other sites
Quote:
Original post by dzeligman
I originally tried a more actual visitor pattern approach, but I didnt like the results mostly because regardless of whatever I tried I still had to check against the type of the passed in gameObject.

For example, if I were to make a Enemy vistor it would either need to check if the passed in gameObject to its visit method is a projectile, player, etc.
OR the alternative I can think of right now would to create a specific visitor for each individual collision case which is similar to what I have now with the multiple methods I guess.


Again I think you're misunderstanding the Visitor pattern, or well the whole double dispatch idiom (or I'm explaining it badly :)). If for example you where to make your GameObject both the visitable and visitor it would be able to visit others of it's own kind. No matter which specilization you're using. The visitor interface would define a method for each type: void Collide(PlayerShip); void Collide(Shot); etc. Plain ol' method overloading. Then whenever you detect a collision between two objects; Object A and B no matter what type they are you would resolve the collision like this.


a.accept(b); // Which in turn would call Collide on object b (The visitor) using this (Object a) of which we know the type. The method overloading would take care of finding and calling the correct method.

b.accept(a); // Same thing here just the other way around.





As you can see there's no need to ever check against the game object type because of the fact that you know the type of the this object inside the called accept(Visitor) method. Since you're using C# you might not need this type resolve since it's so easy. But using a double dispatch there's no need to check types.

Quote:
Original post by dzeligman
That approach would probably be a little more clean and better for a multi-man project, but I'm still not sold.


Don't underestimate the amount of problems even small designfarts can cause you no matter how big your project is.

A final note - I completly agree with what ToohrVyk proposed. Using return values is a perfectly valid and clean way to communicate changes to the callee, in this case the World object.

Share this post


Link to post
Share on other sites
Quote:
Original post by ToohrVyk
I would argue that a simple function, such as std::sqrt, has a bidirectional interaction with its caller (after all, the caller sends the data to be processed, and the function sends back the computation result). Therefore, one must provide the function with a reference to its caller !
Yes, and this is implicitly done; see "continuations" for the details. Objects, on the other hand, have no notion of continuations. (This doesn't really address your point; I just thought I'd take the opportunity to show off a Cool Programming Thing.)

Quote:
There are many ways of implementing bidirectional interactions without having to provide an explicit reference to a class. One of these ways is simply to use a return value: an idiom which I find interesting is to have the "update" function return the list of external effects an object wishes to apply (leaving it up to the world to actually apply these effects).

That's a perfectly reasonable approach. It's not one which plays particularly well with most non-functional programming languages, though. To do it right requires the use of the Command pattern, which tends to require a depressing amount of boilerplate code. (In a functional language you'd just return a lambda, possibly in a state monad, and be done with it.) It also requires that each entry point into the object that could potentially have side effects handle the list of returned effects, or otherwise a complicated effect queueing system (yay, more boilerplate). Finally, it disallows more complicated bidirectional interaction by only allowing one talk-back per update call. (TBF, also only a problem if you're doing it in a non-functional language.) Oh, and it morally discourages (or even prohibits) gameobjects from holding long-term references to objects in the world; whether that's a good thing or a bad thing probably depends on individual preference.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this