Проблеми зі стрибками платформи при зіткненнях AABB


9

Спершу дивіться схему:

Коли мій фізичний двигун AABB вирішує перехрестя, він робить це, знаходячи вісь, де проникнення менше, а потім "виштовхує" сутність на цій осі.

Розглядаючи приклад "стрибки, що рухається ліворуч":

  • Якщо швидкістьX більша за швидкість Y, AABB виштовхує об'єкт на вісь Y, ефективно зупиняючи стрибок (результат: гравець зупиняється в повітрі).
  • Якщо швидкістьX менша за швидкістьY (не показана на схемі), програма працює за призначенням, оскільки AABB виштовхує сутність на осі X.

Як я можу вирішити цю проблему?

Вихідний код:

public void Update()
{
    Position += Velocity;
    Velocity += World.Gravity;

    List<SSSPBody> toCheck = World.SpatialHash.GetNearbyItems(this);

    for (int i = 0; i < toCheck.Count; i++)
    {
        SSSPBody body = toCheck[i];
        body.Test.Color = Color.White;

        if (body != this && body.Static)
        {                   
            float left = (body.CornerMin.X - CornerMax.X);
            float right = (body.CornerMax.X - CornerMin.X);
            float top = (body.CornerMin.Y - CornerMax.Y);
            float bottom = (body.CornerMax.Y - CornerMin.Y);

            if (SSSPUtils.AABBIsOverlapping(this, body))
            {
                body.Test.Color = Color.Yellow;

                Vector2 overlapVector = SSSPUtils.AABBGetOverlapVector(left, right, top, bottom);

                Position += overlapVector;
            }

            if (SSSPUtils.AABBIsCollidingTop(this, body))
            {                      
                if ((Position.X >= body.CornerMin.X && Position.X <= body.CornerMax.X) &&
                    (Position.Y + Height/2f == body.Position.Y - body.Height/2f))
                {
                    body.Test.Color = Color.Red;
                    Velocity = new Vector2(Velocity.X, 0);

                }
            }
        }               
    }
}

public static bool AABBIsOverlapping(SSSPBody mBody1, SSSPBody mBody2)
{
    if(mBody1.CornerMax.X <= mBody2.CornerMin.X || mBody1.CornerMin.X >= mBody2.CornerMax.X)
        return false;
    if (mBody1.CornerMax.Y <= mBody2.CornerMin.Y || mBody1.CornerMin.Y >= mBody2.CornerMax.Y)
        return false;

    return true;
}
public static bool AABBIsColliding(SSSPBody mBody1, SSSPBody mBody2)
{
    if (mBody1.CornerMax.X < mBody2.CornerMin.X || mBody1.CornerMin.X > mBody2.CornerMax.X)
        return false;
    if (mBody1.CornerMax.Y < mBody2.CornerMin.Y || mBody1.CornerMin.Y > mBody2.CornerMax.Y)
        return false;

    return true;
}
public static bool AABBIsCollidingTop(SSSPBody mBody1, SSSPBody mBody2)
{
    if (mBody1.CornerMax.X < mBody2.CornerMin.X || mBody1.CornerMin.X > mBody2.CornerMax.X)
        return false;
    if (mBody1.CornerMax.Y < mBody2.CornerMin.Y || mBody1.CornerMin.Y > mBody2.CornerMax.Y)
        return false;

    if(mBody1.CornerMax.Y == mBody2.CornerMin.Y)
        return true;

    return false;
}
public static Vector2 AABBGetOverlapVector(float mLeft, float mRight, float mTop, float mBottom)
{
    Vector2 result = new Vector2(0, 0);

    if ((mLeft > 0 || mRight < 0) || (mTop > 0 || mBottom < 0))
        return result;

    if (Math.Abs(mLeft) < mRight)
        result.X = mLeft;
    else
        result.X = mRight;

    if (Math.Abs(mTop) < mBottom)
        result.Y = mTop;
    else
        result.Y = mBottom;

    if (Math.Abs(result.X) < Math.Abs(result.Y))
        result.Y = 0;
    else
        result.X = 0;

    return result;
}

Відповіді:


2
  • Перевірте всі свої підтримуючі функції (у другому списку кодів), якщо ви цього ще не зробили.
  • Візуалізуйте або роздрукуйте на стандартному виході те, що роблять ці об’єкти. Переконайтеся, що ви знаєте, що робить кожен код, і що він робить це ідеально.
  • Існує багато розуміння зіткнень http://www.flipcode.com/archives/Theory_Practice-Issue_01_Collision_Detection.shtml .

Я просто подивився на код, який не намагався довести, де це неправильно.

Я подивився на код і ці 2 рядки видалися дивними:

if ((Position.X >= body.CornerMin.X && Position.X <= body.CornerMax.X) &&
(Position.Y + Height/2f == body.Position.Y - body.Height/2f))

Ви перевіряєте інтервал, а потім перевіряєте на рівність? Я можу помилятися (може статися щось округлення), але, здається, це може спричинити неприємності.


0

Важко читати код інших людей, але я думаю, що це один із можливих (суто мозкових) шляхів вирішення, хоча, звичайно, я не в змозі перевірити його:

  1. Перш ніж виявити зіткнення, збережіть швидкість гравців до якоїсь тимчасової змінної.
  2. Після того, як ви виконали відповідь зіткнення, перевірте, чи позиція гравців X чи Y виправлена
  3. Якщо положення X було змінено, вручну скиньте (як своєрідний «скидання безпеки») швидкість Y у гравців до тієї, яку він мав перед відповіддю.

До речі, що відбувається з вашим поточним кодом, коли ваші гравці потрапляють на дах під час стрибків?


Зміна швидкості нічого не вирішить, оскільки швидкість не впливає на реакцію зіткнення. Відповідь просто змінює позицію гравця, залишаючи швидкість незмінною. Коли гравець потрапляє на дах, він пливе деякий час, а потім повертається вниз. Це призначено, оскільки я не встановлюю швидкість 0, коли вона потрапляє на стелю.
Вітторіо Ромео

Що станеться, якщо ви вилучите код, який встановлює один із будь-яких компонентів векторів результатів нульовим методом GetOverlapVector?
TravisG

Суб'єкт виштовхується по діагоналі, іноді він навіть не працює належним чином, в іншому випадку він просто знімає його, як якщо б він був на сітці.
Вітторіо Ромео

Я зараз не можу цього зрозуміти. Спосіб витіснення вашої сутності повинен залежати від його відстані від тіла, з яким воно стикається, але ваш алгоритм це вже робить. Я завтра погляну на це ще раз.
TravisG
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.