Problemas de salto de plataforma con colisiones AABB

9

Vea el diagrama primero:

Cuando mi motor de física AABB resuelve una intersección, lo hace al encontrar el eje donde la penetración es más pequeña y luego "empujar" la entidad en ese eje.

Considerando el ejemplo de "saltar moviéndose a la izquierda":

  • Si la velocidad X es mayor que la velocidad Y, AABB empuja a la entidad hacia el eje Y, deteniendo efectivamente el salto (resultado: el jugador se detiene en el aire).
  • Si velocityX es menor que velocitY (no se muestra en el diagrama), el programa funciona según lo previsto, porque AABB empuja a la entidad hacia el eje X.

¿Como puedó resolver esté problema?

Código fuente:

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
fuente

Respuestas:

2

Acabo de mirar el código que no he intentado probar dónde está mal.

Miré el código y estas 2 líneas parecían extrañas:

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

¿Comprueba el intervalo y luego comprueba la calidad de la UE? Puedo estar equivocado, (puede haber algo de redondeo en curso) pero parece que puede causar un problema.

usuario712092
fuente
0

Es difícil leer el código de otras personas, pero creo que esta es una posible solución (puramente lluvia de ideas), aunque, por supuesto, no puedo probarlo:

  1. Antes de que ocurra la detección de colisión, guarde la velocidad de los jugadores en alguna variable temporal.
  2. Una vez que hayas hecho tu respuesta de colisión, verifica si la posición X o Y de los jugadores ha sido corregida
  3. Si se ha cambiado la posición X, reinicie manualmente (como una especie de "reinicio de seguridad") la velocidad Y de los jugadores a la que tenía antes de la respuesta.

Por cierto, ¿qué sucede con tu código actual cuando tus jugadores golpean el techo mientras saltan?

TravisG
fuente
Cambiar la velocidad no resolvería nada, porque la velocidad no se ve afectada por la respuesta de colisión. La respuesta simplemente cambia la posición del jugador, dejando la velocidad sin cambios. Cuando el jugador golpea el techo, flota por un tiempo y luego vuelve a bajar. Está destinado ya que no estoy configurando la Velocidad a 0 cuando toca el techo.
Vittorio Romeo
¿Qué sucede si elimina el código que establece uno de los componentes de los vectores de resultado en cero en el método GetOverlapVector?
TravisG
La entidad se expulsa diagonalmente, a veces ni siquiera funciona correctamente, otras veces simplemente se rompe como si estuviera en una cuadrícula.
Vittorio Romeo
No puedo entenderlo ahora. La forma en que su entidad es expulsada debe depender de su distancia del cuerpo con el que está colisionando, pero su algoritmo ya lo hace. Lo echaré otro vistazo mañana.
TravisG