Unity 5 Car Tutorial – Atualização 01

car tutorial atualizacao.jpg

Olá pessoal, já faz um bom tempo desde a última postagem no blog (toneladas de trabalho ultimamente me impediram de atualizá-lo).

Abrindo o projeto de carros para Unity que foi apresentado aqui, aqui e aqui (se você ainda não os viu, volte e leia cada um deles, pois são a base teórica para esse tutorial), percebi que o mesmo não estava funcionando mais na Unity 5.

Isso se deve a atualização do PhysX3 e a incompatibilidade do antigo WheelCollider com o novo componente, conforme podemos ver na documentação oficial:

The new WheelCollider is powered by the PhysX3 Vehicles SDK that is basically a completely new vehicle simulation library when compared to PhysX2.

Nessa mesma página temos um tutorial de como fazer o setup de um carro usando o novo sistema, cujas diferenças são mínimas ou inexistentes conforme percebi. Decidi então atualizar o tutorial antigo para essa nova versão, e percebi que agora está muito mais fácil a atualização do steer das rodas e a simulação da suspensão do veículo (esse último totalmente automático!).

Portanto, o script do antigo tutorial foi reduzido, mas existe uma particularidade do parentesco dos meshes das rodas do carro para se tirar proveito da melhoria do steer. Vejamos:

Hierarquia do veículo

Adicione um RigidBody ao objeto pai de todos os sub objetos do veículo.

Adicione um objeto vazio chamado CenterMass localizado ligeiramente abaixo dos pneus, no centro do carro. Esse será o ponto de centro de massa do rigidbody.

center mass.jpg

Adicione um objeto vazio posicionado ao centro do veículo, e um box collider que englobe todo o veículo. Esse será o collider geral do carro. Se quiser, personalize com vários sub box colliders para adequar melhor ao formato do veículo.

Cada objeto com mesh de pneus deve estar agrupado dentro de um outro empty game object, com rotação 0,0,0.

wheel.jpg

Tente manter o parent dos meshes visualmente onde a roda prende no eixo de giro de um carro real, pois ele será o pivô de rotação do pneu.

wheel_lateral.jpg

Assim, teremos quatro objetos vazios contendo o modelo dos pneus.

hierarquia

O próximo passo é recriar a mesma estrutura de objetos vazios->pneus para objetos vazios-> wheel colliders.

Adicione um objeto vazio e posicione-o no mesmo local do objeto vazio que contém os meshes do pneu. Adicione então um wheel collider nesse objeto.

Clique no componente wheel collider e observe sua orientação. É importante que a rotação dele esteja no mesmo sentido que a dos meshes dos pneus. Isso não aconteceu no nosso caso. O wheel collider encontra-se perpendicular ao sentido de giro do pneu.

wheelcollider_badrotation.jpg

Dessa forma, o carro irá correr na lateral e os meshes dos pneus irão rotacionar errados. Se você tentar rotacionar o objeto que contém o wheel collider para corrigir a orientação dele, nada acontecerá. Nem mesmo se criar um outro objeto vazio rotacionado corretamente e fizer com que o wheel collider seja filho dele: ele continuará orientado perpendicular ao pneu. Vamos investigar o motivo.

Consertando a orientação do wheel collider

O sistema de coordenadas global da Unity é: Z forward, Y up e X side. Ou seja, o sentido de ‘face/para frente’ é Z positivo, ‘alto/para cima’ Y positivo e ‘lateral/direita’ X positivo. Vamos observar a orientação do nosso carro de acordo com esses eixos.

car orientation.jpg

 

 

Selecione o pai de todos os objetos do carro. Observe que a frente do carro está alinhada para o eixo X, e a lateral para o eixo Z. Exatamente a mesma orientação inversa que temos no wheel collider das rodas.

O objeto parent LamboAventador contém o RigidBody e está naturalmente orientado em relação aos eixos globais da Unity, pois é um objeto que não é filho de ninguém.

Já o objeto Car, é filho de LamboAventador, e mesmo que possua uma orientação 0,0,0 , esta não está naturalmente orientada em relação ao eixo global da Unity. O correto seria a frente do carro (onde ficam os faróis) apontarem para o eixo +Z (o sentido positivo do eixo é exatamente o sentido da seta azul no ambiente 3D da Unity).

car bad rotation 2.jpg

 

Para corrigir, vamos rotacionar o objeto Car até que o mesh do carro esteja apontando na direção +Z, o que nesse caso foi uma rotação de 0, 270, 0.

car nice orientation2.jpg

Observe que agora o mesh do carro aponta para o eixo +Z e os wheel colliders também.

Para este modelo, ajustei os Wheel Colliders da seguinte maneira:

  • Radius: 0,39
  • Suspension Distance: 0,1

 

Scripts

Conforme vimos na última parte do antigo tutorial, nós tínhamos dois scripts: CarControl e WheelAlignment.

O primeiro script cuidava do controle geral do carro pelo jogador e atualização da rotação dos pneus, e o segundo script era utilizado para simulação da suspensão do veículo utilizando RayCast para identificar quando o wheelCollider tocava ou não alguma superfície.

suspension.jpg

A boa notícia é que esse cálculo não é mais necessário, pois foi automatizado pelo componente agora (já era hora…).

WheelController

Crie um script chamado WheelControler e adicione em cada objeto com um wheel collider:

script wheel controller.jpg

As propriedades significam:

  • WheelCollider wheelCollider: wheelCollider do objeto
  • Transform wheelMesh: mesh da roda referenciado no inspector
  • bool receiveMotorTorque: deve receber torque do motor?
  • bool receiveSteer: deve fazer steer (o pneu gira em curva)?
  • bool receiveBrake: deve receber brake?

Usualmente apenas as rodas frontais possuem steer, as quatro possuem brake e o torque dependerá da posição do motor do seu carro (frontal ou traseiro), embora não faça diferença para esse exemplo.

A cada FixedUpdate executamos o método RotateMesh do WheelCollider, que se encarrega de atualizar a posição e rotação do mesh dos pneus.

GetWorldPose:

Gets the world space pose of the wheel accounting for ground contact, suspension limits, steer angle, and rotation angle (angles in degrees).

Um quaternion e Vector3 são preenchidos pelo método GetWorldPose, que nos retorna a nova posição e rotação da roda levando em conta os atributos do WheelCollider como steer, ponto de contato do solo, eixo de rotação e limite da suspensão em espaço global. Em seguida basta atribuir os valores retornados ao respectivo mesh da roda.

CarController

car controller.jpg

A modificação mais significativa nesse script é o uso de um jagged array para armazenar todos os WheelControllers do veículo, tornando mais fácil a manipulação de veículos com menos ou mais rodas, uma vez que iteramos por todos os itens do array.

Em FixedUpdate, primeiro obtemos os valores de steer,torque e se devemos frear, e então iteramos por todos os itens do array e aplicamos os valores ao wheelCollider conforme definido para cada roda:

Por fim, adicione o script Car Controller em LamboAventador e configure conforme a imagem:

car config controller.jpg

Teste inicial

initial test.jpg

Apesar de nosso esforço em corrigir a orientação do collider, a aplicação de rotação nas rodas está causando um efeito indesejado no mesh do pneu. Vamos analisar o motivo.

Aplicamos uma rotação de 270y em Car para corrigir o alinhamento com +Z do modelo. Porém, isso significa que consequentemente aplicamos também essa rotação em todos os filhos de Car. Quando se diz que um objeto é filho do outro, significa que sua matrix de transformação é relacional à matrix de transformação do objeto pai, portanto, ela ‘absorve’ suas rotações, translações e escalas e as aplica sob suas próprias rotações, translações e escalas. Isso é o efeito de espaço local e espaço global.

Para evitar possíveis complicações, queremos que os objetos com wheel colliders estejam com orientação 0,0,0, portanto, vamos movê-los para fora de Car e torná-los filhos diretos de LamboAventador. Mas isso não corrige o problema. O problema de fato é que o mesh dos pneus está rotacionado. Portanto, vamos torná-los também filhos direto de LamboAventador.

wheel bad rotation.jpg

Imediatamente observamos uma rotação de -90y em WheelFrontLeft, que é exatamente a rotação de 270y que aplicamos em Car. Agora sim sabemos qual é o problema e como resolvê-lo!

Crie um novo objeto vazio chamado NewWheelFrontLeft dentro de WheelFrontLeft com posição e rotação 0,0,0. Agora, mova-o para fora de WheelFrontLeft, torando-o filho direto de LamboAventador. Assim, criamos um novo pivô exatamente na mesma posição da roda. Corrija novamente a rotação de NewWheelFrontLeft  para 0,0,0. Agora, torne WheelFrontLeft filho de NewWheelFrontLeft.

Em WheelFrontLeftCollider, acesse o script WheelController e mude a referência do mesh da roda de WheelFrontLeft para NewWheelFrontLeft. Vamos executar novamente e ver o resultado:

car wheel fixed

Agora sim, a rota gira corretamente. Basta replicar o que foi feito para as demais rodas.

Resultado

Esse é o resultado final do novo carro:

result.jpg

É perceptível a compressão da suspensão quando a roda está em cima do quebra molas (observe o espaço vazio entre ela e o capô).

Visualmente não importa o modelo que se use. Caso suas rodas rotacionem de forma estranha ou no eixo errado, verifique novamente se os meshes estão com rotação 0,0,0 e se o pivot dos mesmos se encontra no eixo de giro adequado.

Não se esqueça de colocar o BoxCollider no carro e o RigidBody com massa. Caso seu veículo saia voando e girando no ar, pode ser por esquecimento dessa etapa.

O projeto completo pode ser acompanhado aqui.

Espero que gostem, até a próxima!

14 comentários sobre “Unity 5 Car Tutorial – Atualização 01

  1. Parabéns, fiz todo o processo do tuto anterior na Unity 5, consegui fazer o carro andar, mover as rodas, porem tive que fazer diversas alterações nos parâmetros dos Colliders das rodas. Percebi a aceleração muito baixa e não aumenta com a alteração do torque, tive também problemas com os freios. Agora vi sua atualização e vou faze-la. Parabéns pelo trabalho.

    Curtir

    1. Obrigado Carlos!
      O WheelCollider é um componente desafiador para ser configurado e obter bons resultados. Tanto que nos exemplos da Unity 3-4 tinham uma abordagem simulando toda a física do veículo sem ele.
      A próxima atualização desse tutorial (em andamento…) será um mecanismo de marchas para o carro.

      Curtir

  2. Olá. Primeiro parabéns e muito obrigado pelo tutorial.

    Tenho uma dúvida:
    Eu usei o exemplo antigo para fazer o alinhamento das mesh. porém não funcionou de jeito nenhum.
    aquele modo não funciona na nova unity?
    tive que fazer algo que não gosto que é copiar e colar o scrip. mas como já tinha perdido muito tempo não quis ir passo a passo.

    Curtir

    1. Olá Bergson. Aquele modelo antigo não funcionará na Unity 5. Houveram mudanças internas no wheelcollider que tornaram o projeto antigo inválido.

      Curtir

  3. a versão antiga tinha ficado assim:

    por favor, me diga onde errei?

    using UnityEngine;
    using System.Collections;

    public class WheelAlignment : MonoBehaviour {

    public WheelCollider wheelCollider;
    private RaycastHit hit;
    
    
    void FixedUpdate()
    {
        Vector3 centerCollider = wheelCollider.transform.position;
        if (Physics.Raycast (centerCollider, -wheelCollider.transform.up, out hit, wheelCollider.suspensionDistance + wheelCollider.radius))
    
            transform.position = hit.point + (wheelCollider.transform.up * wheelCollider.radius);       
            transform.position = centerCollider - (wheelCollider.transform.up * wheelCollider.suspensionDistance);
    
    }
    

    }

    Curtir

  4. Bergson, nesse script nós calculamos a distância que a roda deve deslocar para cima ou para baixo em relação à suspensão do veículo. Atualmente na Unity 5 isso não é mais necessário, por isso no script desse tutorial apenas obtemos a posição do wheelcollider no mundo e aplicamos ao mesh. Esse cálculo do deslocamento vertical da roda é automático agora. =]

    Curtir

    1. Olá Guilherme. Obrigado pela dúvida!

      Estou atualizando o projeto com um novo modelo, e vou ensinar a configurar a partir do zero na Unity 2017.1, vai ficar melhor agora.

      Se inscreve aí que assim que atualizar o post você recebe a notificação!
      Até!

      Curtir

Resposta