Проблемы с прыжками с платформы при столкновениях AABB

9

Сначала смотрите схему:

Когда мой физический движок AABB разрешает пересечение, он делает это путем нахождения оси, где проникновение меньше, а затем «выталкивает» объект на этой оси.

Рассмотрим пример «прыжок влево»:

  • Если скорость X больше скорости Y, AABB выталкивает объект по оси Y, эффективно останавливая прыжок (результат: игрок останавливается в воздухе).
  • Если скорость X меньше, чем скорость (не показано на диаграмме), программа работает, как предполагалось, потому что 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))

Вы проверяете интервал, а затем проверяете на равенство? Возможно, я ошибаюсь (может происходить округление сома), но это может вызвать проблемы.

user712092
источник
0

Трудно читать код других людей, но я думаю, что это один из возможных (чисто мозговых) решений, хотя, конечно, я не могу его протестировать:

  1. Прежде чем обнаружится столкновение, сохраните скорость игрока в некоторой временной переменной.
  2. После того, как вы ответили на столкновение, проверьте, исправлена ​​ли позиция X или Y игроков.
  3. Если позиция X была изменена, вручную сбросьте (как своего рода «безопасный сброс») скорость Y игроков до той, которая была у него до ответа.

Кстати, что происходит с вашим текущим кодом, когда ваши игроки попадают на крышу во время прыжков?

TravisG
источник
Изменение скорости ничего не решит, потому что скорость не зависит от реакции на столкновение. Ответ просто меняет позицию игрока, оставляя скорость без изменений. Когда игрок попадает на крышу, он некоторое время плавает, а затем возвращается обратно. Это предназначено, так как я не устанавливаю Скорость на 0, когда он достигает потолка.
Витторио Ромео
Что произойдет, если вы удалите код, который устанавливает один из компонентов векторов результата в ноль в методе GetOverlapVector?
TravisG
Сущность выталкивается по диагонали, иногда она даже не работает должным образом, в другой раз она просто щелкает, как если бы она была в сетке.
Витторио Ромео
Я не могу понять это прямо сейчас. Способ выталкивания вашей сущности должен зависеть от ее расстояния от тела, с которым она сталкивается, но ваш алгоритм уже делает это. Я еще раз посмотрю на это завтра.
TravisG