Java – the best way to implement collision detector in OOP

I made a simple physics based game in Java, and I insisted on implementing the collision detection method I have several classes that inherit from the common base class shape I store all visible objects in the ArrayList of the shape class I have created several collision detection methods for each possible object collision

private void collision_detector(Shape s1,Shape s2){

    if(s1.getClass()==Ball.class)
        if(s2.getClass() == Block.class) collision_detector((Ball)s1,(Block)s2);
        else collision_detector((Ball)s1,(Ball)s2);
    else if(s1.getClass()==Block.class)
        if(s2.getClass()==Block.class) collision_detector((Block)s1,(Block)s2);
        else collision_detector((Ball)s2,(Block)s1);        
}

It just feels like it's not the right way to implement collision detection, because I have to update this method to check every possible combination every time a new shape (such as triangle or hexagon) is added I know something about visitor patterns But is there a better way to do this?

Solution

If you don't mind putting the collision detection code in the object itself, you can eliminate the check side by doing the following:

public abstract class Shape {
    public abstract boolean collidesWith (Shape s);
}

public class Ball extends Shape {
    @Override public boolean collidesWith (Shape s) {
        if (s instanceof Block)
            return Collision.blockBall((Block)s,this);
        else if (s instanceof Ball)
            return Collision.ballBall(this,(Ball)s);
        else
            return false;
    }
}

public class Block extends Shape {
    @Override public boolean collidesWith (Shape s) {
        if (s instanceof Block)
            return Collision.blockBlock(this,(Block)s);
        else if (s instanceof Ball)
            return Collision.blockBall(this,(Ball)s);
        else
            return false;
    }
}

public class Collision {
    public static boolean blockBlock (Block a,Block b) { ... }
    public static boolean blockBall (Block a,Ball b) { ... }
    public static boolean ballBall (Ball a,Ball b) { ... }
}

If necessary, this also gives you the freedom to implement collision algorithms for some combinations of shapes in shape - you can even get rid of collision blocks collideWithBall,Block. Colladewithblock and ball Colladewithblock, call them appropriately, for example:

public abstract class Shape {
    public abstract boolean collidesWith (Shape s);
}

public class Ball extends Shape {
    @Override public boolean collidesWith (Shape s) {
        if (s instanceof Block)
            return collidesWithBlock((Block)s);
        else if (s instanceof Ball)
            return collidesWithBall((Ball)s);
        else
            return false;
    }
    public boolean collidesWithBall (Ball b) {
        ... 
    }
    public boolean collidesWithBlock (Block b) {
        ...
    }
}

public class Block extends Shape {
    @Override public boolean collidesWith (Shape s) {
        if (s instanceof Block)
            return collidesWithBlock((Block)s);
        else if (s instanceof Ball)
            return ((Ball)s).collidesWithBlock(this);
        else
            return false;
    }
    public boolean collidesWithBlock (Block b) { 
        ...
    }
}

Personally, I prefer the latter because it retains the collision code contained in the related classes Note that block Colladeswithball is not necessary because ball can be used collidesWithBlock.

You still need to update the above code every time you add a new shape If performance is not a problem, you can also do this:

public abstract class CollisionAlgorithm {
    public abstract boolean canCollide (Class<? extends Shape> a,Class<? extends Shape> b);
    public abstract boolean collide (Shape a,Shape b);
}

public class Collider {

    private static final List<CollisionAlgorithm> algorithms;

    public static void registerAlgorithm (CollisionAlgorithm a) { 
        algorithms.append(a); 
    }

    public static CollisionAlgorithm findAlgorithm (Class<? extends Shape> a,Class<? extends Shape> b) {
        for (CollisionAlgorithm algo : algorithms)
            if (algo.canCollide(a,b))
                return algo; 
        return null;
    }

    public static boolean collide (Shape a,Shape b) {
        if (a == null || b == null) 
            return false;
        CollisionAlgorithm algo = findAlgorithm(a.getClass(),b.getClass());
        if (algo != null)
            return algo.collide(a,b);
        algo = findAlgorithm(b.getClass(),a.getClass()); // try swapped order
        if (algo != null)
            return algo.collide(b,a);
        return false;
    }

}

// usage: first register algorithms
Collider.registerAlgorithm(new BallBallAlgorithm());
Collider.registerAlgorithm(new BallBlockAlgorithm());
Collider.registerAlgorithm(new BlockBlockAlgorithm());

// then 
Shape myShape1 = ...;
Shape myShape2 = ...;
boolean collide = Collider.collide(myShape1,myShape2);

Please note: I'm typing here quickly to illustrate a concept - there are many improvements that can be made For example, a map can be used with two shape classes as a key to improving performance, or you can provide common parameters for collisionalgorithm to eliminate the need to convert shapes But remember, this method needs to be found in the algorithm container every time you need to perform a collision test

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>