0

I'm programming in Java and have the following problem:

I would like to do collision detection. For that, I need different types of BoundingBoxes. For the sake of example, let's say that I have entities that have a circular collision box and entities that have a rectangular bounding box. So I need two classes, BoundingBoxCircle and BoundingBoxRectangle to implement collision detection. Also, I would like to have an abstract class BoundingBox that has the abstract method collidesWith(...), which takes as an argument some kind of BoundingBox and returns whether it is currently colliding with some other BoundingBox.

Now for the issue: I can't find a nice way to structure this programmatically. Essentially, I need to implement different collision detections between rectangles and circles (and potentially other shapes). I would also like to be able to call these methods "nicely". When I have a particular bounding box, say BoundingBoxCircle, I would like to be able to call boundingBoxCircle.collidesWith(rectangle) and have it dynamically dispatched to the method that handles circle-rectangle collision detection. However, this does not work:

public abstract class BoundingBox {
    public abstract boolean collidesWith(BoundingBox b);
}

public class BoundingBoxCircle {
    @Override
    public boolean collidesWith(BoundingBoxRectangle b) {
         ...
    }
}

Because the signature of the overriden method is not the same.

So I can think of two options:

  • Provide all method signatures in the superclass
  • Override collidesWith in all subclasses

The first one has the problem that I would have to provide thecircleCollidesWithRectangle, rectangleCollidesWithCircle (etc.) signatures in the BoundingBox class, which is quite ugly, because I essentially have "information" in BoundingBox which should really be part of its subclasses.

The second one has the problem that I would need to override the collidesWith(BoundingBox b) method in each subclass, which means that I do not know which type b is of - so I would have to check for all cases in every subclass; essentially:

class BoundingBoxRectangle extends BoundingBox {
    @Override
    public boolean collidesWith(BoundingBox b) {
         if (b typeof BoundingBoxRectangle) {
             ...
         } else if (b typeof BoundingBoxCirce) {
             ...
         } else {
              throw new IllegalStateException("unexpected type");
         }
    }
}

...which could lead to an insane amount of if-statements if I start programming more subclasses for BoundingBox. It also has the disadvantage that I might forget to add an if-statement in the future, which are potentially hard to find, because I only throw an exception if the collision is checked for two specific entities.

I would really like to implement this in a way that let's me see missing methods at compile-time and does not require code repetition.

3 Answers 3

5

You can munge double dispatch into a single dispatch language with the visitor pattern. This is basically having an interface like:

bool collidesWith(BoundingBox visitor) {
    return visitor.collidesWithVisit(this);
}

bool collidesWithVisit(BoundingBoxRectangle other) {
    return false;
}

bool collidesWithVisit(BoundingBoxCircle other) {
    return false;
}

The main interface is through the first function, then it will call through to the appropriate implementation below.

2

The usual advice, if you want to model this functionality in Java, is to transform your model until it can be implemented with single dispatch. For instance, you could have a shape-specific Extent property or method that doesn't compute collisions, but only where exactly a shape reaches, and then a shape-agnostic CollisionManager that actually checks for collisions, based on information that it gets from dynamically dispatched getExtent() calls on both objects.

This can mean that you have to change your model quite a bit, but the alternative would be to switch to a language that allows multple dispatch natively - and most people aren't prepared to start using Common Lisp just for that.

1
  • +1 for mentioning my pre-Java favorite language, being Common Lisp (in some aspects like multi-dispatch, still unsurpassed). Nov 9, 2017 at 14:35
0

As I see it, it would be incorrect to have a collidesWith abstract method in BoundingBox since even if you know how your implementation works, it isn't enough information to be able to determine if there is a collision.

You should create a CollisionManager containing a list of CollisionHandler classes like so:

public interface CollisionHandler {
    public boolean canHandle(Class<? extends BoundingBox> class1, Class<? extends BoundingBox> class2);
    public boolean collidesWith(BoundingBox instance1, BoundingBox instance2);
}

public class CollisionManager {
    List<CollisionHandler> handlers= new ArrayList<CollisionHandler>();

    public void add(CollisionHandler handler) {
        handlers.add(handler);
    }

    public boolean collidesWith(BoundingBox instance1, BoundingBox instance2) {
        for(CollisionHandler handler : handlers) {
            if(handler.canHandle(instance1.getClass(), instance2.getClass()) {
                return handler.collidesWith(instance1, instance2);
            }
        }
        // If we got here, we don't know how to handle this case.
        throw new CollisionNotHandledException();
    }
}

From here, you'd need to write a CollisionHandler for each case. Perhaps you could write a CollisionHandler which merely checks that the two classes are the same, and in that case, asks the BoundingBox instance to perform the check on its own:

public class SelfCollisionHandler {
    public boolean canHandle(Class<? extends BoundingBox> class1, Class<? extends BoundingBox> class2) {
        return class1.equals(class2);
    }

    public boolean collidesWith(BoundingBox instance1, BoundingBox instance2) {
        return instance1.collidesWith(instance2);
    }
}

Then each implementation of BoundingBox would be responsible for implementing collision with its own instance. For other cases, you would handle by having a specific CollisionHandler devoted to that particular case such as collision between BoundingBoxRectangle and BoundingBoxCircle.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.