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.
TheirPos-ourSize +- 1
remover 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?)Respostas:
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.
fonte
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 :-)
fonte
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 =)
fonte
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:
Eu agradeço muito!
fonte