Problemas de salto de plataforma com colisões AABB


9

Veja o diagrama primeiro:

Quando meu mecanismo de física da AABB resolve uma interseção, ele o faz encontrando o eixo em que a penetração é menor e, em seguida, "empurra" a entidade nesse eixo.

Considerando o exemplo "pulando se movendo para a esquerda":

  • Se a velocidadeX for maior que a velocidadeY, o AABB empurra a entidade para fora do eixo Y, interrompendo efetivamente o salto (resultado: o jogador para no ar).
  • Se o velocityX for menor que o velocitY (não mostrado no diagrama), o programa funcionará como pretendido, porque o AABB empurra a entidade para fora do eixo X.

Como posso resolver este problema?

Código fonte:

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

Respostas:


2

Acabei de olhar para o código que não tentei provar onde está errado.

Eu olhei para o código e essas duas linhas pareciam estranhas:

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

Você verifica o intervalo e depois verifica a euqualidade? Posso estar errado (pode haver algum arredondamento), mas parece que isso pode causar um problema.


0

É difícil ler o código de outras pessoas, mas acho que essa é uma solução possível (puramente pensada em brainstorming), embora, é claro, não seja possível testá-lo:

  1. Antes da detecção de colisão, salve a velocidade dos jogadores em alguma variável temporária.
  2. Depois de responder sua colisão, verifique se a posição dos jogadores X ou Y foi corrigida
  3. Se a posição X foi alterada, redefina manualmente (como uma espécie de "redefinição de segurança") a velocidade Y do jogador para a que ele tinha antes da resposta.

A propósito, o que acontece com o seu código atual quando seus jogadores atingem o teto enquanto pulam?


Alterar a velocidade não resolveria nada, porque a velocidade não é afetada pela resposta de colisão. A resposta simplesmente muda a posição do jogador, mantendo a velocidade inalterada. Quando o jogador atinge o teto, ele flutua por um tempo e depois desce. Pretende-se, já que não estou definindo a velocidade como 0 quando atinge o teto.
Vittorio Romeo

O que acontece se você remover o código que define um dos componentes dos vetores de resultado como zero no método GetOverlapVector?
TravisG

A entidade é empurrada na diagonal, às vezes nem funciona corretamente; outras vezes, apenas a encaixa como se estivesse em uma grade.
Vittorio Romeo

Eu não consigo entender nada agora. A maneira como sua entidade é expulsa deve depender da distância do corpo com o qual está colidindo, mas seu algoritmo já faz isso. Vou dar uma olhada novamente amanhã.
TravisG
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.