0

C++ is the first language I've used at all extensively that uses object-orientation, so I'm still a bit new to the idea. I'm trying to port a game library I was working on from Go (which uses interfaces instead of a true OOP system) to C++.

I have a collision system that uses four types: A point, a bounding, a line, and a polygon. What I'd like to do is have all of these be abstract-able into a "Collider" class and have a function that is able to take two Collider objects and test for a collision. It would look something like this:

bool Collides(Collider obj1, Collider obj2);

Originally I was thinking I could have methods for each collision type that would test for a collision given another type (i.e. methods OnPoint, OnBounding, OnLine, and OnPolygon) and then have "Collider" be a virtual class that requires all these methods, but then I realized that would be impossible in C++ because it would make classes depend on each other for compilation (right?).

I'm a little bit at a loss about what else I can do. Is my design idea a pipe dream?

Jens
  • 9,058
  • 2
  • 26
  • 43
Velovix
  • 527
  • 1
  • 6
  • 19
  • Why do you think that classes will depend on each other? If all of them are the child classes of the abstract class Collider and all of them just override the methods defined in that class, then your Collides method shouldn't worry about the concrete types of the arguments. – Ashalynd Apr 06 '14 at 20:13
  • Do not make the objects in your scene responsible to detect collisions. Either have a scene object detecting these and/or have freestanding functions like `bool collision(const Circle&, const% Rectangle&)` –  Apr 06 '14 at 20:16
  • Okay, so would my best bet for creating some form of the `Collides(Collider obj1, Collider obj2);` function be to overload `Collides` to accept any combination of collision objects? – Velovix Apr 06 '14 at 20:25
  • Surely a collision is two objects sharing the same point in space and time? Or am I missing something? – Ed Heal Apr 11 '14 at 05:20
  • I can't argue with that! If you're confused as to my reasoning for making multiple types of collison objects, I did that to optimize collision detection (ie. It would be a waste of resources to do a ray casting algorithm on a rectangle) I don't mean to say that there isn't a better way to do that, it's just what I came up with. – Velovix Apr 11 '14 at 07:55

1 Answers1

1

What you want is a function which dispatches not only on the first argument, but also on the second, i.e. a double dispatch. This not directly supported in C++, but can be emulated.

class Point;
class Line;

class Collider {
public:
  virtual bool Collides(Collider const& x) const = 0;

protected:
  virtual bool Collides(Point const& p) const = 0;
  virtual bool Collides(Line const& l) const = 0;

  friend class Point;
  friend class Line;
};

class Point: public Collider {
public:
  virtual bool Collides(Collider const& x) const {
    // here, the type of this is now Point
    // now, find out what x is and call the overload on this type
    return x.Collides(*this);
  }

protected:
  virtual bool Collides(Point const& p) const {
    return false;
    // we now know that we are a point and checking vs. another point
  }

  virtual bool Collides(Line const& l) const {
    return false;
    // we now know that we are a point and checking vs. a line
  }
};

class Line: public Collider {
public:
  virtual bool Collides(Collider const& x) const {
    // here, the type of this is now Line 
    return x.Collides(*this);
  }

protected:
  virtual bool Collides(Point const& p) const {
    return false;
    // we now know that we are a line and checking vs. a point
  }

  virtual bool Collides(Line const& l) const {
    return false;
    // we now know that we are a line and checking vs. another line
  }
};

Now, checking two objects will do two runtime dispatches:

Collider* p = new Point();
Collider* l = new Line();

p->Collides(*l);
// first dynamically dispatches on p to call Point::Collides
// in Collides, this (which is p) now has the static Point. 
// now do the second dispatch on l and use *this as the parametet
// to find the overload.

This is e.g. used in the Visitor design pattern. If your set of objects is fixed, but you expect that the operations done on them is going to change or be extended, Visitor is a good choice.

DJSchaffner
  • 562
  • 7
  • 22
Jens
  • 9,058
  • 2
  • 26
  • 43