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.
- Direção:
direction = mouseWorld - pivot.position. É o vetor do ombro até o cursor. - Ângulo:
Mathf.Atan2(direction.y, direction.x)devolve o ângulo em radianos, de -π a π, já com o quadrante certo. É por isso que se usaAtan2e nãoAtan:Atan(y/x)não distingue "direita em cima" de "esquerda embaixo". - Graus: multiplique por
Mathf.Rad2Deg. A Unity trabalha em graus no Inspector e noQuaternion.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.
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.



