简体   繁体   English

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

[英]best way to implement collision detector in oop

I am making a simple physics based game in java and i am stuck in implementing the collision detection methods. 我在java中制作了一个简单的基于物理的游戏,我坚持实现碰撞检测方法。 I have several classes which inherits from a comman base class shape. 我有几个继承自comman基类形状的类。 I am storing all the visible objects in an arraylist of shape class. 我将所有可见对象存储在shape类的arraylist中。 I have created several collision detection methods for every possible collision of objects. 我已经为每个可能的对象碰撞创建了几种碰撞检测方法。 When i started implementing the methods i ended up with a code like this: 当我开始实现这些方法时,我最终得到了这样的代码:

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 doesn't feel like the right way to implement the collision detection because i have to update this method to check for every possible combination every time i add a new shape like triangle or hexagon. 它只是感觉不是实现碰撞检测的正确方法,因为我必须更新此方法以检查每次添加新形状(如三角形或六边形)时的每种可能组合。 I know a bit about Visitor patterns. 我对访客模式有所了解。 But Is there any better way to do this ? 但有没有更好的方法来做到这一点?

If you don't mind putting collision detection code in the objects themselves, you could eliminate one side of the checks by doing something like: 如果您不介意将碰撞检测代码放在对象本身中,则可以通过执行以下操作来消除检查的一面:

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

That also gives you the freedom to implement collision algorithms for certain combinations of Shapes in the Shape itself if necessary -- you can even get rid of Collision and just make eg Block.collideWithBall, Block.collideWithBlock, and Ball.collideWithBlock, calling those as appropriate, eg: 如果有必要,这也让你可以自由地为Shape中的Shapes的某些组合实现碰撞算法 - 你甚至可以摆脱Collision,只需要制作例如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) { 
        ...
    }
}

Personally, I kind of like the latter better, since it keeps collision code contained in the relevant classes. 就个人而言,我更喜欢后者,因为它保留了相关类中包含的碰撞代码。 Note that Block.collidesWithBall is unnecessary, as Ball.collidesWithBlock can be used. 请注意,Block.collidesWithBall是不必要的,因为可以使用Ball.collidesWithBlock。

You still have to update the above code each time you add a new shape. 每次添加新形状时,您仍然需要更新上面的代码。 If performance is not an issue, you could do something like this as well: 如果性能不是问题,你也可以这样做:

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 typed this here quickly, and it's meant to illustrate a concept -- many improvements can be made. 请注意: 我在这里快速输入,这是为了说明一个概念 - 可以进行许多改进。 For example, a map can be used with the two Shape classes as a key to improve performance, or CollisionAlgorithm can be given generic parameters to eliminate the need for casting Shapes. 例如,地图可以与两个Shape类一起使用作为提高性能的关键,或者可以为CollisionAlgorithm提供通用参数以消除转换形状的需要。 Still, keep in mind, this approach requires a lookup in the algorithm container every time you need to perform a collision test. 但请记住,每次需要执行碰撞测试时,此方法都需要在算法容器中查找。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM