3

境界ボックスがプレイヤーの境界ボックスを最後の位置と次の位置から取得する単純な AABB システムを実装しようとしています。次のバウンディング ボックスは、不正な移動 (壁を通り抜けるなど) を防ぐために変更する必要があります。

現在、私のコードはある程度機能しています。たとえば、一方向のみ (キーボード コントロール) に移動すると、正確に正しいポイントで停止します。

衝突のスクリーンショット

白い四角が実際のバウンディング ボックスで、赤い境界線が排他的な右と下の座標であることに注意してください (したがって、赤い線が地形の内側にあるのは正しいことです)。

2 つの軸で同時に移動しようとすると、実際の問題が発生します。たとえば、一番上の壁を抱きながら ( を押しながらW) 左に移動すると、プレイヤーは通常の 16*16 の間隔で (すべてのタイルの端で) 動けなくなります。これを整理したら、バウンディング ボックスをグループ化する貪欲なアルゴリズムを作成しますが、現在BoundingBox、マップ内の塗りつぶされたタイルごとに 1 つしかint i, j; boxes.add(new BoundingBox(new Vec2(i = x * 16, j = y * 16), new Vec2(i + 16, j + 16)));ありません。GIF で何が起こっているかを示す方が簡単かもしれません。

衝突がスタックすることを説明する GIF

このcollide関数は、境界ボックスごとに呼び出されます。プレーヤーの最後の位置と、最後のボックスから変更された位置が、左上から始まり、X が最初にインクリメントされます。境界ボックスのコードは次のとおりです。

public class BoundingBox {
    public Vec2 min, max;

    public BoundingBox(Vec2 min, Vec2 max) {
        this.min = min;
        this.max = max;
    }

    @Override
    public BoundingBox clone() {
        return new BoundingBox(new Vec2(this.min.x, this.min.y), new Vec2(this.max.x, this.max.y));
    }
    public BoundingBox offset(Vec2 vec) {
        this.min.x += vec.x;
        this.max.x += vec.x;
        this.min.y += vec.y;
        this.max.y += vec.y;
        return this;
    }

    private boolean lineCollision(int leftA, int rightA, int leftB, int rightB) {
        return (leftA >= leftB && leftA < rightB) ||
            (rightA >= leftB && rightA < rightB);
    }

    public boolean collide(BoundingBox last, BoundingBox next) {
        boolean collided = false;

        if(lineCollision(last.min.y, last.max.y, this.min.y, this.max.y) || lineCollision(next.min.y, next.max.y, this.min.y, this.max.y)) {
            if(last.max.x <= this.min.x && next.max.x > this.min.x) { // Left -> Right
                next.offset(new Vec2(this.min.x - next.max.x, 0));
                collided = true;
            }
            if(last.min.x >= this.max.x && next.min.x < this.max.x) { // Right -> Left
                next.offset(new Vec2(this.max.x - next.min.x, 0));
                collided = true;
            }
        }
        if(lineCollision(last.min.x, last.max.x, this.min.x, this.max.x) || lineCollision(next.min.x, next.max.x, this.min.x, this.max.x)) {
            if(last.max.y <= this.min.y && next.max.y > this.min.y) { // Top -> Bottom
                next.offset(new Vec2(0, this.min.y - next.max.y));
                collided = true;
            }
            if(last.min.y >= this.max.y && next.min.y < this.max.y) { // Bottom -> Top
                next.offset(new Vec2(0, this.max.y - next.min.y));
                collided = true;
            }
        }
        return collided;
    }
}

おそらく、この問題は操作の順序と関係がありますか? 可能性は低いと思われますが、if内部の 2 つのステートメントの順序を反転するとcollide、動作が変わるようです。つまり、立ち往生する壁が変更されます。

私が知る限り、最初に衝突が発生したとき、結果のベクトルは壁の外側にあるはずなので、衝突しなくなります。ここには何らかの論理エラーがあるはずですが、自分で見つけることはできません。

リクエストに応じて、このペーストビンで追加のコードを利用できます: http://pastebin.com/ueBAKJ9C

4

0 に答える 0