Skip to main content
GameDev.net gamedev.net
🔒 Locked 🎮 Unity

Collision detection poll

Started by ToohrVyk Aug 27, 2006 at 1:51 PM 16 replies 1.7k views
Original Post
ToohrVyk
ToohrVyk
Hello. Here's a quick poll for you: how do you handle multiple-dispatch polymorphism when detecting collisions? To clarify, how do you decide that the collision that just occured happened between an Enemy and a Bullet, instead of an ICollideable and another ICollideable?
  1. You use RTTI and dynamic_cast (in C++) or as/is (in C#).
  2. You use your own in-house version of RTTI.
  3. You use the visitor pattern and a large amount of virtual functions.
  4. You perform collision detection separately for each possible pair of types.
  5. You use another method (please explain).
I'm currently working on option 5, by using a single virtual function to do the dispatch on a template class with the two types as arguments. What do you guys use? EDIT: for completeness, below is an example using my method.

#include <iostream>
#include <string>

#include "collider.hpp"

/* ========================================================================
 * PRELIMINARY DEFINITIONS
 * ======================================================================== */

// Step 0: decide how the position and aspect of entities are
// represented, by creating a hull type and typedef'ing the 
// important classes.

struct Hull {
    
  // Horizontal position
  float x;

  // Vertical position
  float y;

  // Radius
  float r;

  // Construct a new hull
  Hull(float x, float y, float r) : x(x), y(y), r(r) { }

  // Do two hulls collide?
  static bool Collide(const Hull a, const Hull b) {

    const float dx = a.x - b.x;
    const float dy = a.y - b.y;
    const float tr = a.r + b.r;

    return dx * dx + dy * dy < tr * tr;
  }
};

typedef Collider::Detector<Hull> Detector;
typedef Collider::Entity<Hull> Entity;
typedef Collider::Resolver<Hull> Resolver;

// Step 1: create a custom implementation of the collision detector,
// which will perform the actual geometry checks to see when collisions
// occur.
//
// This requires simply to overload the required methods of the
// collision detector.
//
// It may be as simple as checking every hull against all others, 
// or as complex as a hierarchy of bounding boxes, or even worse.

class CollisionDetector : public Detector {
private:
  
  std::list<Entity> hulls;

protected:

  void AddHull(Entity& s) {
    hulls.push_back(s);
  }

  void RemoveHull(Entity& s) {
    hulls.remove(s);
  }

  void ResolveAllCollisions() {
    std::list<Entity>::iterator outer, inner;

    for (outer = hulls.begin(); outer != hulls.end(); ++outer) {
      for (inner = outer; inner != hulls.end(); ++inner) {
        if (inner == outer) continue;
        if (!this->CanCollide(*inner,*outer)) continue;
        if (!Hull::Collide(inner->hull, outer->hull)) continue;

        this->Collide(*inner,*outer);
      }
    }
  }
};

// Step 2: define the various data that will be associated to the
// colliding objects. In general, these are the objects that own the
// hulls. In our case, they will be simple strings and integers.

typedef std::string A;
typedef int B;

// Step 3: define the behaviour of various types of collisions 
// statically. This is used to differentiate Player-Enemy, 
// Player-World and other collision types.

class C_AA  {
public:
  void Resolve(const A & a, const A & b) {
    std::cout << "A-A : " << a << " " << b << std::endl;
  }
} aa_callback;

class C_AB {
public:
  void Resolve(const A & a, const B & b) {
    std::cout << "A-B : " << a << " " << b << std::endl;
  }
} ab_callback;

class C_BB {
public:
  void Resolve(const B & a, const B & b) {
    std::cout << "B-B : " << a << " " << b << std::endl;
  }
} bb_callback;

/* ========================================================================
 * MAIN FUNCTION
 * ======================================================================== */

int main(int argc, char** argv) {

  // Step 4: create a collision detector

  CollisionDetector detector;

  // Step 5: define the various groups of objects. In general, they
  // are PlayerBullets, EnemyBullets, Player, Enemy, World... here,
  // we define two: a and b.

  Detector::Group* a = detector.CreateGroup();
  Detector::Group* b = detector.CreateGroup();

  {
    // Step 6: create resolver instances using the aforementioned
    // callbacks. 

    detector.CreateResolver(a,a,aa_callback);
    detector.CreateResolver(a,b,ab_callback);
    detector.CreateResolver(b,b,bb_callback);

    // Step 7: enjoy! Add and remove objects and their associated 
    // hulls to the groups above, and launch collision testing
    // with the appropriate detector method. 
    //
    // The first constraint here is that the Hull must always be
    // an lvalue. This is necessary so that the collision detector
    // can take into account modifications to the hull without
    // requiring a reinsertion every frame. The second constraint
    // is that a given hull may only be used once in a given group.

    Hull h1(0.0,0.0,10.0);
    Hull h2(0.0,2.0,1.0);
    Hull h3(2.0,0.0,1.0);
    Hull h4(2.0,5.0,5.0);
    Hull h5(6.0,6.0,3.0);
    
    a->Add(h1, "1");
    b->Add(h2, 2);
    a->Add(h3, "3");
    b->Add(h4, 4);
    a->Add(h5, "5");

    detector.ResolveAll();
  }
}

[Edited by - ToohrVyk on August 27, 2006 5:08:05 PM]
Ravuya
Ravuya
#3, mostly.
jfclavette
jfclavette
#6 It doesn't matter. Each collided object receives the collision speed, angle and point. It also receives a pointer to the other collision object. (My collidables derive from IPhysicalObject and have amongst other things, a mass and a temperature (they are actually C# properties, and for most objects, they are hard coded so I don't even keep any extra data unless the object's mass or temperature might change)
I teleported home one night; With Ron and Sid and Meg; Ron stole Meggie's heart away; And I got Sydney's leg. <> I'm blogging, emo style
ToohrVyk
ToohrVyk
jfclavette: how do you make the character pick up power-ups he touches, but receive damage from rockets?
jfclavette
jfclavette
Quote:
Original post by ToohrVyk
jfclavette: how do you make the character pick up power-ups he touches, but receive damage from rockets?


I have no powerups, so this is not a problem. Right now, collision, in my system does, mean collision in the "bang bang, we just collided" sense.

I don't know how I'd deal with powerups. I'd probably implement a scriptable hotspot/trigger system and check for the two situations separately (physical collisions versus "I reached a specific point") Then I'd pass a larger-scale object like my player object (or a base class if I wanted enemies to trigger them too) to the script.

Those two cases should take care of everything. Except destroyable powerups. Those would be a pain.
I teleported home one night; With Ron and Sid and Meg; Ron stole Meggie's heart away; And I got Sydney's leg. <> I'm blogging, emo style
ScottC
ScottC
What i've done so far with collision detection, is have a DoCollision function in my base object, and make it virtual so I can define it for each class if I need to, and just give the objects you're checking for collisions pointers to eachother. I also have an enumeration of object types, so I can define how to handle the collision based on what its colliding with. With the object pointer I can get the collision box, collision sphere, or whatever else I may need. You would be best off specifying how every object should handle a collision against every other object, at least in my experience.
ToohrVyk
ToohrVyk
Quote:
Original post by ScottC
You would be best off specifying how every object should handle a collision against every other object, at least in my experience.


Exactly. The question is, how do we specify this?
nilkn
nilkn
I'm basically just doing double-dispatch right now. My game is simple enough that a more elaborate solution such as yours would simply be superfluous.
ToohrVyk
ToohrVyk
Quote:
Original post by nilkn
I'm basically just doing double-dispatch right now.


How?

ScottC
ScottC
Quote:
Original post by ToohrVyk
Quote:
Original post by ScottC
You would be best off specifying how every object should handle a collision against every other object, at least in my experience.


Exactly. The question is, how do we specify this?


Well you should have a collision box, or triangle data (a pointer to, anyway), or whatever you use for collision checking in your base class. What I like to do is check for collision between two objects outside of them, and if I detect a collision, I alert them of a collision:
objectb.Collide(pointer to object a)
objecta.Collide(pointer to object b)

For a game I made,
bool EnemyShip::Collide(GameObject *CollidedWith){	switch (CollidedWith->GetObjType()){		case ENEMYSHIP:			return false;				case SHIP:			SetDelete(true);			return true;		case BULLET:			TakeDamage((Bullet *)CollideWith->GetDamage());		case OTHER:			SetDelete(true);			return true;		default:			return false;	}}
ToohrVyk
ToohrVyk
Quote:
Original post by ScottC
switch (CollidedWith->GetObjType()){


Ok, so you're using an in-house RTTI.

ScottC
ScottC
Quote:
Original post by ToohrVyk
Quote:
Original post by ScottC
switch (CollidedWith->GetObjType()){


Ok, so you're using an in-house RTTI.


I suppose so, i'm going to look into those other methods you posted.

Edit: It seems like all the methods you listed are very similar ways of doing the same thing, such as splitting up RTTI into multiple functions.
nilkn
nilkn
Quote:
Original post by ToohrVyk
Quote:
Original post by nilkn
I'm basically just doing double-dispatch right now.


How?


I based my solution off this Wikipedia article, so I'll just refer you to that for the details.

Basically, object A specifies how it will react when it collides with object B by overloading OnCollision() to take a reference to B. Double-dispatch is then used to resolve which overload is most appropriate.
GroZZleR
GroZZleR
Hand the object a reference to the object its collided with and use the 'is' keyword to expand on any specialised situations.

.NET > *
ToohrVyk
ToohrVyk
Quote:
Original post by GroZZleR
Hand the object a reference to the object its collided with and use the 'is' keyword to expand on any specialised situations.


Ok, so you're using the C# equivalent of dynamic_cast. I'll change my post to reflect this.

load_bitmap_file
load_bitmap_file
Quote:
Original post by GroZZleR
Hand the object a reference to the object its collided with and use the 'is' keyword to expand on any specialised situations.

.NET > *


So basically RTTI then :P
Samith
Samith
That last project I was a part of used an in house RTTI. For myself, I plan on using #3, pretty much.
Richy2k
Richy2k
I was considering going for #1 in my engine, but I am unsure of, well, the overhead of doing so. I can't imagine it being all that bad since my game won't have that many collisions to be honest.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.