Problemi di salto della piattaforma con collisioni AABB


9

Vedi prima il diagramma:

Quando il mio motore fisico AABB risolve un incrocio, lo fa trovando l'asse in cui la penetrazione è più piccola, quindi "espelle" l'entità su quell'asse.

Considerando l'esempio "saltare muovendosi a sinistra":

  • Se la velocità X è maggiore della velocità Y, AABB spinge l'entità verso l'esterno sull'asse Y, arrestando efficacemente il salto (risultato: il giocatore si ferma a mezz'aria).
  • Se velocityX è più piccolo di velocitY (non mostrato nel diagramma), il programma funziona come previsto, perché AABB spinge l'entità verso l'esterno sull'asse X.

Come posso risolvere questo problema?

Codice sorgente:

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

Risposte:


2

Ho appena guardato il codice che non ho provato a dimostrare dove è sbagliato.

Ho guardato il codice e queste 2 righe sembravano strane:

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

Verifichi l'intervallo e poi controlli l'euqualità? Potrei sbagliarmi, (potrebbe esserci qualcosa di simile) ma sembra che potrebbe causare problemi.


0

È difficile leggere il codice di altre persone, ma penso che questa sia una possibile soluzione (puramente brainstorming), anche se ovviamente non sono in grado di testarlo:

  1. Prima che avvenga il rilevamento delle collisioni, salva la velocità dei giocatori in una variabile temporanea.
  2. Dopo aver effettuato la risposta alla collisione, controlla se la posizione X o Y del giocatore è stata corretta
  3. Se la posizione X è stata cambiata, reimpostare manualmente (come una sorta di "reset di sicurezza") la velocità Y dei giocatori su quella che aveva prima della risposta.

A proposito, cosa succede con il tuo codice attuale quando i tuoi giocatori colpiscono il tetto mentre saltano?


Cambiare la velocità non risolverebbe nulla, perché la velocità non è influenzata dalla risposta alla collisione. La risposta cambia semplicemente la posizione del giocatore, lasciando invariata la velocità. Quando il giocatore colpisce il tetto, galleggia per un po ', poi torna giù. È inteso dal momento che non sto impostando la velocità su 0 quando colpisce il soffitto.
Vittorio Romeo

Cosa succede se si rimuove il codice che imposta uno dei componenti di un vettore di risultato su zero nel metodo GetOverlapVector?
TravisG,

L'entità viene espulsa in diagonale, a volte non funziona nemmeno correttamente, altre volte la scatta come se fosse su una griglia.
Vittorio Romeo,

Non riesco a capirlo in questo momento. Il modo in cui la tua entità viene espulsa dovrebbe dipendere dalla sua distanza dal corpo con cui si scontra, ma il tuo algoritmo lo fa già. Daro 'un'altra occhiata domani.
TravisG,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.