Plattformsprungprobleme bei AABB-Kollisionen

9

Siehe zuerst das Diagramm:

Wenn meine AABB-Physik-Engine einen Schnittpunkt auflöst, findet sie die Achse, auf der die Penetration kleiner ist, und "drückt" dann die Entität auf dieser Achse heraus.

Betrachtet man das Beispiel "Springen nach links":

  • Wenn VelocityX größer als VelocityY ist, drückt AABB das Objekt auf der Y-Achse heraus und stoppt so den Sprung (Ergebnis: Der Spieler stoppt in der Luft).
  • Wenn VelocityX kleiner als VelocitY ist (im Diagramm nicht dargestellt), funktioniert das Programm wie vorgesehen, da AABB das Objekt auf der X-Achse herausschiebt.

Wie kann ich dieses Problem lösen?

Quellcode:

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
quelle

Antworten:

2

Ich habe mir nur den Code angesehen, den ich nicht zu beweisen versucht habe, wo er falsch ist.

Ich schaute auf den Code und diese 2 Zeilen schienen seltsam:

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

Sie prüfen auf Intervall und dann auf Gleichheit? Ich kann mich irren (es könnte eine Rundung geben), aber es scheint, dass es ein Problem verursachen könnte.

user712092
quelle
0

Es ist schwer, Code von anderen Leuten zu lesen, aber ich denke, dies ist eine mögliche (rein gedankliche) Problemumgehung, obwohl ich ihn natürlich nicht testen kann:

  1. Speichern Sie die Geschwindigkeit des Spielers vor der Kollisionserkennung in einer temporären Variablen.
  2. Überprüfen Sie nach der Kollisionsreaktion, ob die X- oder Y-Position des Spielers korrigiert wurde
  3. Wenn die X-Position geändert wurde, setzen Sie die Y-Geschwindigkeit des Spielers manuell (als eine Art "Sicherheits-Reset") auf die Geschwindigkeit zurück, die er vor der Antwort hatte.

Was passiert übrigens mit Ihrem aktuellen Code, wenn Ihre Spieler beim Springen auf das Dach schlagen?

TravisG
quelle
Das Ändern der Geschwindigkeit würde nichts lösen, da die Geschwindigkeit von der Kollisionsreaktion nicht beeinflusst wird. Die Antwort ändert einfach die Position des Spielers und lässt die Geschwindigkeit unverändert. Wenn der Spieler auf das Dach trifft, schwebt er eine Weile und kommt dann wieder herunter. Es ist beabsichtigt, da ich die Geschwindigkeit nicht auf 0 setze, wenn sie die Decke berührt.
Vittorio Romeo
Was passiert, wenn Sie den Code entfernen, der eine der Ergebnisvektorkomponenten in der GetOverlapVector-Methode auf Null setzt?
TravisG
Die Entität wird diagonal herausgeschoben, manchmal funktioniert sie nicht einmal richtig, manchmal schnappt sie einfach so, als wäre sie in einem Raster.
Vittorio Romeo
Ich kann es momentan nicht verstehen. Die Art und Weise, wie Ihre Entität herausgedrückt wird, sollte von ihrer Entfernung zum Körper abhängen, mit dem sie kollidiert, aber Ihr Algorithmus tut dies bereits. Ich werde es mir morgen noch einmal ansehen.
TravisG