Eu implementei uma rotina simples de detecção de colisão usando AABB's entre meu sprite de jogo principal e várias plataformas (consulte o código abaixo). Funciona muito bem, mas agora estou introduzindo a gravidade para fazer meu personagem cair e isso mostrou alguns problemas com a rotina do meu CD.
Eu acho que o cerne da questão é que minha rotina de CD move o sprite de volta ao longo do eixo em que ele penetrou a menor quantidade no outro sprite. Portanto, se ele estiver mais no eixo Y do que no X, ele será movido de volta ao longo do eixo X.
No entanto, agora meu sprite (herói) está caindo a uma taxa de 30 pixels por quadro (é uma tela de 1504 de altura - eu preciso que caia tão rápido quanto eu quero tentar simular a gravidade 'normal', qualquer velocidade mais lenta parece estranha ) Estou recebendo esses problemas. Vou tentar mostrar o que está acontecendo (e o que acho que está causando isso - embora não tenha certeza) com algumas fotos: (Código abaixo das fotos).
Gostaria de receber algumas sugestões sobre como contornar esse problema.
Para esclarecimento, na foto acima, quando digo que a posição está corrigida 'incorretamente', isso talvez seja um pouco enganador. O código em si está funcionando corretamente para como ele é escrito, ou dito de outra forma, o próprio algoritmo se comportando como eu esperaria, mas preciso alterar o comportamento para impedir que esse problema aconteça, espero que esclareça meus comentários na imagem .
My Code
public boolean heroWithPlatforms(){
//Set Hero center for this frame
heroCenterX = hero.xScreen+(hero.quadWidth/2);
heroCenterY = hero.yScreen+(hero.quadHeight/2);
//Iterate through all platforms to check for collisions
for(x=0;x<platformCoords.length;x+=2){
//Set platform Center for this iteration
platformCenterX = platformCoords[x]+(platforms.quadWidth/2);
platformCenterY = platformCoords[x+1]+(platforms.quadHeight/2);
// the Dif variables are the difference (absolute value)
// of the center of the two sprites being compared (one for X axis difference
//and on for the Y axis difference)
difX = Math.abs(heroCenterX-platformCenterX);
difY = Math.abs(heroCenterY-platformCenterY);
//If 1
//Check the difference between the 2 centers and compare it to the vector (the sum of
//the two sprite's widths/2. If it is smaller, then the sprites are pverlapping along
//the X axis and we can now proceed to check the Y axis
if (difX<vecXPlatform){
//If 2
//Same as above but for the Y axis, if this is also true, then we have a collision
if(difY<vecYPlatform){
//we now know sprites are colliding, so we now need to know exactly by how much
//penX will hold the value equal to the amount one sprite (hero, in this case)
//has overlapped with the other sprite (the platform)
penX = vecXPlatform-difX;
penY = vecYPlatform-difY;
//One sprite has penetrated into the other, mostly in the Y axis, so move sprite
//back on the X Axis
if (penX < penY){hero.xScreen-=penX*(heroCenterX-platformCenterX>=0 ? -1 : 1);}
//Sprite has penetrated into the other, mostly in the X asis, so move sprite
//back on the Y Axis
else if (penY < penX) {hero.yScreen-=penY*(heroCenterY-platformCenterY>=0 ? -1 : 1);}
return true;
}//Close 'If' 2
} //Close 'If' 1
}
//Otherwise, no collision
return false;
}
fonte
//One sprite has penetrated into the other, mostly in the Y axis, so move sprite //back on the X Axis
Respostas:
durante minhas experiências com a tela HTML5 e o AABB, encontrei exatamente o que você está enfrentando. Isso aconteceu quando tentei criar uma plataforma a partir de caixas adjacentes de 32x32 pixels.
Soluções que eu tentei por ordem de minha preferência pessoal
1 - Divida os movimentos por eixo
O meu atual, e acho que vou continuar meu jogo com ele. Mas não se esqueça de verificar o que chamo de Phantom AABB, se você precisar de uma solução rápida sem precisar modificar muito o seu design atual.
Meu mundo do jogo tem uma física muito simples. Ainda não há conceito de forças, apenas deslocamento. A gravidade é um deslocamento constante para baixo. Sem aceleração (ainda).
O deslocamento é aplicado por eixo. E nisso consiste esta primeira solução.
Cada quadro de jogo:
move_x é definido de acordo com o processamento de entrada, se não for pressionada a tecla de seta, move_x = 0. Valores abaixo de zero significam à esquerda, caso contrário, à direita.
Processe todos os outros sprites em movimento e decida os valores do componente "movimentos", eles também têm o componente "movimentos".
Nota: após a detecção e resposta da colisão, o componente de movimento deve ser definido como zero, ambas as propriedades x e y, porque no próximo tique, a gravidade será adicionada novamente. Se você não redefinir, você terminará com um movimento.y de duas vezes a gravidade desejada e, no próximo tick, será três vezes.
Em seguida, insira o código de aplicação de deslocamento e de detecção de colisão.
O componente de movimentos não é realmente a posição atual do sprite. O sprite ainda não foi movido, mesmo que o move.x ou y seja alterado. O componente "movimentos" é uma maneira de salvar o deslocamento a ser aplicado na posição do sprite na parte correta do ciclo do jogo.
Processe primeiro o eixo y (move.y). Aplique moves.y para sprite.position.y. Em seguida, aplique o método AABB para verificar se o sprite em movimento que está sendo processado se sobrepõe a uma plataforma AABB (você já entendeu como fazer isso). Se ele se sobrepõe, mova o sprite de volta ao eixo y aplicando penetration.y. Sim, ao contrário da minha outra resposta, agora este método de evolução evoluiu ignora a penetração.x, para esta etapa do processo. E usamos a penetração. Mesmo que seja maior que a penetração.x, nem estamos verificando.
Em seguida, processe o eixo x (move.x). Aplique moves.x em sprite.position.x. E faça o oposto do que antes. Desta vez, você moverá o sprite apenas no eixo horizontal aplicando penetration.x. Como antes, você não se importa se penetration.x for menor ou maior que penetration.y.
O conceito aqui é que, se o sprite não estiver se movendo e inicialmente não estiver colidindo, ele permanecerá assim no próximo quadro (sprite.moves.xe ey são zero). O caso especial em que outro objeto do jogo se teletransporta magicamente para uma posição que se sobrepõe ao sprite que começa a ser processado é algo que tratarei mais tarde.
Quando um sprite começa a se mover. Se ele estiver se movendo apenas no eixo x, então estamos interessados apenas se ele penetrar em algo pela esquerda ou pela direita. Como o sprite não está se movendo para baixo, e sabemos disso porque a propriedade y do componente de movimentos é zero, nem verificamos o valor calculado para a penetração.y, vemos apenas em penetration.x. Se existe penetração no eixo do movimento, aplicamos a correção, caso contrário, simplesmente deixamos o sprite se mover.
E o deslocamento diagonal, não necessariamente em um ângulo de 45 graus?
O sistema proposto pode lidar com isso. Você só precisa processar cada eixo separadamente. Durante sua simulação. Diferentes forças são aplicadas ao sprite. Mesmo que seu motor ainda não entenda as forças. Essas forças se traduzem em um deslocamento arbitrário no plano 2D, esse deslocamento é um vetor 2D em qualquer direção e comprimento. A partir deste vetor, você pode extrair o eixo y e o eixo x.
O que fazer se o vetor de deslocamento for muito grande?
Você não quer isso:
Como nossos novos movimentos de aplicação lógica processam um eixo primeiro e depois o outro, um deslocamento grande pode acabar sendo visto pelo código de colisão como um L grande, isso não detectará corretamente colisões com objetos que estão cruzando seu vetor de deslocamento.
A solução é dividir grandes deslocamentos em pequenos passos, idealmente a largura ou altura do sprite ou metade da largura ou altura do sprite. Para obter algo parecido com isto:
E quanto a teletransportar sprites?
Se você representar o teletransporte como um deslocamento grande, usando o mesmo componente de movimentos que um movimento normal, não há problema. Caso contrário, é necessário pensar em uma maneira de marcar o sprite de teletransporte a ser processado pelo código de colisão (adicionando a uma lista dedicada a esse objetivo?). Durante o código de resposta, você pode deslocar diretamente o sprite permanente que estava no a maneira como o teletransporte ocorreu.
2 - AABB fantasma
Tenha um AABB secundário para cada sprite afetado pela gravidade que comece no fundo do sprite, tenha a mesma largura do AABB do sprite e 1 pixel de altura.
A idéia aqui é que esse AABB fantasma esteja sempre colidindo com a plataforma abaixo do sprite. Somente quando esse AABB fantasma não se sobrepõe, é permitido que a gravidade seja aplicada ao sprite.
Você não precisa calcular o vetor de penetração para o AABB fantasma e a plataforma. Você está interessado apenas em um teste de colisão simples; se ele se sobrepõe a uma plataforma, a gravidade não pode ser aplicada. Seu sprite pode ter uma propriedade booleana que informa se está sobre uma plataforma ou no ar. Você está no ar quando o seu AABB fantasma não está colidindo com uma plataforma abaixo do player.
Este:
Torna-se algo assim:
Muito simples e confiável.
Você deixa sua implementação do AABB como está.
O AABB fantasma pode ter um loop dedicado que os processa, que apenas verifica colisões simples e não perde tempo calculando o vetor de penetração. Esse loop deve estar antes do código normal de colisão e resposta. Você pode aplicar a gravidade durante esse loop, pois os sprites no ar aplicam a gravidade. Se o AABB fantasma é em si uma classe ou estrutura, ele pode ter uma referência ao sprite ao qual pertence.
Isso é muito semelhante à outra solução proposta, na qual você verifica se o seu jogador está pulando. Estou dando uma maneira possível de detectar se o jogador está de pé sobre uma plataforma.
fonte
Eu combinaria os blocos de plataforma em uma única entidade de plataforma.
Por exemplo, suponha que você pegue os três blocos das fotos e os combine em uma única entidade, sua entidade teria uma caixa de colisão de:
Usar isso para colisão fornecerá o y como o eixo menos penetrante na maior parte da plataforma e ainda permitirá que você tenha blocos específicos dentro da plataforma.
fonte
Problemas como esses são comuns com novos métodos de detecção de colisões.
Não sei muito sobre sua configuração de colisão atual, mas aqui está como deve ser:
Primeiro, faça estas variáveis:
Segundo, faça um cronômetro para calcular o delta para afetar a velocidade do objeto.
Terceiro, faça o seguinte:
Se você fizer isso, não deverá haver nenhum problema. Espero que isto ajude!
fonte