Voltar para o Blog
Quest Log

Mirar no Mouse na Unity 2D: Girar Arma e Personagem

Personagem 2D em pixel art segurando uma arma apontada para uma mira de cursor no canto da tela

Unity rotate towards mouse 2D: gire a arma para o cursor com Mathf.Atan2 e transform.right, faça o flip sem inverter a arma e mire também com o analógico.

Ferramenta:UnityC#

Mirar no Mouse na Unity 2D: Girar Arma e Personagem

Para fazer um objeto girar em direção ao mouse em 2D na Unity (o "unity rotate towards mouse 2d" das buscas), você precisa de três passos: converter o cursor para posição no mundo, calcular a direção do objeto até ele e transformar essa direção em ângulo com Mathf.Atan2. Aplique o ângulo em Z com Quaternion.Euler(0, 0, ângulo) e a arma aponta para o cursor.

O que vem depois disso é o que todo tutorial curto pula: a arma fica de cabeça para baixo quando você mira para a esquerda, o personagem vira mas a arma vira ao contrário, e no controle não existe cursor. Este guia resolve os três com código para Unity 6.3 LTS e o Input System, e o mesmo script mira no mouse e no analógico.

Unity rotate towards mouse 2d: a conta

Pense na arma como um ponteiro de relógio preso no ombro do personagem. Você quer o ângulo entre o eixo X (o "3 horas") e a linha que vai do ombro até o cursor.

  1. Direção: direction = mouseWorld - pivot.position. É o vetor do ombro até o cursor.
  2. Ângulo: Mathf.Atan2(direction.y, direction.x) devolve o ângulo em radianos, de -π a π, já com o quadrante certo. É por isso que se usa Atan2 e não Atan: Atan(y/x) não distingue "direita em cima" de "esquerda embaixo".
  3. Graus: multiplique por Mathf.Rad2Deg. A Unity trabalha em graus no Inspector e no Quaternion.Euler.

Repare na ordem dos argumentos: Atan2(y, x), Y primeiro. Trocar a ordem é o erro mais comum, e o sintoma é a arma girar espelhada em relação ao cursor.

A conta pressupõe que o sprite da arma aponta para a direita na arte original. Se a sua arma foi desenhada apontando para cima, some -90 ao ângulo ou redesenhe o sprite; padronizar "toda arma aponta para a direita" economiza esse ajuste em todo o projeto.

Posição do mouse no mundo

O cursor vem em pixels de tela. Para virar ponto no mundo 2D:

Vector2 screen = Mouse.current.position.ReadValue();
Vector3 world = cam.ScreenToWorldPoint(new Vector3(screen.x, screen.y, -cam.transform.position.z));
world.z = 0f;

O Z passado é a distância da câmera até o plano do jogo. Com câmera ortográfica em z = -10, isso é 10. Passar só o Vector2 (z = 0) funciona por acaso em câmera ortográfica para X e Y, mas devolve z = -10 e quebra qualquer conta que use o vetor 3D. O post sobre posição do mouse com o Input System detalha esse bug e o caso 3D.

Atan2 e ângulo em graus

Monte a hierarquia assim: Player (com o sprite do corpo) > WeaponPivot (objeto vazio na altura do ombro) > Weapon (sprite da arma, deslocado para a direita do pivot, com o cabo no centro do pivot). O script gira o WeaponPivot; a arma acompanha porque é filha.

Arquivo AimAtMouse2D.cs:

using UnityEngine;
using UnityEngine.InputSystem;

public class AimAtMouse2D : MonoBehaviour
{
    [SerializeField] private Camera cam;
    [SerializeField] private Transform weaponPivot;

    public Vector2 AimDirection { get; private set; } = Vector2.right;

    private void Awake()
    {
        if (cam == null)
        {
            cam = Camera.main;
        }
    }

    private void Update()
    {
        if (Mouse.current == null)
        {
            return;
        }

        Vector2 screen = Mouse.current.position.ReadValue();
        Vector3 mouseWorld = cam.ScreenToWorldPoint(new Vector3(screen.x, screen.y, -cam.transform.position.z));
        Vector2 direction = (Vector2)(mouseWorld - weaponPivot.position);

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

        AimDirection = direction.normalized;
        float angle = Mathf.Atan2(AimDirection.y, AimDirection.x) * Mathf.Rad2Deg;
        weaponPivot.rotation = Quaternion.Euler(0f, 0f, angle);
    }
}

O if (direction.sqrMagnitude < 0.0001f) evita a arma girar maluca quando o cursor está exatamente em cima do pivot: com vetor quase zero, Atan2 devolve ângulos instáveis. A propriedade AimDirection fica pública (só leitura) para o script de tiro usar como direção da bala.

Girar a arma com transform.right

Existe um atalho que dispensa o ângulo:

weaponPivot.right = AimDirection;

Atribuir a transform.right gira o objeto para o eixo X local apontar para a direção dada. Em 2D, com a direção no plano XY, o resultado é o mesmo do Quaternion.Euler com Atan2. Uso transform.right quando só preciso apontar, e o ângulo explícito quando preciso dele para outra coisa: limitar a mira (um canhão que só gira entre 0 e 90 graus), interpolar com Mathf.MoveTowardsAngle para uma torreta lenta, ou decidir o flip. Quer uma torreta que gira devagar em vez de travar no cursor? Troque a atribuição direta por Mathf.MoveTowardsAngle(anguloAtual, anguloAlvo, velocidadeEmGraus * Time.deltaTime). A função já trata a volta de 359 para 0 grau pelo caminho curto, coisa que um Mathf.Lerp comum erra e faz a torreta girar a volta inteira.

Sobre Quaternion.LookRotation em 2D: a versão de um argumento aponta o eixo Z para a direção, o que deita o sprite de lado e ele some. A versão de dois argumentos, Quaternion.LookRotation(Vector3.forward, direction), mantém o Z para a câmera e gira o eixo Y para a direção. Serve se o seu sprite aponta para cima na arte. Para sprite que aponta para a direita, transform.right ou Atan2 são mais diretos.

Mirar é um pedaço do tiro. O tiro em si precisa de projétil com pool, recarga, recuo, feedback de acerto, inimigos que respondem e uma câmera que não enjoa. Cada um é simples sozinho; o difícil é fazer tudo funcionar junto num jogo que dá vontade de jogar, que é onde o curso de Unity concentra a prática, com projetos completos em vez de mecânicas soltas.

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

Flip do personagem sem inverter a arma

Dois problemas aparecem quando o jogador mira para a esquerda.

A arma fica de cabeça para baixo. Girar 180 graus em Z é rodar o sprite como uma roda, e a coronha passa para cima. A correção é espelhar o eixo Y local da arma quando o ângulo passa de 90 graus para qualquer lado.

O corpo e a arma brigam. Se você vira o personagem com transform.localScale = new Vector3(-1, 1, 1), todos os filhos são espelhados também, incluindo o WeaponPivot. A rotação de um filho de pai espelhado inverte o sentido, e a arma passa a apontar para o lado oposto do cursor. A saída é virar só o sprite do corpo com SpriteRenderer.flipX, deixando o Transform do Player sem escala negativa.

Arquivo AimFlip2D.cs, que complementa o anterior:

using UnityEngine;

public class AimFlip2D : MonoBehaviour
{
    [SerializeField] private AimAtMouse2D aim;
    [SerializeField] private SpriteRenderer bodyRenderer;
    [SerializeField] private Transform weapon;

    private void LateUpdate()
    {
        bool aimingLeft = aim.AimDirection.x < 0f;

        bodyRenderer.flipX = aimingLeft;

        Vector3 scale = weapon.localScale;
        scale.y = aimingLeft ? -Mathf.Abs(scale.y) : Mathf.Abs(scale.y);
        weapon.localScale = scale;
    }
}

Usar x < 0 é o mesmo que "ângulo acima de 90 ou abaixo de -90", só que mais legível. Espelhar o localScale.y da Weapon (e não do pivot) mantém a rotação intacta e corrige só o desenho. SpriteRenderer.flipY também funciona para a arma, mas não espelha filhos como o ponto de saída da bala; com escala, o FirePoint filho acompanha.

O LateUpdate garante que o flip acontece depois da mira do frame. Se houver Animator no corpo controlando flipX, decida quem manda: ou o script, ou a animação. Os dois juntos piscam.

Mirar com analógico do controle

No controle não tem cursor. A direção vem direto do analógico direito, e o resto do código não muda. O asset padrão da Unity 6 tem a action Look com o analógico direito, mas no teclado e mouse ela lê <Mouse>/delta (movimento, não posição), o que não serve para mira em 2D. Por isso o script escolhe a fonte: se o analógico está inclinado, ele manda; se o mouse se mexeu, o mouse manda.

Arquivo AimHybrid2D.cs:

using UnityEngine;
using UnityEngine.InputSystem;

public class AimHybrid2D : MonoBehaviour
{
    [SerializeField] private Camera cam;
    [SerializeField] private Transform weaponPivot;
    [SerializeField] private float stickDeadzone = 0.3f;

    private bool usingStick;

    public Vector2 AimDirection { get; private set; } = Vector2.right;

    private void Update()
    {
        Gamepad pad = Gamepad.current;
        if (pad != null)
        {
            Vector2 stick = pad.rightStick.ReadValue();
            if (stick.magnitude > stickDeadzone)
            {
                usingStick = true;
                AimDirection = stick.normalized;
            }
        }

        Mouse mouse = Mouse.current;
        if (mouse != null && mouse.delta.ReadValue().sqrMagnitude > 1f)
        {
            usingStick = false;
        }

        if (!usingStick && mouse != null)
        {
            Vector2 screen = mouse.position.ReadValue();
            Vector3 world = cam.ScreenToWorldPoint(new Vector3(screen.x, screen.y, -cam.transform.position.z));
            Vector2 direction = (Vector2)(world - weaponPivot.position);
            if (direction.sqrMagnitude > 0.0001f)
            {
                AimDirection = direction.normalized;
            }
        }

        float angle = Mathf.Atan2(AimDirection.y, AimDirection.x) * Mathf.Rad2Deg;
        weaponPivot.rotation = Quaternion.Euler(0f, 0f, angle);
    }
}

Se preferir ler pelo asset em vez de Gamepad.current, crie uma action própria (Aim, por exemplo) com um binding só de <Gamepad>/rightStick, troque pad.rightStick.ReadValue() por aimAction.ReadValue<Vector2>() e ganhe remapeamento. Quando o jogador solta o analógico, AimDirection mantém o último valor, e a arma não volta para a direita do nada. Com o AimFlip2D lendo este componente em vez do anterior (troque o tipo do campo), o flip funciona igual no mouse e no controle.

Erros comuns

Arma aponta 90 graus fora. Sprite desenhado apontando para cima. Some -90 ao ângulo ou gire a arte.

Mira espelhada. Atan2(x, y) em vez de Atan2(y, x), ou o pai com escala X negativa.

A mira treme quando o personagem anda. A câmera segue o personagem no LateUpdate e a mira roda no Update com a posição da câmera do frame anterior. Mova o cálculo para o LateUpdate ou mire depois da câmera.

Arma em pixel art fica serrilhada ao girar. Sprite de pixel art girado em ângulos livres deforma os pixels, e com a Pixel Perfect Camera fica ainda mais visível. Duas saídas: arredonde o ângulo para passos fixos (Mathf.Round(angle / 22.5f) * 22.5f dá 16 direções) ou desenhe a arma em poucas direções e troque o sprite conforme o ângulo, como os jogos de 16 bits faziam. O arredondamento resolve em uma linha; os sprites dedicados ficam mais bonitos, mas custam arte.

Bala sai torta. O script de tiro usa transform.right do Player em vez de AimDirection.

Próximo passo

Ligue o AimDirection num script de tiro com projétil e taxa de disparo, e teste com mouse e controle. Se a parte de vetores ainda estiver nebulosa, o post de matemática para jogos: vetores explica direção, normalização e magnitude. Para ver a mesma mecânica num jogo inteiro em outra engine, o jogo de tiro top-down na Godot usa a mesma conta de atan2.

Perguntas frequentes

Como girar um objeto para o mouse na Unity 2D?

Pegue a posição do mouse no mundo com ScreenToWorldPoint, subtraia a posição do objeto para ter a direção, calcule o ângulo com Mathf.Atan2(direção.y, direção.x) * Mathf.Rad2Deg e aplique com Quaternion.Euler(0, 0, ângulo). Também funciona atribuir a direção a transform.right.

Quaternion.LookRotation funciona em 2D?

Não do jeito que a maioria espera. LookRotation aponta o eixo Z para a direção, o que vira o sprite de lado em 2D. O truque que funciona é Quaternion.LookRotation(Vector3.forward, direção), que mantém o Z e gira o eixo Y (o "para cima" do sprite) para a direção.

Por que a arma fica de cabeça para baixo quando miro para a esquerda?

Porque girar 180 graus em Z vira o sprite. A correção é espelhar o eixo Y da arma quando o ângulo passa de 90 graus para qualquer lado, com localScale.y negativo ou SpriteRenderer.flipY.

Como mirar com o analógico do controle na Unity?

Leia o analógico direito como Vector2 e use esse vetor como direção, no lugar de mouse menos posição. A conta do ângulo é a mesma. Ignore valores abaixo de uma zona morta e mantenha a última direção quando o jogador solta o analógico.