Manuseio de colisão de plataformas 2D

8

Estou tentando criar um jogo de plataformas 2D (tipo Mario) e estou tendo alguns problemas ao lidar com colisões corretamente. Estou escrevendo este jogo em C ++, usando SDL para entrada, carregamento de imagens, carregamento de fontes, etc. Também estou usando o OpenGL através da biblioteca FreeGLUT em conjunto com o SDL para exibir gráficos.

Meu método de detecção de colisão é o AABB (Axis-Aligned Bounding Box), que é realmente tudo o que preciso para começar. O que eu preciso é de uma maneira fácil de detectar em que lado a colisão ocorreu e lidar adequadamente com as colisões. Então, basicamente, se o jogador colidir com o topo da plataforma, reposicione-o no topo; se houver colisão com os lados, reposicione o jogador de volta ao lado do objeto; se houver uma colisão no fundo, reposicione o player embaixo da plataforma.

Eu tentei muitas maneiras diferentes de fazer isso, como tentar encontrar a profundidade da penetração e reposicionar o player para trás pela profundidade da penetração. Infelizmente, nada do que tentei parece funcionar corretamente. O movimento do jogador acaba sendo muito problemático e reposiciona o jogador quando eu não quero. Parte do motivo é provavelmente porque sinto que isso é algo tão simples, mas estou pensando demais.

Se alguém achar que pode ajudar, dê uma olhada no código abaixo e me ajude a tentar melhorar isso, se puder. Eu gostaria de não usar uma biblioteca para lidar com isso (como quero aprender por conta própria) ou algo como o SAT (Teorema do Eixo Separador), se possível. Agradeço antecipadamente por sua ajuda!

void world1Level1CollisionDetection()
{
for(int i; i < blocks; i++)
{
    if (de2dCheckCollision(ball,block[i],0.0f,0.0f)==true)
    {
        de2dObj ballPrev;
        ballPrev.coords[0] = ball.coords[0];
        ballPrev.coords[1] = ball.coords[1];
        ballPrev.coords[2] = ball.coords[2];
        ballPrev.coords[3] = ball.coords[3];
        ballPrev.coords[0] -= ball.xspeed;
        ballPrev.coords[1] -= ball.yspeed;
        ballPrev.coords[2] -= ball.xspeed;
        ballPrev.coords[3] -= ball.yspeed;

        int up = 0;
        int left = 0;
        int right = 0;
        int down = 0;

        if (ballPrev.coords[0] < block[i].coords[0] && ballPrev.coords[2] < block[i].coords[0] && (((ball.coords[1] < block[i].coords[1]) || (ball.coords[3] < ball.coords[1]))  || ((ball.coords[1] < block[i].coords[3]) || ball.coords[3] < block[i].coords[3])))
        {
            left = 1;
        }

       if (ballPrev.coords[0] > block[i].coords[2] && ballPrev.coords[2] > block[i].coords[2] && (((ball.coords[1] < block[i].coords[1]) || (ball.coords[3] < ball.coords[1]))  || ((ball.coords[1] < block[i].coords[3]) || (ball.coords[3] < block[i].coords[3]))))
        {
            right = 1;
        }
        if(ballPrev.coords[1] < block[i].coords[1] && block[i].coords[1] < ballPrev.coords[3] && ballPrev.coords[3] < block[i].coords[3])
        {
            up = 1;
        }
        if(block[i].coords[1] < ballPrev.coords[1] && ball
        {
            down = 1;
        }

        cout << left << ", " << right << ", " << up << ", " << down << ", " << endl;

        if (left == 1)
        {
            ball.coords[0] = block[i].coords[0] - 18.0f;
            ball.coords[2] = block[i].coords[0] - 2.0f;
        }
        else if (right == 1)
        {
            ball.coords[0] = block[i].coords[2] + 2.0f;
            ball.coords[2] = block[i].coords[2] + 18.0f;
        }
        else if (down == 1)
        {
            ball.coords[1] = block[i].coords[3] + 4.0f;
            ball.coords[3] = block[i].coords[3] + 20.0f;
        }
        else if (up == 1)
        {
            ball.yspeed = 0.0f;
            ball.gravity = 0.0f;
            ball.coords[1] = block[i].coords[1] - 17.0f;
            ball.coords[3] = block[i].coords[1] - 1.0f;
        }
    }
    if (de2dCheckCollision(ball,block[i],0.0f,0.0f)==false)
    {
        ball.gravity = -0.5f;
    }
}
}

Para explicar o que parte desse código significa:

A variável de blocos é basicamente um número inteiro que está armazenando a quantidade de blocos ou plataformas. Estou verificando todos os blocos usando um loop for e o número em que o loop está atualmente é representado pelo número inteiro i. O sistema de coordenadas pode parecer um pouco estranho, então vale a pena explicar. as cordas [0] representam a posição x (esquerda) do objeto (onde começa no eixo x). as cordas [1] representam a posição y (em cima) do objeto (onde começa no eixo y). as cordas [2] representam a largura do objeto mais as cordas [0] (direita). as cordas [3] representam a altura do objeto mais as cordas [1] (embaixo). de2dCheckCollision executa uma detecção de colisão AABB. Up é negativo y e down é positivo y, como na maioria dos jogos.

Espero ter fornecido informações suficientes para alguém me ajudar com sucesso. Se houver algo que deixei de fora que possa ser crucial, informe-me e fornecerei as informações necessárias. Finalmente, para quem puder ajudar, fornecer código seria muito útil e muito apreciado.

Obrigado novamente por sua ajuda!

Edit : Eu atualizei meu código com um novo algoritmo que verifica onde a bola estava anteriormente antes da colisão. Os estojos de canto funcionam nessa plataforma única corretamente agora, mas quando tenho uma parede de objetos, consigo deslizar contra ela, mas se me mover em direção à parede enquanto deslizo, passo por ela e agora estou essencialmente em cima de uma bloco dentro da parede. Além disso, há um efeito de tremor que acontece quando estou no chão, onde a bola está constantemente subindo e descendo.

zona de defesa
fonte
Você resolveu seu jitter? Caso contrário, provavelmente é um problema em que você está movendo seu personagem muito longe, em vez de TheirPos-ourSize +- 1remover o 1, se é isso que você está fazendo. Se você não é apenas certo como obter esse algoritmo descobri que eu seria mais do que dispostos a ajudar, o meu é perfeito no momento =] (também que ordem você está verificando os objetos?)
ultifinitus

Respostas:

6

Se entendi seu código corretamente, você compensa a colisão deslocando a bola em um valor fixo sem afetar sua velocidade. A razão pela qual sua bola age agitada é que, embora sua posição seja corrigida com base na colisão, sua velocidade ainda está voltada para o objeto em colisão. Devido a essa velocidade, a bola ainda está se movendo em direção ao bloco que colide e colidirá após alguns quadros deslocando-o novamente.

Talvez a solução mais simples para isso seja garantir que as velocidades sejam redefinidas após a colisão, garantindo que a bola não se mova em direção ao bloco após ser deslocada.

fantasma
fonte
5

Você não mostra nenhum código que lide com o movimento / velocidade da bola. Se você não parar / quicar a bola, no próximo quadro sua bola voltará para o bloco e será "movida" para trás, de modo que provavelmente ficará "presa" na lateral do bloco.

Se não for esse o caso, se você puder especificar mais especificamente os problemas e / ou publicar mais código, alguém poderá ajudar melhor :-)

Danny Tuppeny
fonte
Se ajudar, atualizei o código e adicionei um novo algoritmo que agora verifica onde a bola estava antes da colisão. O único problema restante é que há um pequeno efeito de tremor que acontece quando estou no chão, onde a bola está constantemente subindo e descendo como se estivesse sendo puxada pela gravidade e depois cai novamente no objeto.
defender-zone
@ zona defensora, você não quer colocar a bola de volta na posição anterior. Em vez disso, calcule a sobreposição da bola e o obstáculo quando ocorrer uma colisão e afaste a bola do obstáculo pela quantidade calculada (sobreposição).
bummzack
4

Bem, você sempre pode fazer o que fiz, por pior que tenha sido implementado da minha parte, tenho um método simples na minha classe "being" chamada update_prev_pos (), que faz exatamente isso, ele armazena sua posição atual em uma estrutura para examinar mais tarde. Na minha função de resolução de colisão, observo a posição do lado em questão. Se o lado estava anteriormente fora do objeto, esse lado deve ser corrigido.

Estou usando um método de duas passagens para verificar e resolver colisões. O que eu recomendaria fazer é verificar todas as colisões do eixo x, depois todas as colisões do eixo y. Dessa forma, você não ficará preso a objetos que não deveria. feliz colisão =)

ultifinito
fonte
1

Obrigado por toda a ajuda, pessoal, e desculpe pela resposta tardia.

Levei todas essas idéias em consideração e elas me ajudaram a resolver o problema. Se alguém estiver curioso para saber como eu resolvi o problema original e completo, veja aqui no Stack Overflow a resposta completa (incluindo explicações sobre como lidar com a colisão mais adequadamente). A pessoa que respondeu à minha pergunta foi realmente generosa e forneceu um diagrama, para ajudar aqueles que também têm esse problema.

Obrigado às seguintes pessoas por me ajudarem com esse problema:

  • DanTup - Danny Tuppeny
  • bummzack
  • ghostonline
  • ultifinito

Eu agradeço muito!

zona de defesa
fonte
5
Acho melhor se você der crédito às pessoas que o ajudaram, em vez de criar sua própria resposta, do que marcá-la como resposta. No mínimo, dê a eles um voto positivo. Apenas meus dois centavos.
dman
Essa é uma boa ideia; Eu não pensei sobre isso. Peço desculpas por isso, mas ainda não tenho reputação suficiente para votar. Quando eu conseguir, eu farei.
Defesa-zona
@ defesa-zona - Não é nada contra você. Tomei a liberdade e votei todas as respostas para você.
dman
Obrigado por fazer isso. Aparentemente, agora eu recebi reputação suficiente para votar graças ao seu voto positivo, por isso também o fiz. Também dei crédito àqueles que ajudaram em minha resposta.
defesa da zona do
@ zona defensora - Haha, isso é muito fofo. Não me lembrava que seria o caso!
dman