Desenvolvendo em XNA

GameDev #9: Trabalhando com sistemas de detecção de colisão

No último artigo da coluna GameDev , você aprendeu um pouco mais sobre os conceitos utilizados no desenvolvimento de jogos em 2D, sendo o ... (por Sergio Oliveira em 22/03/2013, via Xbox Blast)

GameDev #9: Trabalhando com sistemas de detecção de colisão
No último artigo da coluna GameDev, você aprendeu um pouco mais sobre os conceitos utilizados no desenvolvimento de jogos em 2D, sendo o principal deles o de sprites. Além disso, você explorou alguns recursos do XNA, criou seus primeiros métodos e fez os sprites ganharam vida, movimentando-os dentro dos limites da tela do jogo. O artigo de hoje vai um pouco mais fundo e não só mostrará, mas também ensinará os princípios utilizados em todos os jogos para detectar colisões entre dois ou mais objetos.

Não esqueça de preparar seu ambiente de desenvolvimento

Se você não acompanhou o último artigo e não tiver a última versão do projeto que está sendo desenvolvido, poderá fazer o download dele acessando o endereço: http://sdrv.ms/ZJ6bFv.

Lembrando que, para desenvolver o que é proposto na coluna, é necessário ter instalado em sua máquina o Microsoft Visual C# 2010 Express e o XNA Game Studio, ambos gratuitos.


Se você está utilizando o Windows 8, então talvez tenha algum problema ao instalar o XNA Game Studio. Esse erro ocorre porque o Game Studio instala consigo uma versão do Games for Windows – LIVE nos bastidores. O problema é que há incompatibilidade entre o Games for Windows – LIVE que o XNA está tentando instalar e o Windows 8, mas isso pode ser corrigido baixando e instalando manualmente a versão mais recente no seguinte link: http://bit.ly/W96StP

Ao finalizar a atualização, basta tentar instalar o XNA Game Studio novamente e deverá funcionar.

Entendendo os conceitos de colisões

Fazer com que o sprite tenha sua movimentação limitada às bordas do jogo não deixa de ser uma forma de detecção de colisão, bem simples, é verdade. Porém, em jogos 2D, geralmente se deseja realizar detecção de colisão entre dois ou mais sprites.

Existem várias formas de trabalhar com detecção de colisões em qualquer jogo que se queira fazer – basta dar uma procurada no oráculo para constatar essa afirmação. Porém, o objetivo aqui é apresentar um exemplo simples para que os que acompanham a coluna possam entender o conceito.

Quando se trabalha com detecção de colisões, não é sensato pensar em testar pixel por pixel em cada um dos sprites do jogo. Já imaginou o quão trabalhoso isso seria? E o quanto a performance comprometeria o desempenho do jogo?

Desse modo, os algoritmos de detecção de colisão trabalham com representações aproximadas das formas dos sprites, o que já facilita bastante a elaboração da sua fórmula. Os algoritmos de detecção de colisão mais comuns utilizam o conceito de bounding boxes, que aproxima a forma do objeto com um ou mais retângulos, ou caixas (daí a palavra boxes). A figura abaixo nos mostra um sprite cuja forma está contida dentro de uma caixa que define seus limites para colisão.

A forma mais simples de detectar colisões é por meio do método bounding boxes

Implementando o bounding box

Uma maneira simples de desenvolver e testar o bounding box é simplesmente verificar se as posições das coordenadas X e Y da primeira caixa (que envolve o sprite que você deseja testar) estão dentro da segunda caixa (que envolve o segundo objeto a ser testado).

Em outras palavras, você deve verificar se os valores de X e Y da caixa que quer testar são menores ou iguais aos valores de X e Y da outra caixa, mais a largura da outra caixa.

Sendo assim, mãos à obra. No arquivo classSprite.cs, adicione o seguinte método (nomeado de detectaColisao) que receberá um sprite como parâmetro e o testará com o sprite atual. Se houver uma colisão, o método retornará verdadeiro.


Para entender a lógica desse código, analise-o observando a figura abaixo e prossiga somente se tiver certeza do que está acontecendo aqui.

Demonstração de como detectar a colisão de dois retângulos
Analisando o exemplo do código, as duas caixas só irão se sobrepor se ambas as coordenadas X e Y do retângulo 2 estiverem no intervalo (X para X + largura, Y para Y + altura) do retângulo 1. Observando a figura acima, veja que a coordenada Y do retângulo 2 não é maior que a coordenada Y mais a altura do retângulo 1. Isso significa que suas caixas podem estar colidindo. Porém, quando verificamos a coordenada X do retângulo 2, você verá que é maior que a coordenada X mais a largura do retângulo 1, o que significa que as caixas não estão colidindo.

A figura abaixo ilustra uma situação em que você tem uma colisão. Nesse caso, perceba que ambas as posições, X e Y, do retângulo 2 estão contidas no intervalo do retângulo 1. No exemplo de código, você também faz o teste oposto, verificando se as coordenadas X e Y do retângulo 1 estão contidas no intervalo do retângulo 2. Dessa forma, estamos nos prevenindo de, por exemplo, o canto superior esquerdo do retângulo 2 estar fora do retângulo 1, mas o topo deste estar dentro do retângulo 2.

Exemplo de quando dois retângulos colidem
Para testar o método na prática, você criará um segundo sprite no meio da tela do jogo. Para fazer isso, basta replicar o sprite que você criou no encontro anterior e incluir o código para testar colisões no método Update na classe Game1.

Primeiramente, declare uma nova variável de sprite no início da classe Game1, junto com as outras definições:


Agora, no método LoadContent, inclua o código para a criação e inicialização do sprite:


E não se esqueça de adicionar o código para descarregar o sprite no UnloadContent:


No método Update, inclua o código para movimentar o sprite na tela:


E, finalmente, no método Draw, inclua o código para desenhar o novo sprite na tela:


Nesse momento, se você executar o jogo, perceberá que os dois sprites estão se movimentado, mas ainda não estão colidindo. Resolveremos isso adicionando uma chamada ao método detectaColisao no método Update e alterando a velocidade entre os sprites, conforme segue abaixo:


Agora sim, se você executar o seu jogo, irá perceber que os sprites estão colidindo um com o outro, além de colidirem com as bordas da janela. Bacana, não?

Apesar do bounding boxes definir um espaço retangular para detecção de colisões, ainda é possível detectar colisões entre esferas com a adição de alguns cálculosContudo, apesar do sistema de detecção de colisões estar utilizando o algoritmo do bounding box, depois de alguns testes você perceberá um problema. Se os sprites colidirem diagonalmente, eles irão se chocar antes de atingirem um ao outro de verdade. Isso ocorre justamente pelo bounding box utilizar caixas para representar a forma geométrica das esferas.

Quando quiser testar colisões entre esferas, verifique se a distância entre os centros delas são menores que a soma dos seus raios. Em caso positivo, então uma colisão ocorreu. Essa é a forma mais eficiente de detectar colisões entre duas esferas.

Para dar suporte a esse sistema, na classe classSprite vamos criar duas novas propriedades, uma chamada centro e outra raio, que serão calculadas de acordo com outra propriedade do sprite.


Em seguida, crie um novo método para testar esse tipo específico de colisão:


Finalmente, atualize o método Update na classe Game1 para chamar o método detectaColisaoCirculo ao invés do detectaColisao. Agora sim, você perceberá que os círculos só irão colidir quando eles de fato colidirem ;)

No encontro de hoje você trabalhou a ideia básica de um sistema de detecção de colisões entre dois sprites em um jogo 2D. Aos poucos você está avançando nos conceitos de programação de jogos e no próximo artigo aprenderá a capturar a entrada de dados do jogador via teclado e a utilizará para controlar um dos sprites. Fique ligado e até lá!

O projeto atualizado com o código desse encontro pode ser baixado no endereço: http://sdrv.ms/103ONfV

Revisão: Bruno Nominato 
Sergio Oliveira escreve para o Xbox Blast sob a licença Creative Commons BY-SA 3.0. Você pode usar e compartilhar este conteúdo desde que credite o autor e veículo original do mesmo.

Comentários

Google+
Disqus
Facebook