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;
}
Vittorio Romeo
źródło

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.

użytkownik712092
źródło
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?

TravisG
źródło
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,