Jump to content
  • Advertisement
Sign in to follow this  
CyJackX

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

This topic is 3078 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

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.

Share this post


Link to post
Share on other sites
Advertisement
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 */ }

}

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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).

Share this post


Link to post
Share on other sites
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!

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.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!