1

私は slick2d + Java で衝突検出のアルゴリズムを書いています。これは、私が作成するプラットフォーマー ゲームで最終的に使用します。アルゴリズムがどのように機能するかというと、プレイヤーが四角形にどれだけオーバーラップしているかを検出し、そのオーバーラップによってプレイヤーを四角形の外に移動させます。問題は、アルゴリズムにかなりの数の問題があり、修正方法がわからないことです。まず、プレーヤーが長方形の外に出すぎて、跳ね返っているように見えることがあります。第 2 に、プレーヤーが長方形の内側で小さいながらも目立つ量を移動できる場合があります。最後に、速度を上げると、プレイヤーが四角形を通り抜けることができる場合があります。これは非常に漠然とした質問ですが、何が間違っているのかを理解するために本当に助けが必要です. どんなアイデアでも大歓迎です。

アルゴリズム:

public void Collision(Polygon player, Polygon poly, Vector2f translation){
    Vector2f magnitude = new Vector2f();

    //Find the vectre of each object
    Vector2f p1Centre = new Vector2f(player.getX() + (player.getWidth()/2), player.getY() + (player.getHeight()/2));
    Vector2f p2Centre = new Vector2f(poly.getX() + (poly.getWidth()/2), poly.getY() + (poly.getHeight()/2));

    //Calculate the distance between the two
    Vector2f distance = new Vector2f(p1Centre);
    distance.sub(p2Centre);

    //Get the absolute distance
    Vector2f absDistance = new Vector2f(distance.x<0 ? -distance.x : distance.x, distance.y<0 ? -distance.y : distance.y);

    //Get the combined half widths and heights of each object
    Vector2f halvedBounds = new Vector2f((player.getWidth() + poly.getWidth())/2.0f, (player.getHeight() + poly.getHeight())/2.0f);

    //If the absolute distance is less thate the halved widths heights then there is a collision
    if((absDistance.x < halvedBounds.x) && (absDistance.y < halvedBounds.y)){

        //Set the magnitude vector to the halved bounds minus the absolute distance
        magnitude.x = halvedBounds.x - absDistance.x;
        magnitude.y = halvedBounds.y - absDistance.y;

        //Only react to the lesser overlap;
        if(magnitude.x < magnitude.y){
            magnitude.x = (distance.x > 0) ? magnitude.x : -magnitude.x;
            magnitude.y = 0;
        }
        else{
            magnitude.y = (distance.y > 0) ? magnitude.y : -magnitude.y;
            magnitude.x = 0;
        }

        //Debug
        System.out.println(magnitude.x+"            "+magnitude.y);
        System.out.println(translation.x+"            "+translation.y+"\n");

        //Add the magnitude to the player position
        position.add(magnitude);
    }       
}

完全なソース:

import java.util.ArrayList;

import org.newdawn.slick.AppGameContainer;
import org.newdawn.slick.BasicGame;
import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.geom.Polygon;
import org.newdawn.slick.geom.Vector2f;

public class TestCode extends BasicGame {

    private Vector2f position = new Vector2f(300, 300);

    private ArrayList<Polygon> solids;
    private Polygon player;

    public TestCode(String title) {
        super(title);
    }

    public static void main(String[] args) throws SlickException{
        AppGameContainer game = new AppGameContainer(new TestCode("test"));
        game.setDisplayMode(800, 600, false);
        game.start();
    }

    @Override
    public void render(GameContainer gc, Graphics g) throws SlickException {

        if(gc.isPaused()){
            g.setColor(Color.red);
            g.drawString("Paused", 90, 10);
        }else{
            g.setColor(Color.green);
            g.drawString("Playing", 90, 10);
        }

        g.setColor(Color.red);
        for(Polygon p : solids)
            g.fill(p);

        g.setColor(Color.cyan);
        g.fill(player);

    }

    @Override
    public void init(GameContainer gc) throws SlickException {      
        gc.setVSync(true);

        solids = new ArrayList<Polygon>();

        player = new Polygon(new float[]{
                50, 50,  // upper left point
                70, 50,  // upper right
                70, 90, // lower right
                50, 90   // lower left
        }); 


        for(int i=0, x=200, y=200; i<10; i++, x+=40){
            solids.add(new Polygon(new float[]{
                    x, y,  // upper left point
                    x+40, y,  // upper right
                    x+40, y+40, // lower right
                    x, y+40   // lower left
            }));    
        }

    }

    @Override
    public void update(GameContainer gc, int delta) throws SlickException {

        Input input = gc. getInput();

        Vector2f translation = new Vector2f(0, 0);

        if(input.isKeyDown(Input.KEY_UP))
            translation.y = -1f;
        if(input.isKeyDown(Input.KEY_DOWN))
            translation.y = 1f;
        if(input.isKeyDown(Input.KEY_LEFT))
            translation.x = -1f;
        if(input.isKeyDown(Input.KEY_RIGHT))
            translation.x = 1f;

        translation.normalise();
        translation.x*=2;
        translation.y*=2;

        position.add(translation);

        for(Polygon p : solids)
            Collision(player, p, translation);

        player.setLocation(position);
    }

    public void Collision(Polygon player, Polygon poly, Vector2f translation){
        Vector2f magnitude = new Vector2f();

        //Find the vectre of each object
        Vector2f p1Centre = new Vector2f(player.getX() + (player.getWidth()/2), player.getY() + (player.getHeight()/2));
        Vector2f p2Centre = new Vector2f(poly.getX() + (poly.getWidth()/2), poly.getY() + (poly.getHeight()/2));

        //Calculate the distance between the two
        Vector2f distance = new Vector2f(p1Centre);
        distance.sub(p2Centre);

        //Get the absolute distance
        Vector2f absDistance = new Vector2f(distance.x<0 ? -distance.x : distance.x, distance.y<0 ? -distance.y : distance.y);

        //Get the combined half widths and heights of each object
        Vector2f halvedBounds = new Vector2f((player.getWidth() + poly.getWidth())/2.0f, (player.getHeight() + poly.getHeight())/2.0f);

        //If the absolute distance is less thate the halved widths heights then there is a collision
        if((absDistance.x < halvedBounds.x) && (absDistance.y < halvedBounds.y)){

            //Set the magnitude vector to the halved bounds minus the absolute distance
            magnitude.x = halvedBounds.x - absDistance.x;
            magnitude.y = halvedBounds.y - absDistance.y;

            //Only react to the lesser overlap;
            if(magnitude.x < magnitude.y){
                magnitude.x = (distance.x > 0) ? magnitude.x : -magnitude.x;
                magnitude.y = 0;
            }
            else{
                magnitude.y = (distance.y > 0) ? magnitude.y : -magnitude.y;
                magnitude.x = 0;
            }

            //Debug
            System.out.println(magnitude.x+"            "+magnitude.y);
            System.out.println(translation.x+"            "+translation.y+"\n");

            //Add the magnitude to the player position
            position.add(magnitude);
        }       
    }   
}
4

2 に答える 2

2

したがって、問題を好転させてください。移動後ではなく、移動前に確認してください。プレーヤーが長方形に着地する場合は、許可しないでください。

そして、ここであなたの問題のより重要な部分が来ます:「表面に触れているだけ」の距離です(実際には衝突検出の方法に依存します;バウンディングボックスの衝突を行っている場合、それは計算に変換されます。場合によっては、いくつかの what-if ケースをテストする必要があるかもしれません)。

これは常に発生するため、不必要な作業を避けるようにしてください (なぜピクセルごとにテストするのか、間隔分割がより良いアプローチになる可能性があります。最初にバウンディング ボックスの計算を行い、次にピクセル パーフェクトを行います。「長方形で十分な」オブジェクト バウンディング ボックス ~~ ピクセル pirfect .. .)

プログラミングの世界へようこそ、それは問題を解決することであり、「尖塔をペイントし、x ピクセル移動して 10 に移動」ステートメントを一緒に平手打ちすることではありません ;-)

于 2012-05-24T08:46:20.580 に答える
2

高速でプレーヤーが長方形を通過する問題は、衝突データをサンプリングする頻度に関係しています。

私が本当にバカで、1 秒に 1 回しか衝突をチェックしていないとしましょう。オブジェクトが毎秒 15 メートルで移動していて、その途中に 1 メートルの正方形があるとします。オブジェクトが正方形から 7 メートル離れたときに衝突をチェックし、1 秒後にオブジェクトが正方形を通過したことを完全に見逃してしまいます。

多くの衝突検出ライブラリがこれに対処する方法は、高速で移動するオブジェクトを通常のオブジェクトよりも頻繁にチェックすることです。これが発生したとき、プレーヤーはどれくらい「速く」動いていますか?

于 2012-04-24T21:45:24.783 に答える