Voltar para o Blog
Quest Log

Rotação na Unity: Quaternion, Euler e LookRotation

Torreta de jogo girando sobre a base para mirar num personagem, com arcos de rotação desenhados em volta

Unity Quaternion sem medo: Euler, LookRotation, RotateTowards e Slerp numa torreta que mira no jogador, e o erro de mexer em rotation.x achando que é ângulo.

Ferramenta:UnityC#

Rotação na Unity: Quaternion, Euler e LookRotation

Unity Quaternion é o tipo que a engine usa para guardar rotações. Você quase nunca mexe nos números dele diretamente: cria com Quaternion.Euler(x, y, z) a partir de ângulos em graus, aponta para um alvo com Quaternion.LookRotation, e gira aos poucos com Quaternion.RotateTowards ou Quaternion.Slerp. Os ângulos em graus que você vê no Inspector são os Euler angles, uma forma legível da mesma rotação.

Aqui você vai construir uma torreta que mira no jogador, primeiro virando na hora e depois girando com velocidade limitada, e vai ver o erro mais comum de rotação na Unity: tentar mudar transform.rotation.x achando que é um ângulo. Código para a Unity 6.3 LTS.

Por que a Unity usa Quaternion

Rotação em 3D com três ângulos (Euler) parece natural: tanto em X, tanto em Y, tanto em Z. O problema é que a ordem em que os três giros são aplicados muda o resultado, e algumas combinações travam um eixo. Esse travamento se chama gimbal lock: dois eixos de rotação se alinham e você perde um grau de liberdade. Interpolar entre dois conjuntos de ângulos Euler também gera caminhos tortos, com o objeto dando voltas estranhas no meio do giro.

O quaternion resolve isso guardando a rotação como um eixo e um ângulo codificados em quatro números (x, y, z, w). Ele não sofre gimbal lock, combina rotações com multiplicação e interpola pelo caminho mais curto. O preço é que os quatro números não fazem sentido para humanos: uma rotação de 90 graus em Y vira algo como (0; 0,707; 0; 0,707), e ninguém lê isso e enxerga a porta aberta. Por isso a Unity guarda em quaternion e mostra em Euler.

A regra prática: pense em graus, guarde em quaternion. Use as funções da classe Quaternion para converter e nunca edite x, y, z ou w na mão.

Euler angles vs quaternion

Transform oferece as duas visões da mesma rotação:

  • transform.rotation e transform.localRotation: o Quaternion.
  • transform.eulerAngles e transform.localEulerAngles: um Vector3 em graus.

O que aparece no campo Rotation do Inspector são os localEulerAngles. Escrever neles funciona para definir uma rotação absoluta:

transform.localEulerAngles = new Vector3(0f, 90f, 0f);

Ler deles exige cuidado. A Unity converte o quaternion de volta para graus toda vez que você lê, e essa conversão pode devolver uma combinação diferente da que você escreveu. Você define -10 em X e lê 350. Define (180, 0, 0) e lê (0, 180, 180), que é a mesma orientação expressa de outro jeito. Código que lê eulerAngles.x, soma um pouco e escreve de volta funciona até o ângulo cruzar 0 ou 360, e aí o objeto pula.

A solução é guardar o ângulo numa variável sua e só escrever:

using UnityEngine;

public class Pitch : MonoBehaviour
{
    [SerializeField] private float pitchSpeed = 60f;
    [SerializeField] private float minPitch = -30f;
    [SerializeField] private float maxPitch = 60f;

    private float pitch;

    public void AddPitch(float input)
    {
        pitch = Mathf.Clamp(pitch + input * pitchSpeed * Time.deltaTime, minPitch, maxPitch);
        transform.localRotation = Quaternion.Euler(pitch, 0f, 0f);
    }
}

O ângulo verdadeiro mora em pitch, que você controla e limita. A rotação do transform é só o resultado.

Quaternion.Euler e transform.Rotate

Duas formas de girar com graus:

Quaternion.Euler(x, y, z) cria uma rotação absoluta. Use quando sabe exatamente para onde o objeto deve apontar: uma porta que abre 90 graus, uma peça de cenário rotacionada no spawn.

transform.Rotate(x, y, z) gira a partir da rotação atual. A documentação do Unity 6.3 diz que, sem o parâmetro de espaço, o giro é relativo ao próprio objeto (Space.Self). Arquivo Spinner.cs:

using UnityEngine;

public class Spinner : MonoBehaviour
{
    [SerializeField] private Vector3 degreesPerSecond = new Vector3(0f, 90f, 0f);
    [SerializeField] private Space space = Space.Self;

    private void Update()
    {
        transform.Rotate(degreesPerSecond * Time.deltaTime, space);
    }
}

A diferença entre Space.Self e Space.World aparece quando o objeto já está inclinado. Uma moeda tombada 30 graus girando em Space.Self no Y gira em volta do próprio eixo inclinado. Em Space.World, gira em volta do eixo vertical do mundo, como num carrossel. Para uma moeda colecionável, Space.World costuma ser o que você quer.

Combinar rotações também tem atalho: multiplicar quaternions. transform.rotation = transform.rotation * Quaternion.Euler(0f, 45f, 0f) gira 45 graus no eixo Y local. A ordem da multiplicação importa: a * b aplica b no espaço de a.

LookRotation: virar para um alvo

A torreta. Monte um objeto Turret com um filho Head, que é a parte que gira. Arquivo TurretAim.cs:

using UnityEngine;

public class TurretAim : MonoBehaviour
{
    [SerializeField] private Transform head;
    [SerializeField] private Transform target;

    private void Update()
    {
        if (target == null)
        {
            return;
        }

        Vector3 direction = target.position - head.position;
        direction.y = 0f;

        if (direction.sqrMagnitude < 0.0001f)
        {
            return;
        }

        head.rotation = Quaternion.LookRotation(direction, Vector3.up);
    }
}

Quaternion.LookRotation(direcao, cima) devolve a rotação que aponta o eixo Z do objeto (o forward, seta azul no editor) para a direção, mantendo o eixo Y o mais próximo possível de cima. O direction.y = 0f faz a torreta girar só na horizontal, sem inclinar para cima ou para baixo quando o jogador pula.

Para isso funcionar, o modelo da cabeça da torreta precisa ter o cano apontando para o eixo Z local. Se o modelo veio de outra ferramenta com o cano em X, coloque o modelo como filho de um objeto vazio, gire o filho para alinhar o cano com o Z do pai e mire o pai.

A checagem de distância pequena evita passar um vetor zero ao LookRotation, que a Unity responde com um aviso no Console.

Uma torreta que mira é o primeiro passo. Num jogo, ela precisa detectar o jogador, girar, atirar com o projétil saindo do cano certo, ter animação e morrer. Juntar rotação, física e spawn num inimigo completo é o tipo de integração que trava quem aprende sozinho, porque cada tutorial ensina uma peça.

Curso de Unity
Quer fazer seu primeiro jogo na Unity?

No curso de Unity 6 da CursoGame.Dev você vai do zero ao primeiro jogo, com um desafio prático em cada level e suporte individual no WhatsApp quando travar.

Conhecer o curso de Unity
Suporte no WhatsAppAcesso vitalícioGarantia de 7 dias

RotateTowards e Slerp para girar suave

Virar instantaneamente é estranho em quase todo inimigo. Para dar peso, a torreta gira com velocidade limitada:

using UnityEngine;

public class TurretAimSmooth : MonoBehaviour
{
    [SerializeField] private Transform head;
    [SerializeField] private Transform target;
    [SerializeField] private float turnSpeed = 120f;
    [SerializeField] private float fireAngle = 5f;

    public bool IsAimed { get; private set; }

    private void Update()
    {
        if (target == null)
        {
            return;
        }

        Vector3 direction = target.position - head.position;
        direction.y = 0f;

        if (direction.sqrMagnitude < 0.0001f)
        {
            return;
        }

        Quaternion desired = Quaternion.LookRotation(direction, Vector3.up);
        head.rotation = Quaternion.RotateTowards(head.rotation, desired, turnSpeed * Time.deltaTime);

        IsAimed = Quaternion.Angle(head.rotation, desired) <= fireAngle;
    }
}

Quaternion.RotateTowards(atual, alvo, grausMaximos) gira no máximo aquele número de graus por chamada. A documentação garante que ele não passa do alvo. Com turnSpeed * Time.deltaTime, a torreta gira 120 graus por segundo em qualquer FPS. O Quaternion.Angle mede quantos graus faltam, e o IsAimed deixa outro script decidir quando atirar.

Quaternion.Slerp(a, b, t) é a outra opção: interpola uma fração t entre duas rotações. Com t crescendo de 0 a 1 ao longo de um tempo fixo, ele faz uma animação de rotação com duração exata, como uma porta que abre em 0,4 segundo. Com uma fração fixa a cada frame (Slerp(atual, alvo, 5f * Time.deltaTime)), ele desacelera perto do alvo, e o resultado depende do FPS. As armadilhas são as mesmas do Lerp de posição, explicadas no post de Lerp desta série.

Quando usar cada um:

  • RotateTowards: velocidade angular constante, mira de inimigo, volante, qualquer coisa com "velocidade de giro".
  • Slerp com t de 0 a 1: animação de rotação com duração definida.
  • LookRotation direto: quando virar na hora é o certo, como uma câmera de mira em primeira pessoa.

Rotação em 2D com um ângulo só

Em 2D, só existe um eixo de rotação que importa: o Z. Não precisa de LookRotation; basta o ângulo da direção, calculado com Mathf.Atan2. Arquivo Aim2D.cs:

using UnityEngine;

public class Aim2D : MonoBehaviour
{
    [SerializeField] private Transform target;
    [SerializeField] private float turnSpeed = 360f;

    private void Update()
    {
        if (target == null)
        {
            return;
        }

        Vector2 direction = target.position - transform.position;
        float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
        Quaternion desired = Quaternion.Euler(0f, 0f, angle);

        transform.rotation = Quaternion.RotateTowards(transform.rotation, desired, turnSpeed * Time.deltaTime);
    }
}

Atan2(y, x) devolve o ângulo da direção em radianos, com 0 apontando para a direita. Multiplicado por Rad2Deg, vira graus. O sprite precisa estar desenhado olhando para a direita para isso bater. O mesmo cálculo com a posição do mouse é a base de mirar armas em jogos top-down, detalhado em mirar no mouse em 2D na Unity.

Erros comuns: gimbal lock e rotation.x

Escrever em transform.rotation.x. O x de um quaternion não é um ângulo, é um componente interno com valor entre -1 e 1. Mudar ele sem mexer nos outros gera rotações que não fazem sentido. Além disso, como rotation devolve uma cópia (é struct), atribuir só o x nem compila. Use Quaternion.Euler ou eulerAngles.

Ler eulerAngles, somar e escrever. Funciona até o ângulo dar a volta em 360 e a conversão devolver outra combinação. Guarde o ângulo numa variável sua.

Gimbal lock com Euler. Acumular rotações somando ângulos Euler em X, Y e Z leva a um ponto em que dois eixos se alinham e o objeto gira "errado". Trabalhe com Rotate, multiplicação de quaternions ou LookRotation.

Sprite 2D sumindo ao girar. Girar em X ou Y deixa o sprite de lado, invisível de frente. Em 2D, gire só em Z.

Esquecer Time.deltaTime. Rotate(0, 90, 0) por frame é 90 graus por frame, não por segundo. O conceito está em delta time e movimento por frame.

Próximo passo

Monte a torreta com TurretAimSmooth e faça um cubo de jogador andar em volta dela. Teste três valores de turnSpeed (60, 120 e 360) e veja como cada um muda a sensação de perigo. Depois adicione o disparo quando IsAimed for verdadeiro. Para reforçar a matemática de direções por trás de tudo isso, volte em vetores sem susto.

Perguntas frequentes

Como girar um objeto na Unity por código?

Para girar continuamente, use transform.Rotate(0f, graus * Time.deltaTime, 0f). Para definir uma rotação exata, atribua transform.rotation = Quaternion.Euler(x, y, z) com os ângulos em graus. Rotate gira em relação ao próprio objeto por padrão (Space.Self).

Como fazer um objeto olhar para outro na Unity?

Calcule a direção (alvo.position - transform.position) e use Quaternion.LookRotation(direcao) para obter a rotação que aponta o eixo Z do objeto para ela. Para girar aos poucos em vez de virar na hora, passe essa rotação para Quaternion.RotateTowards com um passo em graus.

Por que transform.rotation.x não é o ângulo em graus?

Porque transform.rotation é um Quaternion, e x, y, z e w são componentes internos dele, valores entre -1 e 1 sem relação direta com graus. Para ler ou escrever ângulos, use transform.eulerAngles ou transform.localEulerAngles.

Qual a diferença entre Slerp e RotateTowards?

RotateTowards gira no máximo um número de graus por chamada e para exatamente no alvo, o que dá velocidade angular constante. Slerp interpola por uma fração t entre duas rotações; usado com uma fração fixa a cada frame, ele desacelera perto do alvo.