OOP/RTII, identifying Types for collision detection? Overloading/Overriding?

Started by
6 comments, last by CyJackX 14 years ago
My specific case is this: I have an abstract superclass: GravitationalBody. From it is derived other classes, notably: Planet; Shot; and Ship. GravitationalBody has a field, an ArrayList<GravitationalBody> that contains references to every instance of a subclass: This is because there are static methods in GravitationalBody that loop every game round to adjust the position, velocity, acceleration, etc of every single "Gravitational Body" in the game. There is another static method in there that detects for collisions between any of the aforementioned subclasses. To do this, it takes every object out from the collection and tests for an Area overlap with the rest of the objects. (Overwhelmingly resource consuming at the moment, but not my current worry) Once it detects a collision, however, I am unsure how to enable specific reactions BETWEEN the subclasses. Right now, one body is dubbed "self" and the other is "other," and the code after a collision is detected is: self.collidedWith(other). However, because self and other have both just been taken out of the ArrayList, they are determined to be of the type GravitationalBody. But, I want each subclass to have particular interactions with the other subclasses! For example, a planet colliding with a planet will react differently than a shot colliding with a planet. But how do I properly override and overload these methods? Each subclass right now has the collidedWith() method overridden and overloaded for things such as: collidedWith(Planet planet) collidedWith(Ship ship) collidedWith(Shot shot) etc. The easy way out is obviously to use instanceof, but I would much rather know the classy way of fixing this problem. Is it a flaw in my design or is there a valuable tool out there? ...they say that when there's a problem, make more classes. Perhaps I should have a class called CollisionResolver which will contain all my method interactions? The only problem, then, still, is that I will be passing it my objects as GravitationalBodys instead of the subclasses.
Advertisement
Double dispatch.
I've been away from C# for quite some time, so forgive me If I'm just babbling nonsense here, but could you not have the method: collidedWith(GravitationalBody gb) where you would overload it for each derived class (Planet, Shot, Ship) and within each of those check the passed object (gb) to see if it is a Planet, Shot, or Ship and the apply the proper collision implementation? ie.

public override collideWith(GravitationalBody gb) {	if (gb is Planet) { /* planet collision */ }	else if (gb is Shot) { /* shot collision */ }	else if (gb is Ship) { /* ship collision */ }}
Quote:Original post by enigmatix
I've been away from C# for quite some time, so forgive me If I'm just babbling nonsense here, but could you not have the method: collidedWith(GravitationalBody gb) where you would overload it for each derived class (Planet, Shot, Ship) and within each of those check the passed object (gb) to see if it is a Planet, Shot, or Ship and the apply the proper collision implementation? ie.

*** Source Snippet Removed ***


That is precisely what I'm doing at the moment; I want to know if there is an alternative to having to check, because I've read that using the "is a" statement should be a last resort, and that proper polymorphism and OOP should be the proper way to go.
While you're right that it should be a last resort, I think that this might just be a good time to resort to it. I think in one of the Effective C++ / More Effective C++ books Scott Meyers spends like a whole chapter about pros and cons of using double dispatch for something like this exact situation, but my takeaway was that there wasn't really any clean solution and you're going to have to edit all of the classes anytime you add a new object type anyway (which is the point of avoiding RTTI), so its really no worse of a solution to use it than DD. His Double Dispatch examples looked like a nightmare in C++, (though I don't know what language you're using) so I'd just go with a quick GetType() function and switch on that.

I'll be listening if any Guru's know of a better solution though.
[size=2]My Projects:
[size=2]Portfolio Map for Android - Free Visual Portfolio Tracker
[size=2]Electron Flux for Android - Free Puzzle/Logic Game
Maybe refactoring your objects to a composite/behavior like pattern and implementing an event/subscriber system for them could solve or at least break down your problems... just thinking.

You would then have to deal with more basic object interactions which can be combined to form more complex behaviors.

There are often better approaches to model object behaviors than subclassing, which is taught so extensively in textbooks that one can easily come to the conclusion that it's the only *right* way to do it.

Beware of deep class hierarchies as they can make your code rigid and hard to maintain.
Quote:Original post by Madhed
Maybe refactoring your objects to a composite/behavior like pattern and implementing an event/subscriber system for them could solve or at least break down your problems... just thinking.

Too many patterns, not enough solutions.



Collision response is a n-squared problem. If order doesn't matter (a with b == b with a), then it's half of that.

That is how much unique code must be written, no way around it.


If collision response matches the class hierarchy, then double dispatch is optimal. One function per pair.


If responses do not match the class hierarchy, or responses are non-trivial (some objects bounce off, but on occasion they also fire off sparks and if X and Y collide they explode), then response mechanics must be factored out completely. This is the case where class hierarchy, regardless of inheritance, no longer matches the responses.


Solution in this case is something like this:
Map<String, Response> responses;void collide(Object a, Object b) {  String key = a.getClass().getName() + b.getClass().getName();  Response r = responses.get(key);  if (r != null) r.perform(a, b);}
For each desired response pair, register a handler:
interface Response {  void perform(Object a, Object b);};class ExplodingResponse implements Response {  void perform(Object a, Object b) {    Explodable aa = (Explodable)a; // exact type doesn't matter, we know it's Explodable    Explodable bb = (Explodable)b;    makeExplosionAnimation(a.getLocation());    a.destroy();    b.destroy();  }}
Then, all that's left is to assign this type of behavior to appropriate types:
Response r = new ExplodingResponse();responses.put(Ship.getClass().getName()+Ship.getClass().getName(), r);responses.put(Asteroid.getClass().getName()+Ship.getClass().getName(), r);responses.put(Ship.getClass().getName()+Asteroid.getClass().getName(), r);responses.put(Asteroid.getClass().getName()+Asteroid.getClass().getName(), r);

So if at some point a SpaceStation is added, appropriate responses are added.


Some implementation details:
- If more than one response is allowed, then map will be Map<String, List<Response>>
- Key here is string for simplicity. It could be any other ID, but since we can always get class name, it's just convenient
- If responses are symmetric, then the following optimization can be used to reduce number of responses into half:
void makeKey(Object a, Object b) {  String aa = a.getClass().getName();  String bb = b.getClass().getName();  if (aa.compareTo(b) > 0) {    String temp = aa;    aa = bb;    bb = temp;  }  return aa+bb;}
The trick here is to always use lexicographically smaller/larger of the two as first key. This means that classes A and B will always result in key AB, regardless of order.


This alternative approach keeps collision responses independent from hierarchy. Even more, rather than using types, response could be tied to individual objects, so that one player's ship behaves differently than enemy's. And each individual enemy could have unique response, some could bounce off, some could explode, ....

But the bottom line remains, for each distinct pair (whatever that is), unique response code must be written. Since this number grows fast, it makes sense to either generalize as much as possible (all objects bounce off each other), or to specialize (only player's ship has collision response).
Awesome, I think that that is the idea; I am now reading up on eventHandlers and the like. I guess this is simply another one of those OOP concepts that has slipped by me. Thanks!

This topic is closed to new replies.

Advertisement