2D Platformer AABBの衝突問題


51

http://dl.dropbox.com/u/3724424/Programming/Gifs/game6.gif

AABBの衝突解決に問題があります。


最初にX軸を解決し、次にY軸を解決して、AABB交差を解決します。これは、このバグを防ぐために行われます:http : //i.stack.imgur.com/NLg4j.png


現在のメソッドは、オブジェクトがプレーヤーに移動し、プレーヤーを水平方向に押す必要がある場合に正常に機能します。.gifでわかるように、水平スパイクはプレーヤーを正しく押します。


ただし、垂直スパイクがプレーヤーに移動しても、X軸が最初に解決されます。これにより、「スパイクをリフトとして使用する」ことができなくなります。

プレーヤーが垂直スパイクに移動すると(重力の影響を受けて落下します)、最初はX軸にオーバーラップがなかったため、Y軸に押し込まれます。


私が試したのは、このリンクの最初の回答で説明した方法でした:2D長方形オブジェクト衝突検出

ただし、スパイクと移動オブジェクトは、速度ではなく位置を変更することで移動します。Update()メソッドが呼び出されるまで、次の予測位置を計算しません。言うまでもなく、このソリューションも機能しませんでした。:(


上記の両方のケースが意図したとおりに機能するように、AABBの衝突を解決する必要があります。

これは私の現在の衝突ソースコードです:http : //pastebin.com/MiCi3nA1

このバグは最初からずっとエンジンに存在していたので、誰かがこれを調べることができれば本当に感謝しています。これは真剣に衝突コードを見て夜を過ごし、「楽しい部分」に到達してゲームロジックをコーディングするのを妨げています:(


XNA AppHubプラットフォーマーデモと同じ衝突システムを実装しようとしました(ほとんどのものをコピーアンドペーストします)。ただし、私のゲームでは「ジャンプ」バグが発生しますが、AppHubデモでは発生しません。[ジャンプバグ:http : //i.stack.imgur.com/NLg4j.png ]

ジャンプするには、プレーヤーが「onGround」であるかどうかを確認し、Velocity.Yに-5を追加します。

プレーヤーのVelocity.XはVelocity.Yよりも高いため(図の4番目のパネルを参照)、onGroundは本来あるべきではないときにtrueに設定され、プレーヤーを空中でジャンプさせます。

プレーヤーのVelocity.XがVelocity.Yよりも高くなることはないので、AppHubデモではこれは起こらないと思いますが、間違っている可能性があります。

これを解決する前に、まずX軸で解決し、次にY軸で解決しました。しかし、それは私が上で述べたようにスパイクとの衝突を台無しにします。


5
実際には何も問題はありません。私が使用している方法は、最初にX軸を押し、次にY軸を押します。そのため、プレーヤーは垂直スパイクでも水平方向に押されます。「ジャンプの問題」を回避し、プレイヤーを浅い軸(最も貫通力の低い軸)に押し上げる解決策を見つける必要がありますが、見つかりません。
ヴィットリオロミオ

1
プレーヤーが障害物のどの面に触れているかを検出し、それを解決できます
Ioachim

4
@Johnathan Hobbs、質問を読んでください。Veeは自分のコードが何をしているのかを正確に知っていますが、特定の問題を解決する方法を知りません。コードをステップ実行しても、この状況では彼は役に立ちません。
AttackingHobo

4
@Maik @Jonathan:プログラムで何も「うまくいかない」ことはありません。彼は自分のアルゴリズムが望んでいることをどこで、なぜ実行しないのかを正確に理解しています。彼は自分のやりたいことをするためにアルゴリズムを変更する方法を知りません。したがって、この場合、デバッガーは役に立ちません。
BlueRaja-ダニーPflughoeft

1
@AttackingHobo @BlueRaja同意し、コメントを削除しました。それは私がただ何が起こっているかについての結論に飛びついただけでした。あなたがそれを説明しなければならないことと、少なくとも一人を誤解したことを本当に謝ります。正直に言って、次回回答を残す前に、質問を本当に適切に吸収してくれると期待してください。
-doppelgreener

回答:


9

わかりました。XNAAppHubプラットフォーマーデモに「ジャンプ」バグがない理由がわかりました。デモで、衝突タイルを上から下にテストします。「壁」に立ち向かうとき、プレーヤーは複数のタイルを重ねているかもしれません。1つの衝突を解決すると、他の衝突も解決される可能性があるため(ただし、異なる方向に)解決順序が重要です。このonGroundプロパティは、プレーヤーをy軸上に押し上げて衝突が解決された場合にのみ設定されます。以前の解像度がプレーヤーを押し下げた場合や水平に押した場合、この解像度は発生しません。

この行を変更することで、XNAデモの「ジャンプ」バグを再現できました。

for (int y = topTile; y <= bottomTile; ++y)

これに:

for (int y = bottomTile; y >= topTile; --y)

(物理学関連の定数も調整しましたが、これは重要ではありません。)

おそらくbodiesToCheck、ゲームの衝突を解決する前にy軸でソートすると、「ジャンプ」バグが修正されます。XNAデモが行うように、Trevorが示唆しているように、「浅い」侵入軸上​​の衝突を解決することをお勧めます。また、XNAデモプレーヤーの高さは、衝突可能なタイルの2倍であるため、複数の衝突が発生する可能性が高くなります。


これは面白そうに聞こえます...衝突を検出する前にすべてをY座標で並べ替えることですべての問題を解決できると思いますか?キャッチはありますか?
ヴィットリオロミオ

Aaaand「キャッチ」を見つけました。この方法を使用すると、ジャンプのバグが修正されますが、天井に達した後にVelocity.Yを0に設定すると、再び発生します。
ヴィットリオロミオ

@Vee:ループPosition = nextPosition内ですぐに設定しforます。そうしないと、不要な衝突解決(設定onGround)が引き続き発生します。プレイヤーは、天井にぶつかったときに垂直に押し下げられ(決して上げられない)ようにするonGround必要があります。これがXNAデモの方法であり、「天井」バグを再現できません。
レフティウム

pastebin.com/TLrQPeBU-これは私のコードです。「nextPosition」変数を使用せずに、Position自体を実際に操作します。XNAデモと同じように機能するはずですが、Velocity.Yを0に設定する行(行57)のコメントを外しておくと、ジャンプのバグが発生します。私がそれを取り除いた場合、プレイヤーは天井に飛び込むときに浮遊し続けます。これは修正できますか?
ヴィットリオロミオ

@Vee:コードにonGroundは設定方法が表示されないため、ジャンプが誤って許可されている理由を調査できません。XNAデモは、isOnGround内部の類似のプロパティを更新しますHandleCollisions()。また、Velocity.Y0 に設定した後、重力が再びプレーヤーを下に動かし始めないのはなぜですか?私の推測では、onGroundプロパティが不適切に設定されています。XNAデモの更新方法previousBottomisOnGroundIsOnGround)をご覧ください。
レフティウム

9

最も簡単な解決策は、衝突を解決する前に世界のすべてのオブジェクトに対して両方の衝突方向を確認し、2つの結果の「複合衝突」のうち小さい方を解決することです。これは、常に最初にxを解決する代わりに、または常に最初にyを解決するのではなく、可能な限り最小量で解決することを意味します。

コードは次のようになります。

// This loop repeats, until our object has been fully pushed outside of all
// collision objects
while ( StillCollidingWithSomething(object) )
{
  float xDistanceToResolve = XDistanceToMoveToResolveCollisions( object );
  float yDistanceToResolve = YDistanceToMoveToResolveCollisions( object );
  bool xIsColliding = (xDistanceToResolve != 0.f);

  // if we aren't colliding on x (not possible for normal solid collision 
  // shapes, but can happen for unidirectional collision objects, such as 
  // platforms which can be jumped up through, but support the player from 
  // above), or if a correction along y would simply require a smaller move 
  // than one along x, then resolve our collision by moving along y.

  if ( !xIsColliding || fabs( yDistanceToResolve ) < fabs( xDistanceToResolve ) )
  {
    object->Move( 0.f, yDistanceToResolve );
  }
  else // otherwise, resolve the collision by moving along x
  {
    object->Move( xDistanceToResolve, 0.f );
  }
}

大きな修正:他の答えの解説を読んで、私は最終的にこのアプローチが機能しない原因となる無言の仮定に気づいたと思います(そして、すべてではないが人々はこのアプローチで見ました)。詳しく説明するために、これまでに参照した関数が実際に実行することになっていることをより明確に示す、いくつかの擬似コードを示します。

bool StillCollidingWithSomething( MovingObject object )
{
  // loop over every collision object in the world.  (Implementation detail:
  // don't test 'object' against itself!)
  for( int i = 0; i < collisionObjectCount; i++ )
  {
    // if the moving object overlaps any collision object in the world, then
    // it's colliding
    if ( Overlaps( collisionObject[i], object ) )
      return true;
  }
  return false;
}

float XDistanceToMoveToResolveCollisions( MovingObject object )
{
  // check how far we'd have to move left or right to stop colliding with anything
  // return whichever move is smaller
  float moveOutLeft = FindDistanceToEmptySpaceAlongNegativeX(object->GetPosition());
  float moveOutRight = FindDistanceToEmptySpaceAlongX(object->GetPosition());
  float bestMoveOut = min( fabs(moveOutLeft), fabs(moveOutRight) );

  return minimumMove;
}

float FindDistanceToEmptySpaceAlongX( Vector2D position )
{
  Vector2D cursor = position;
  bool colliding = true;
  // until we stop colliding...
  while ( colliding )
  {
    colliding = false;
    // loop over all collision objects...
    for( int i = 0; i < collisionObjectCount; i++ )
    {
      // and if we hit an object...
      if ( Overlaps( collisionObject[i], cursor ) )
      {
        // move outside of the object, and repeat.
        cursor.x = collisionObject[i].rightSide;
        colliding = true;

        // break back to the 'while' loop, to re-test collisions with
        // our new cursor position
        break;
      }
    }
  }
  // return how far we had to move, to reach empty space
  return cursor.x - position.x;  
}

これは「オブジェクトペアごと」のテストではありません。移動するオブジェクトを世界地図の各タイルに対して個別にテストおよび解決しても機能しません(そのアプローチは確実に機能せず、タイルのサイズが小さくなると壊滅的な方法で失敗します)。代わりに、世界地図内のすべてのオブジェクトに対して移動オブジェクトを同時にテストし、世界地図全体との衝突に基づいて解決します。

これにより、(たとえば)壁内の個々の壁タイルが2つの隣接するタイル間でプレーヤーを上下にバウンドさせないようにします。その結果、プレーヤーはそれらの間に存在しないスペースに閉じ込められます。衝突解決距離は常に、その上に別のソリッドタイルがある単一のタイルの境界までではなく、ワールド内の空のスペースまでずっと計算されます。


1
i.stack.imgur.com/NLg4j.png-上記の方法を使用した場合に発生します。
ヴィットリオロミオ

1
コードを正しく理解していないかもしれませんが、ダイアグラムで問題を説明しましょう。プレーヤーはX軸上で高速であるため、X侵入はY侵入よりも大きいです。コリジョンはY軸を選択し(貫通力が小さいため)、プレイヤーを垂直に押します。これは、プレーヤーの速度が十分に遅く、Y侵入が常にX侵入よりも大きい場合には発生しません。
ヴィットリオロミオ

1
@Veeここでは、このアプローチを使用して不正な動作を与えると、ダイアグラムのどのペインを参照していますか?私はペイン5を想定しています。このペインでは、プレーヤーはXよりもYよりも明らかに貫通しているため、X軸に沿って押し出されます。これは正しい動作です。実際の浸透に基づいてではなく、速度に基づいて解像度の軸を選択している場合にのみ、悪い動作が発生します(私が提案したように)。
トレバーパウエル

1
@Trevor:ああ、あなたの言っていることがわかります。その場合、あなたは単にそれを作ることができますif(fabs( yDistanceToResolve ) < fabs( xDistanceToResolve ) || xDistanceToResolve == 0) { object->Move( 0.f, yDistanceToResolve ); } else { object->Move( xDistanceToResolve, 0.f ); }
BlueRaja-ダニーPflughoeft

1
@BlueRaja素晴らしい点。あなたが提案するように、より少ない条件を使用するように回答を編集しました。ありがとう!
トレバーパウエル

6

編集

私はそれ改善しました

変更した重要な点は、交差点を分類することです。

交差点は次のいずれかです。

  • 天井バンプ
  • 地面に当たる(床から押し出す)
  • 空中壁の衝突
  • 接地壁の衝突

そして、私はその順序でそれらを解決します

プレイヤーがタイルの少なくとも1/4の方向にいるように地面の衝突を定義する

これは地上衝突であり、プレイヤー()はタイル()の 上に座ります地上衝突

しかし、これは地面の衝突ではなく、プレイヤーはタイルに着地したときにタイルの右側で「スリップ」します 地面衝突プレーヤーにならないように気をつけてください

この方法により、プレイヤーは壁の側面に引っかかることがなくなります。


私はあなたの方法を試しました(私の実装を参照してください:pastebin.com/HarnNnnp)が、プレーヤーのベロシティを0に設定する場所がわかりません。壁に沿ってスライドしながら、また彼は繰り返し壁ジャンプすることができます。
ヴィットリオロミオ

ただし、これはプレーヤーがスパイクと相互作用するときに正しく機能します(.gifを参照)。壁跳び(壁に引っかかっている)の問題の解決にご協力いただければ幸いです。私の壁はいくつかのAABBタイルでできていることに注意してください。
ヴィットリオロミオ

別のコメントを投稿してすみませんが、真新しいプロジェクトにコードをコピーアンドペーストし、spを5に変更し、タイルを壁の形でスポーンさせると、ジャンプバグが発生します(この図を参照:i.stack.imgur.com /NLg4j.png)
ヴィットリオ・ロメオ

OK、今すぐ試してください。
ボボボボ

動作中のコードサンプルの+1(ただし、水平方向に移動する「スパイク」ブロックがありません:)
レフティウム

3

注:
私のエンジンは、オブジェクトが移動するときのみ、および現在占有しているグリッドスクエアでのみ、すべてのオブジェクトとの衝突をリアルタイムでチェックする衝突検出クラスを使用します。

私のソリューション:

2Dプラットフォーマーでこのような問題に直面したとき、次のようなことをしました。

-(エンジンは衝突の可能性をすばやく検出します)
-(ゲームは特定のオブジェクトの正確な衝突をチェックするようエンジンに通知します)[オブジェクトのベクトルを返します*]
-(オブジェクトは他のオブジェクトの前の位置と比較した貫通の深さ、および前の位置を見て、どちら側からスライドするかを決定し
ます)-(オブジェクトが移動し、オブジェクトの速度とオブジェクトの速度を平均します(オブジェクトが移動している場合))


「(オブジェクトは、他のオブジェクトの前の位置と相対的な前の位置だけでなく、浸透の深さを見て、どちらの側からスライドするかを決定します)」これは、私が興味を持っている部分です。.gifと図で示される状況で成功しますか?
ヴィットリオロミオ

この方法が機能しない状況(高速以外)をまだ見つけていません。
ultifinitus11年

良さそうに聞こえますが、浸透を解決する方法を実際に説明できますか?常に最小の軸で解決しますか?衝突の側面を確認しますか?
ヴィットリオロミオ

いいえ、実際のところ、小さい軸は適切な(プライマリ)方法ではありません。私見では。私は通常、どちらの側が以前に他のオブジェクトの側の外側にあったかに基づいて解決しますが、それは最も一般的です。それが失敗したら、速度と深さに基づいてチェックします。
ultifinitus

2

私がプログラムしたビデオゲームでは、アプローチはあなたの位置が有効かどうかを伝える機能を持つことでした。bool ValidPos(int x、int y、int wi、int he);

この関数は、ゲーム内のすべてのジオメトリに対して境界ボックス(x、y、wi、he)をチェックし、交差がある場合はfalseを返します。

移動したい場合は、右に言ってプレーヤーの位置を取り、xに4を加えて、位置が有効かどうかを確認します。有効でない場合は、有効になるまで+ 3、+ 2などで確認します。

重力も必要な場合は、地面に当たらない限り成長する変数が必要です(地面を打つ:ValidPos(x、y + 1、wi、he)== true、ここでyは正の値です)。その距離を移動できる場合(つまり、ValidPos(x、y + gravity、wi、he)はtrueを返します)、落下しているときに、落下時にキャラクターを制御できない場合に便利です。

さて、あなたの問題は、あなたの世界に移動するオブジェクトがあるので、まずあなたの古い位置がまだ有効かどうかを確認する必要があるということです!

そうでない場合は、ある位置を見つける必要があります。ゲーム内のオブジェクトがゲーム1回転あたり2ピクセルを超える速度で移動できない場合は、位置(x、y-1)が有効かどうかを確認し、次に(x、y-2)、次に(x + 1、y)などを確認する必要がありますなど。(x-2、y-2)から(x + 2、y + 2)までのスペース全体をチェックする必要があります。有効な位置がない場合は、「押しつぶされた」ことを意味します。

HTH

バルモンド


私は、ゲームメーカーの時代にこのアプローチを使用していました。より良い検索(たとえば、移動距離とジオメトリに応じて、移動した空間でのバイナリ検索など)で高速化/改善する方法を考えることができますが、このアプローチの主な欠点はつまり、起こり得るすべての衝突に対して1つのチェックを行うのではなく、位置の変更がチェックである限り実行しているということです。
ジェフ

「あなたのポジションの変更は何でもチェックです」申し訳ありませんが、どういう意味ですか?ところで、もちろん、スペースパーティショニングテクニック(BSP、Octree、クアッドツリー、タイルマップなど)を使用してゲームをスピードアップしますが、とにかく(マップが大きい場合)常にそれを行う必要がありますが、問題はそれについてではありませんが、プレーヤーを(正しく)移動するために使用されるアルゴリズムについて。
ヴァルモンド

@Valmond @Jeffは、プレイヤーが左に10ピクセル移動している場合、最大10の異なる衝突検出ができることを意味すると思います。
ジョナサンコネル

それが彼が意味するものであるなら、彼は正しいです、そして、それはそうでなければなりません(そうでなければ、+ 7ではなく+6で止まるかどうか誰がわかることができますか?)コンピューターは高速で、私はこれをS40(〜200mhzとそれほど高速ではないkvm)で使用しました。いつでも最初のアルゴリズムを最適化することができますが、これにより、OPのようなコーナーケースが常に得られます。
ヴァルモンド

それはまさに私が意味したものです
ジェフ

2

これに答え始める前にいくつか質問があります。最初に、壁に引っかかった元のバグでは、それらのタイルは、1つの大きなタイルではなく、個々のタイルの左側にありましたか?そして、彼らがいた場合、プレイヤーは彼らの間に立ち往生していましたか?両方の質問に「はい」の場合新しい役職が有効であることを確認してください。つまり、プレーヤーに移動するよう指示した場所に衝突があるかどうかを確認する必要があります。したがって、以下で説明するように最小変位を解決し、それが可能な場合にのみそれに基づいてプレーヤーを移動しますそこに移動します。鼻の下でもほとんど:Pこれにより、実際には別のバグが発生します。これを「コーナーケース」と呼びます。本質的にコーナー(.gifで水平スパイクが出てくる左下のようなスパイクですが、スパイクがなかった場合)は衝突を解決しません。生成する解像度はどれも有効な位置につながらないと考えられるためです。 。これを解決するには、衝突が解決されたかどうかと、すべての最小侵入解像度のリストを単純に保持します。その後、衝突が解決されていない場合は、生成したすべての解像度をループし、最大Xおよび最大Y解像度を追跡します(同じ解像度から最大値を取得する必要はありません)。次に、それらの最大値で衝突を解決します。これは、私が遭遇した問題だけでなく、すべての問題を解決するようです。

    List<Vector2> collisions = new List<Vector2>();
        bool resolved = false;
        foreach (Platform p in testPlat)
        {
            Vector2 dif = p.resolveCollision(player.getCollisionMask());

            RectangleF newPos = player.getCollisionMask();

            newPos.X -= dif.X;
            newPos.Y -= dif.Y;


            if (!PlatformCollision(newPos)) //This checks if there's a collision (ie if we're entering an invalid space)
            {
                if (dif.X != 0)
                    player.velocity.X = 0; //Do whatever you want here, I like to stop my player on collisions
                if (dif.Y != 0)
                    player.velocity.Y = 0;

                player.MoveY(-dif.Y);
                player.MoveX(-dif.X);
                resolved = true;
            }
            collisions.Add(dif);
        }

        if (!resolved)
        {
            Vector2 max = Vector2.Zero;

            foreach (Vector2 v in collisions)
            {
                if (Math.Abs(v.X) > Math.Abs(max.X))
                {
                    max.X = v.X;
                }
                if (Math.Abs(v.Y) > Math.Abs(max.Y))
                {
                    max.Y = v.Y;
                }
            }

            player.MoveY(-max.Y);

            if (max.Y != 0)
                player.velocity.Y = 0;
            player.MoveX(-max.X);

            if (max.X != 0)
                player.velocity.X = 0;
        }

もう1つの質問は、表示するスパイクが1つのタイルですか、それとも個々のタイルですか?それらが個々の薄いタイルである場合、水平方向のものと垂直方向のものに対して、以下で説明するものとは異なるアプローチを使用する必要があります。しかし、それらがタイル全体である場合、これは機能するはずです。


さて、基本的にこれは@Trevor Powellが説明したことです。AABBのみを使用しているため、必要なのは、1つの長方形が他の長方形をどれだけ貫通しているかを見つけることだけです。これにより、X軸とY軸に量が与えられます。2つの最小値を選択し、その量に沿って衝突オブジェクトをその軸に沿って移動します。AABBの衝突を解決するために必要なのはそれだけです。このような衝突では複数の軸に沿って移動する必要は決してないので、最初に移動するものについて混乱しないでください。

Metanetソフトウェアには、ここでのアプローチに関する古典的なチュートリアルがあります。他の形にもなります。

2つの長方形のオーバーラップベクトルを見つけるために作成したXNA関数を次に示します。

    public Point resolveCollision(Rectangle otherRect)
    {
        if (!isCollision(otherRect))
        {
            return Point.Zero;
        }

        int minOtherX = otherRect.X;
        int maxOtherX = otherRect.X + otherRect.Width;

        int minMyX = collisionMask.X;
        int maxMyX = collisionMask.X + collisionMask.Width;

        int minOtherY = otherRect.Y;
        int maxOtherY = otherRect.Y + otherRect.Height;

        int minMyY = collisionMask.Y;
        int maxMyY = collisionMask.Y + collisionMask.Height;

        int dx, dy;

        if (maxOtherX - minMyX < maxMyX - minOtherX)
        {
            dx = (maxOtherX - minMyX);
        }
        else
        {
            dx = -(maxMyX - minOtherX);
        }

        if (maxOtherY - minMyY < maxMyY - minOtherY)
        {
            dy = (maxOtherY - minMyY);
        }
        else
        {
            dy = -(maxMyY - minOtherY);
        }

        if (Math.Abs(dx) < Math.Abs(dy))
            return new Point(dx, 0);
        else
            return new Point(0, dy);
    }

(それを実装するより良い方法があると確信しているので、従うことが簡単であることを願っています...)

isCollision(Rectangle)は、文字通りXNAのRectangle.Intersects(Rectangle)の単なる呼び出しです。

移動プラットフォームでこれをテストしましたが、うまくいくようです。あなたの.gifに似たテストをいくつか行い、確認して、機能しない場合は報告します。



私は、薄いプラットフォームで動作しないというコメントに対して懐疑的です。そうしない理由は考えられません。また、ソースが必要な場合は、XNAプロジェクトをアップロードできます
ジェフ

うん、これをもう少しテストしてみたところ、壁に引っかかったという同じ問題に戻った。これは、2つのタイルが互いに並ぶ場所で、下のタイルのために上に移動し、上のタイルでは外に移動するように指示するためです(あなたの場合は右)。 。これに対する解決策を検討する必要があります。以前にこの問題があったことを覚えているようです。
ジェフ

さて、あなたの最初の問題を回避するための非常にハックな方法を思いつきました。解決策は、各AABBの最小侵入を見つけ、最大Xを持つもののみを解決し、次に最大Yのみを解決する2回目のパスを見つけることです。水平方向に、元の貼り付きの問題はなくなりました。それはハッキーであり、他の形にどのように変換されるのか分かりません。別の可能性は、接触するジオメトリを溶接することかもしれませんが、私はそれを試みさえしませんでした
ジェフ

壁は16x16タイルでできています。スパイクは16x16タイルです。最小軸で解決すると、ジャンプのバグが発生します(図を見てください)。「非常にハッキング」されたソリューションは、.gifに示されている状況で動作しますか?
ヴィットリオロミオ

うん、私の中で働いています。キャラクターのサイズは?
ジェフ

1

それは簡単です。

スパイクを書き換えます。それはスパイクのせいです。

衝突は、個別の具体的な単位で発生する必要があります。私が見る限り問題は次のとおりです。

1. Player is outside spikes
2. Spikes move into intersection position with the player
3. Player resolves incorrectly

問題は3ではなくステップ2にあります!!

物事をしっかりさせようとしている場合、アイテムをそのように相互にスライドさせないでください。交差点に着いたら、場所を失うと、問題の解決が難しくなります。

スパイクは、プレイヤーの存在を理想的に確認し、移動するときに、必要に応じてプレイヤーをプッシュする必要があります。

プレイヤーが持っているため、これを達成するための簡単な方法があるmoveXmoveY景観を理解した機能を、一定のデルタによってプレーヤーを突き出すます障害物を押すことなく限り、彼らはできる限りか

通常、それらはイベントループによって呼び出されます。ただし、プレーヤーをプッシュするためにオブジェクトから呼び出すこともできます。

function spikes going up:

    move spikes up

    if intersecting player:
        d = player bottom edge + spikes top edge

        player.moveY(-d)
    end if

end function

明らかにプレーヤーはスパイク突入した場合、スパイクに反応する必要があります

包括的なルールは、オブジェクトが移動した後、交差しない位置で終了することです。この方法では、あなたが見ているグリッチを取得することは不可能です。


moveY交差点の除去に失敗する極端な場合(別の壁が邪魔になるため)、交差点を再度確認し、moveXを試すか、単に彼を殺すことができます。
クリスバートブラウン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.