segunda-feira, 25 de maio de 2015

Unity3D: Porquê evitar 2 colliders em um objeto.



Unity3D: Porquê dois Colliders em um único GameObject podem causar imprevistos, e como proceder.



Disclaimer: Artigo técnico! Se você durmir, a culpa não é minha!

Às vezes nos deparamos com problemas gigantescos que possuem soluções simples. Neste caso, a resposta veio por acaso, e só depois de resolver o problema é que fui encontrar essa dica na documentação oficial!

Há meses tenho lutado com uma série de bugs no jogo do Sansão. Vasculhei milhares de linhas de código, em dezenas de arquivos, e nada... até que hoje consegui encontrar a fonte desses problemas: 2 colliders em um único GameObject.

Um pouco de background...

Estou trabalhando com um Game Engine chamado Unity 3D. Nele existe um componente chamado "Collider", que detecta colisões com outros Colliders, desde que um deles esteja em um objeto com um "RigidBody" (componente de física).

Quando um Collider inicia uma colisão com outro, ele dispara um evento chamado "OnCollisionEnter", ou "OnTriggerEnter", dependendo se ele está calculando física (isTrigger). Quando o Collider sai da colisão, dispara o evento chamado "OnTriggerExit" ou "OnCollisionExit".

Até aí, tudo bem, pois podemos usar esses métodos para atacar um inimigo quando trombamos com ele, coletar um item, etc...

E aí, qual o problema?

Meu problema começou quando coloquei um collider do tamanho do personagem para as colisões da física, e um outro collider um pouco maior como trigger (não reage com a física, apenas dispara os eventos citados) para gerenciar a coleta de itens e a proximidade dos inimigos.

2 Colliders em um objeto: a cápsula é usada para física, a esfera como trigger.
Na minha cabeça tudo estava bem, pois ao inimigo aproximar do personagem, eles começavam a brigar, e ao coletar itens, eles eram coletados normalmente, porém existiam vários pequenos bugs difíceis de reproduzir que aconteciam de vez em quando: alguns itens eram coletados duas vezes, o inimigo morria e continuava sendo atacado pelo personagem, etc...

O que estava acontecendo? veja a imagem acima e imagine o que vou descrever!

Quando um inimigo se aproxima do personagem, ele toca primeiro na esfera, o collider maior. Neste momento é disparado um evento de entrada. Quando o inimigo se aproxima mais um pouco do centro do objeto e toca na cápsula (collider menor, dentro da esfera), novamente o mesmo evento é disparado. Neste momento por conta das colisões da física, o inimigo é expulso de dentro da cápsula, e é disparado um evento de saída. 

Se no script eu usar o evento de saída para avisar o jogador que o inimigo já não está mais por perto, podemos ter um problema, pois eu não tenho certeza de que o inimigo está longe, pois ele ainda pode estar dentro da esfera maior. 

Você até poderia dizer que são eventos distindos, um para Triggers (OnTriggerExit) e Colliders (OnColliderExit), porém não é tão simples:
Segundo a documentação da Unity, um evento de OnTriggerExit, por exemplo é enviado não só para o GameObject que contém o Trigger: "This message is sent to the trigger and the collider that touches the trigger.".
Sendo assim, se o objeto que meu personagem está tocando contém um collider do tipo trigger, esse evento será disparado mesmo quando o objeto sair do collider do tipo "não-trigger".

Existem diversas situações onde estamos propensos a problemas, pela repetição do mesmo evento, e principalmente nesta zona "neutra", em que está dentro de um collider, porém fora de outro.

Tudo isso só é um problema se não for observada essa particularidade da relação entre os colliders

E agora, o que eu faço?

Minha recomendação? Evite trabalhar com dois colliders no mesmo objeto sempre que você estiver dependendo dos eventos de entrada ou saída. Obviamente você poderia tratar caso a caso, mas sempre que possível, evite problemas.

Minha solução foi de manter o collider de cápsula, que estava sendo usado pela física junto com o rigidbody, e em um outro GameObject, filho do anterior, foi colocado o trigger em forma de esfera, juntamente com os scripts que verificam as colisões relacionadas à esfera. Assim ficou muito mais simples gerenciar as colisões, sem redundâncias, nem ambiguidades!


O mais interessante foi observar que a própria documentação da Unity recomenda não usar mais de um collider em um único gameobject (descobri isso buscando material para este post!):


"To add multiple Colliders for an object, create child GameObjects and attach a Collider to each one. This allows each Collider to be manipulated independently."


O detalhe é que essa informação genérica sobre colliders não está em uma seção geral, mas está dentro do conteúdo sobre box colliders:

http://docs.unity3d.com/Manual/class-BoxCollider.html


Recomendo a leitura desse artigo para verificar os diversos tipos de colisões e como funcionam, além de dicas.

Resumindo, com Unity3D, evite problemas: não coloque mais de um Collider por GameObject. Se for preciso, crie filhos, cada qual com seu Collider!
Unknown Desenvolvedor

Funny Sheep é uma empresa de conteúdo da área de entretenimento, focada em Conteúdo Cristão de Qualidade!

Nenhum comentário:

Postar um comentário