java - 在 oop 中实现碰撞检测器的最佳方法

标签 java oop collision-detection

我正在用 Java 制作一个简单的基于物理的游戏,我一直在实现碰撞检测方法。我有几个继承自公共(public)基类形状的类。我将所有可见对象存储在形状类的数组列表中。我为每种可能的物体碰撞创建了几种碰撞检测方法。 当我开始实现这些方法时,我得到了这样的代码:

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);        
}

感觉这不是实现碰撞检测的正确方法,因为每次添加新形状(如三角形或六边形)时我都必须更新此方法以检查所有可能的组合。 我对访问者模式有所了解。但是有没有更好的方法来做到这一点?

最佳答案

如果您不介意将碰撞检测代码放在对象本身中,您可以通过执行以下操作来消除检查的一侧:

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) { ... }
}

这也让您可以在必要时自由地为形状本身中的某些形状组合实现碰撞算法——您甚至可以摆脱碰撞并只制作例如Block.collideWithBall、Block.collideWithBlock 和 Ball.collideWithBlock,适当调用它们,例如:

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) { 
        ...
    }
}

就我个人而言,我更喜欢后者,因为它在相关类中保留了冲突代码。请注意,Block.collidesWithBall 是不必要的,因为可以使用 Ball.collidesWithBlock。

每次添加新形状时,您仍然需要更新上面的代码。如果性能不是问题,您也可以这样做:

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);

请注意:我在这里快速输入了这个,它是为了说明一个概念——可以进行许多改进。例如,一个 map 可以使用两个 Shape 类作为键以提高性能,或者可以为 CollisionAlgorithm 提供通用参数以消除类型转换形状的需要。不过请记住,每次需要执行碰撞测试时,这种方法都需要在算法容器中进行查找。

关于java - 在 oop 中实现碰撞检测器的最佳方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18094089/

相关文章:

oop - 类和子类的类图

ruby - 我是否正确理解了 Ruby 中的对象?

javascript - 球与球弹性碰撞 : ball bounces in an unequal proportion

C++ 碰撞检测在最后一次检查时不起作用?

java - 将 JSONArray 转换为 JSONObject

java - 如何在Java的Web项目中找到用户定义的配置文件(xml)的路径

java - 是否有提供 LDAP 样式解析的独立 Java 库?

java - (Windows) 为什么我在使用最新的 Java 和 JDK 时会收到 UnsupportedClassVersionError 错误?

PHP : Best way to call an object method from another object ?

java - 如何检测碰撞?