Problemy z przeskakiwaniem platformy z kolizjami AABB


9

Najpierw zobacz diagram:

Kiedy mój silnik fizyki AABB rozwiązuje przecięcie, robi to, znajdując oś, na której penetracja jest mniejsza, a następnie „wypycha” byt na tej osi.

Biorąc pod uwagę przykład „skaczący w lewo”:

  • Jeśli prędkość X jest większa niż prędkość Y, AABB wypycha byt na oś Y, skutecznie zatrzymując skok (wynik: gracz zatrzymuje się w powietrzu).
  • Jeśli velocityX jest mniejsze niż velocitY (nie pokazano na schemacie), program działa zgodnie z przeznaczeniem, ponieważ AABB wypycha element na oś X.

Jak mogę rozwiązać ten problem?

Kod źródłowy:

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;
}

Odpowiedzi:


2

Właśnie spojrzałem na kod, którego nie próbowałem udowodnić, gdzie jest źle.

Spojrzałem na kod i te 2 wiersze wydawały się dziwne:

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

Sprawdzasz interwał, a potem sprawdzasz równość? Mogę się mylić (może się zdarzyć, że coś się dzieje), ale wydaje się, że może to powodować problemy.


0

Trudno odczytać kod innych ludzi, ale myślę, że jest to jedno z możliwych (czysto burzy mózgów) obejścia, chociaż oczywiście nie jestem w stanie go przetestować:

  1. Zanim nastąpi wykrycie kolizji, zapisz prędkość graczy w jakiejś zmiennej tymczasowej.
  2. Po zakończeniu reakcji na kolizję sprawdź, czy pozycja X lub Y graczy została poprawiona
  3. Jeśli pozycja X została zmieniona, ręcznie zresetuj (jako rodzaj „resetu bezpieczeństwa”) prędkość Y graczy do tej, którą miał przed odpowiedzią.

Nawiasem mówiąc, co dzieje się z twoim obecnym kodem, gdy twoi gracze uderzają w dach podczas skoku?


Zmiana prędkości niczego nie rozwiąże, ponieważ na reakcję na zderzenie nie ma wpływu prędkość. Odpowiedź po prostu zmienia pozycję gracza, pozostawiając prędkość bez zmian. Kiedy gracz uderza w dach, unosi się na chwilę, po czym wraca. Jest to zamierzone, ponieważ nie ustawiam Prędkości na 0, kiedy uderza ona w sufit.
— Vittorio Romeo,

Co się stanie, jeśli usuniesz kod, który ustawia jeden z komponentów wektorów wynikowych na zero w metodzie GetOverlapVector?
— TravisG,

Istota jest wypychana po przekątnej, czasami nawet nie działa poprawnie, innym razem po prostu przyciąga ją, jakby znajdowała się na siatce.
— Vittorio Romeo,

Nie mogę tego teraz zrozumieć. Sposób wypychania twojego bytu powinien zależeć od jego odległości od ciała, z którym się zderza, ale twój algorytm już to robi. Przyjrzę się temu jutro.
— TravisG,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.