5

Java で単純な物理ベースのゲームを作成していますが、衝突検出方法の実装に行き詰まっています。コマンド基本クラス形状から継承するいくつかのクラスがあります。すべての可視オブジェクトを形状クラスの配列リストに格納しています。オブジェクトの衝突の可能性ごとにいくつかの衝突検出方法を作成しました。メソッドの実装を開始したとき、次のようなコードになりました。

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

三角形や六角形などの新しい形状を追加するたびに、可能なすべての組み合わせをチェックするためにこのメソッドを更新する必要があるため、衝突検出を実装する正しい方法とは思えません。訪問者パターンについて少し知っています。しかし、これを行うより良い方法はありますか?

4

1 に答える 1

3

オブジェクト自体に衝突検出コードを入れても構わない場合は、次のようにしてチェックの片側を削除できます。

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

これにより、必要に応じて、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) { 
        ...
    }
}

個人的には、関連するクラスに衝突コードが含まれているため、後者の方が好きです。Ball.collidesWithBlock を使用できるため、Block.collidesWithBall は不要であることに注意してください。

新しい形状を追加するたびに、上記のコードを更新する必要があります。パフォーマンスが問題にならない場合は、次のようなこともできます。

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

注:ここに簡単に入力したのは、概念を説明するためのものです。多くの改善を行うことができます。たとえば、マップを 2 つの Shape クラスと共にキーとして使用してパフォーマンスを向上させたり、CollisionAlgorithm にジェネリック パラメーターを指定して、Shape をキャストする必要をなくしたりできます。それでも、このアプローチでは、衝突テストを実行する必要があるたびに、アルゴリズム コンテナーでルックアップが必要になることに注意してください。

于 2013-08-07T03:30:35.143 に答える