タイルベースのマップの衝突検出の問題


7

私はタイルベースのマリオクローンに取り組んでいます。

これは、ウォーキングや落下の際にすべて正常に機能します。しかし、プレイヤーが壁の近くにジャンプして空中を右に歩くと、プレイヤーは壁に引っかかってしまいます。プレーヤーがキーを離すと、プレーヤーのスプライトは再び落ちます。

セットアップはかなり簡単で、問題を見つけることができません。マップは、マップブロックを持つ2D配列として構築されます。ブロックは固体でもそうでなくてもかまいません。プレーヤーは固体オブジェクトを移動できません。

ゲームループでは:

  • プレーヤーの位置が更新されます(重力、動きなど)。
  • 衝突の地図を確認してください。Yで衝突が検出されると、プレーヤーの位置が更新され、ブロックの上または下(プレーヤーの方向に応じて)になり、その後、衝突ボックスが新しい位置で更新されます。次に、Xの同じプロセス。
  • コリジョンボックスが新しい場所(空き場所)に更新されます。ボックスが調整され、プレイヤーの下にあるブロックが着地したかどうかを確認するのが少し高くなります。これは、プレイヤーの状態を、スプライトの飛行からアイドル状態に変更するためです。

また、XとYのチェックを入れ替えて、プレーヤーがX線上を移動するようにしました。その後、プレイヤーが移動すると、動きが非常に遅くなります。ボタンを押して離して移動すると、プレイヤーはより速く移動しますが、チャックの中にいます。とても奇抜です。

誰かがエラーを見たり、これのためのより良い衝突アルゴリズムを私に与えることができますか?

更新(コードを更新していません)

xとyのチェック方法を入れ替えて、isonland変数を実装しました。したがって、壁に向かって歩いたりジャンプしたりするときは、完璧に機能します。今だけ、プレイヤーがジャンプすると、マリオは着地したときに戻ってきます。これは、Xチェックメソッドが最初に実行され、マリオの位置を調整するためです。

どうすれば解決できますか?

マップクラス更新メソッド:

public void update(int timeElapsed) {
        //update entities
        for(Entity entity : _mapEntities) {
            entity.update(timeElapsed);
        }

        //update objects
        for(MapObject mapObt : _mapObjects) {
            mapObt.update(timeElapsed);
        }

        //check for collisions
        checkMapCollision();
    }

エンティティ(抽象)更新メソッド:

public void update(int timeElapsed) {
        _velocity = new Vector2d(0.0F, 0.0F);

        //add gravity
        _velocity.y = Map._GRAVITY_PER_SEC * timeElapsed;
    }

マリオ(エンティティを拡張)更新メソッド:

@Override
    public void update(int timeElapsed) {
        super.update(timeElapsed);

        if(_state == STATES.IDLE) {

        } else if(_isMoving) {
            _marioSmallWalk.update(timeElapsed);
        }

        if(_state == STATES.JUMPING) {
            setVelocityY(getVelocity().y + _jumpSpeed);

            _jumpSpeed += _JUMP_DECREASE * timeElapsed;

            //falling?
            if(getVelocity().y > 0) {
                setState(STATES.FALLING);
            }
        } 

        if(_isMoving) {
            double walkSpd = (_WALK_SPEED_SEC * timeElapsed);

            if(getFacing() == FACING.LEFT) {
                walkSpd = -walkSpd;
            }

            setVelocityX(getVelocity().x + walkSpd);
        }

        //falling?
        if(getVelocity().y > (Map._GRAVITY_PER_SEC * timeElapsed) + 1.0F) {
            setState(STATES.FALLING);
        }

        setPosition((int)(getX() + getVelocity().x), (int)(getY() + getVelocity().y));
    }

MapクラスのCheckMapCollisionメソッド:

public void checkMapCollision() {
        //enteties move so check it
        for(Entity entity : _mapEntities) {
            //get the corners
            Rectangle bounds = entity.getBounds();
            Block[] corners = getCornerBlocks(bounds);
            Vector2d dir = entity.getDirection();

            //moving down
            if(dir.y > 0) {
                if(corners[2].isSolid() || corners[3].isSolid()) {
                    Rectangle blkBounds = null;

                    if(corners[2].isSolid()) {
                        blkBounds = corners[2].getBounds();
                    } else {
                        blkBounds = corners[3].getBounds();
                    }

                    entity.setPositionY(blkBounds.y);
                }
            } else {
                if(corners[0].isSolid() || corners[1].isSolid()) {
                    Rectangle blkBounds = null;

                    if(corners[0].isSolid()) {
                        blkBounds = corners[0].getBounds();
                    } else {
                        blkBounds = corners[1].getBounds();
                    }

                    entity.setPositionY(blkBounds.y + blkBounds.height + bounds.height);
                }
            }

            bounds = entity.getBounds();
            corners = getCornerBlocks(bounds);

            //moving to the right
            if(dir.x > 0) {
                if(corners[1].isSolid() || corners[3].isSolid()) {
                    Rectangle blkBounds;

                    if(corners[1].isSolid()) {
                        blkBounds = corners[1].getBounds();
                    } else {
                        blkBounds = corners[3].getBounds();
                    }

                    entity.setPositionX(blkBounds.x - (bounds.width-entity.getCurrentSprite().getOffsetX())-1);
                }
            } else {
                if(corners[0].isSolid() || corners[2].isSolid()) {
                    Rectangle blkBounds;

                    if(corners[0].isSolid()) {
                        blkBounds = corners[0].getBounds();
                    } else {
                        blkBounds = corners[2].getBounds();
                    }

                    entity.setPositionX(blkBounds.x + blkBounds.width + (bounds.width/2));
                }
            }

            bounds = new Rectangle(bounds.x, bounds.y, bounds.width, bounds.height+1);
            corners = getCornerBlocks(bounds);

            //moving down
            if(dir.y > 0) {
                if(corners[2].isSolid() || corners[3].isSolid()) {
                    Rectangle blkBounds = null;

                    if(corners[2].isSolid()) {
                        blkBounds = corners[2].getBounds();
                    } else {
                        blkBounds = corners[3].getBounds();
                    }

                    entity.landed();
                    System.out.println("landed");
                }
            }
        }
    }

衝突を検出した後、あなたはそれに反応していますか?キャラクターはオブジェクトの外に押し戻されておらず、代わりに停止されているようです(ただし、コードを完全に読んでいません)。
ジェームズ

はい、確信しています。entity.setPositionX()またはentity.setPositionY()衝突チェックの後に呼び出されます。ジャンプせずに壁に向かって歩くと、プレイヤーは正しく押し戻されます。
Sven van Zoelen、2011年

回答:


8

スプライトキャラクターは、2つのタイルの交差部分に引っかかります。衝突ルーチンの開始時の状況を示す次の図を参照してください。

壁との衝突

プレイヤーのコントロールにより、マリオは右に移動しました。これにより、スプライトが3つのタイルに配置されます。次に、下向きのYに対して衝突検出を実行します。青い円の点に注意してください。これにより、2番目のブロックとの衝突が検出され、Y座標をそのブロックの上限に設定することで解決が試行され、マリオが中空。

プレーヤーがキーを放してマリオを右に移動するとすぐに、スプライトは壁に移動せず、以前に衝突を検出するポイントはヒットを記録しません。これにより、マリオが地面に倒れるようになります。

この問題の迅速で汚い解決策は、Xの衝突を最初に解決し、プレイヤーが地面にいないときにのみ重力を適用することです。重力を維持すると、歩き回るときに同じ問題が発生します(これは、XとYのチェックを入れ替えた後で既に確認した「トリッピーな」動作です;))。


ありがとう!xチェックとyチェックを入れ替えて、isOnLandチェックを実装しました。そのため、プレイヤーはジャンプして壁に滑り込むことができます。今、別の問題が...彼が土地ときに、xが(床の衝突から)調整し、プレイヤーが壁の中に置かれます取得、プレイヤーは次の壁に立って、ジャンプしたときに、来ている
スヴェン・バンZoelenは、

私はあなたのコメントに基づいて可能なコード構造を想像するのは難しいと思います。ジャンプ後にマリオが床に衝突したときにXを調整する必要があるのはなぜですか?これはYでの調整のみが必要で、Xはそのままにしておくと思います。
ゴースト

1

私は自分が間違っていたことがわかりました。

重要なのは、ポジションを更新するときです。私が間違ったことは、「最初に位置を変更してから、確認して調整する」方法を実行したことで、それを順番に実行して、確認してから更新する必要がありました。

私の以前のコードは次のことをしました:

  • エンティティの位置を速度で更新します。
  • 衝突をチェックし、エンティティの場所を更新します。

これにより、プレーヤーがジャンプした後、更新メソッドでマリオの位置が変更され、衝突ボックスが地面のタイルに配置されるようになりました。次に、衝突メソッドはX位置を左下隅のxに変更し、yはそれを地面タイルの上に移動します。これは、プレイヤーがジャンプから着地するたびに、マリオが数ピクセル戻ることを意味します。

XとYのチェック方法を交換しようとしましたが、問題は他の何か(壁のハグ)に変わります。

今解決策は、私はそれを次のように変更しました:

  • エンティティの速度を更新します。
  • 衝突法
    • 速度でエンティティをXに配置してみてください。失敗した場合は、可能な限り変更してください。
    • エンティティを速度でYに配置してみてください。失敗した場合は、可能な限り変更してください。

Xが最初にチェック/変更され、次に衝突ボックスが新しいエンティティの場所で更新されます。次に、Yがチェック/変更されます。

これは魅力のように機能します:)


いくつか例を挙げていただけますか?あなたが何をしたのか理解するのは難しい
gyozo kudor 2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.