Este é um post reciclado do blog antigo da época em que fazia um curso de desenvolvimento de games. Na ocasião, brincávamos na aula de animar um personagem e detectar colisões. Mas, em casa, descobri que a lógica apresentada em aula para detectar as colisões entre áreas retangulares estava com algum erro.

Desenvolvia, nas horas vagas, para treinar C e SDL, o SDLSoccer, que acabou ficando mais para um WarSoccer :-) . Num próximo post apresentarei ele para vocês reescrito também em js. Mas o importante é que eu estava tentando utilizar a tal lógica para impedir que os jogadores entrassem um dentro do outro, dando um efeito meio ‘fantasma’. Mas com a rotina de colisão que estava usando, nada dava certo. E depois de muito depurar, afinal era madrugada, cheguei a conclusão que o erro tinha que estar nela. Então acabei fazendo um mais simples.

Se detectar colisão em retângulos é relativamente simples, mais simples ainda é detectar quando não ocorre a colisão, que foi a lógica que adotei. Mas antes vejam esta rotina funcionando no exemplo abaixo, impedindo que a div do guerreiro entre na div da árvore:

Fixar Scroll no Chrome (Ainda não funciona em outros navegadores)

Vamos lá:

Sendo r1 e r2 os retângulos 1 e 2 podemos afirmar que não haverá colisão de r1 estiver todo a direita ou todo a esquerda de r2. Ou seja se r1.x+r1.w<r2.x ou r1.x>r2.x+r2.w conforme mostra o diagrama abaixo:

Caso contrário, ainda podemos afirmar não haverá colisão se r1 estiver totalmente acima ou totalmente abaixo de r2. Ou seja se r1.y+r1.h<r2.y ou r1.y>r2.y+r2.h conforme mostra o diagrama abaixo:

Caso contrário, se r1 não está nem totalmente acima ou totalmente abaixo ou totalmente a direita ou ainda a esquerda, então afirmamos que existe colisão!

Com isso o código que era:

bool colidiu(SDL_Rect r1, SDL_Rect r2)
{
if ((r1.x >= r2.x && r1.x <= (r2.x + r2.w) &&
r1.y >= r2.y && r1.y <= (r2.y + r2.h)) ||
((r1.x + r1.w) >= r2.x && (r1.x + r1.w) <= (r2.x + r2.w) &&
(r1.y + r1.h) >= r2.y && (r1.y + r1.h) <= (r2.y + r2.h)))
return true;
}

Transformamos em algo mais simples e, no mínimo, mais legível:

bool colidiu(SDL_Rect r1, SDL_Rect r2)
{
if((r1.x+r1.w < r2.x) || (r1.x > r2.x+r2.w)) return false;
if((r1.y+r1.h < r2.y) || (r1.y > r2.y+r2.h)) return false;
return true;
}

ou ainda, numa forma ainda mais simples:

bool colidiu(SDL_Rect r1, SDL_Rect r2)
{
if(((r1.x+r1.w < r2.x) || (r1.x > r2.x+r2.w)) ||
   ((r1.y+r1.h < r2.y) || (r1.y > r2.y+r2.h))) return false;
return true;
}

Traduzindo para Javascript:

function colide(r1_x, r1_y, r1_h, r1_w, r2_x, r2_y, r2_h, r2_w)
{
    if((r1_x+r1_wr2_x+r2_w) ||
       (r1_y+r1_hr2_y+r2_h)) return false;
	return true;
}

No nosso código, só precisamos fazer uma pequena alteração ao final da função trigger: Ao fim dela verificaremos se houve colisão. Caso tenha havido, restauramos a posição anterior da div do aventureiro, conforme vemos abaixo:

	if(!colide(iPosLeft, iPosTop, iTamSprites, iTamSprites,
        iPosLeftArvore, iPosTopArvore, iTamSprites, iTamSprites))
	{
      //não tivemos colisão, então a nova posição é aceita.
      divAnime.style.left=iPosLeft;
      divAnime.style.top=iPosTop;
      return;
	}
       //colisão detectada, posição restaurada.
	iPosLeft= iAtualLeft;
	iPosTop = iAtualTop;

Conclusões:
Embora esta função, tal qual, seja ótima para jogos 2D 'chapados', ela não fica bem em jogos que simulam perspectiva como este. Para casos como este é conveniente determinar uma área de colisão diferente da definida pela própria div. Nas próximas postagens veremos como fazer o ajuste fino na área de colisão encapsulando div, retângulo e posição do personagem num único objeto.

Abraços e até a próxima!

5 Respostas para “Função para Detectar Colisões em Javascript”
  1. Demetrio diz:

    Cara, eu entedi tudo, so fiquei boiando em uma coisa, e se tivessem mais de uma arvore? teria q fazer tratamento para cada retangulo que eu criar? to tentando bolar alguma coisa aqui, mas não sou tão fodastico, estou tentando…

  2. renatolouro diz:

    Olá Demetrio. Você gostará do próximo post que estou preparando para o assunto. Tratará também sobre isso. No próximo post, mostrarei como encapsular os dados de uma dada abstração – no nosso caso a árvore – em uma única estrutura. No caso da linguagem JavaScript, estou falando de objetos. Após criar um objeto do tipo árvore, podemos replica-lo pelo mundo. Podemos, então, guardar todos os objetos do arvore numa estrutura comum, por exemplo, um array. Depois, para o teste de colisão, teremos fazer uma iteração- por exemplo utilizando o comando for – testando todos os elementos do array. Esta é apenas uma maneira de se fazer.
    Vou tentar me esforçar para terminar o post durante a semana.
    Abraços!

  3. Replicando Personagens com Objetos em Javascript | Blog do Renato, o Louro diz:

    [...] última postagem desta série, Função para Detectar Colisões em Javascript, vimos como criar uma rotina de verificação de colisão entre as DIVs que representava o [...]

  4. adailton diz:

    amigo vejo que faz tempo que voce postou esse tutorial e é muito bom eu queria se eu posso definir um tamanho x da div que recebera a colisao? para dar um efeito de que o avatar esta passando atras da arvore.

    tenho um projeto em sockets tcp que estou fazendo mais me esbarrei nessa logica e nao estou encontrando muita coisa na internet sobre isso vlw obrigadao ja ajudou bastante.

  5. renatolouro diz:

    Olá adailton,
    Na verdade o tutorial começou a muito tempo, mas continuo escrevendo ele ainda hoje no meu raro tempo livre.
    No post http://blog.renatolouro.com.br/2011/06/replicando-personagens-com-objetos-em-javascript/ acho que respondo ao que pergunta. No próximo post da série alterarei o z-index dos personagens em função da altura da tela.

    Abraços

Deixe um Comentário

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>