Raycast na Unity: Como Funciona e Exemplos em C#
Unity raycast é uma consulta à física: você dispara uma linha invisível de um ponto numa direção e a Unity responde se ela acertou algum collider, qual, onde e a que distância. Nada se move e nada colide. É a ferramenta para tiro instantâneo (hitscan), checagem de chão, clique em objeto com o mouse, linha de visão de inimigo e qualquer pergunta do tipo "o que tem nessa direção?".
Este post monta três casos de jogo com Physics.Raycast e RaycastHit: tiro hitscan, checagem de chão e seleção com o mouse pelo Input System. Em todos, você vai usar LayerMask para filtrar o que o raio enxerga e Debug.DrawRay para ver o raio na tela, e no fim fica claro quando trocar o raycast por SphereCast. Código C# para Unity 6.3 LTS.
Unity raycast: o que é
Pense num laser apontador. Você define a origem (onde o laser sai), a direção (para onde aponta) e a distância máxima (até onde ele vai). A Unity percorre essa linha e devolve o primeiro collider que encontrar.
Três diferenças importantes em relação a um collider normal:
- É instantâneo. O raio vai do começo ao fim no mesmo instante. Não existe "bala viajando". Por isso funciona para tiro de arma de fogo, que no jogo não precisa de trajetória visível.
- Não tem espessura. É uma linha matemática. Um objeto pequeno pode passar entre dois raios.
- Ignora o collider em que começa. A referência do Physics.Raycast no Unity 6.3 diz que o raycast não detecta colliders que contêm a origem do raio. Se você dispara de dentro da cápsula do jogador, ela não atrapalha. Se dispara de dentro de uma parede, a parede também é ignorada.
Raycast é feito com vetores: origem é uma posição, direção é um vetor. Se esses conceitos estão confusos, o post de matemática para jogos com vetores explica direção, normalização e produto escalar com exemplos de jogo.
Physics.Raycast e RaycastHit
A forma que você mais vai usar recebe origem, direção, um out RaycastHit, distância e máscara:
bool acertou = Physics.Raycast(origem, direcao, out RaycastHit hit, distancia, mascara);
O retorno é true se acertou algo. Nesse caso, o hit vem preenchido:
Campo de RaycastHit | O que traz |
|---|---|
hit.collider | Collider atingido |
hit.transform | Transform do objeto atingido |
hit.rigidbody | Rigidbody do objeto, ou null |
hit.point | Ponto do impacto, em coordenadas do mundo |
hit.normal | Direção perpendicular à superfície no ponto atingido |
hit.distance | Distância da origem até o ponto |
Existem também versões que recebem um Ray (estrutura com origem e direção juntas), útil quando o raio vem da câmera. O último parâmetro opcional, QueryTriggerInteraction, decide se colliders trigger contam: UseGlobal (padrão, segue a opção Queries Hit Triggers de Project Settings > Physics), Ignore ou Collide. Para tiro, quase sempre você quer Ignore, senão a bala acerta a zona de checkpoint invisível.
Distância e LayerMask
Distância. O padrão é infinito. Sempre passe um valor: além de mais barato, evita a arma acertar algo a um quilômetro.
LayerMask. É o filtro mais importante. Declare no script e escolha as layers no Inspector:
[SerializeField] private LayerMask layersAtingiveis;
O raio só considera colliders das layers marcadas. Isso resolve dois problemas de uma vez: o tiro não acerta o próprio jogador (deixe a layer dele fora da máscara) e não acerta coisas que não interessam (efeitos, triggers decorativos).
Uma pegadinha de sobrecarga: Physics.Raycast(origem, direcao, mascara) compila, mas a máscara entra no lugar da distância (o int da máscara vira float). O raio fica com uma distância aleatória e sem filtro nenhum. Passe sempre distância e máscara, nessa ordem. O post de LayerMask e Layer Collision Matrix detalha como montar máscaras por código.
Ver o raio com Debug.DrawRay
Raycast invisível é difícil de depurar. Debug.DrawRay(origem, direcao * distancia, cor) desenha uma linha na janela Scene (e na Game, se o botão Gizmos estiver ligado). A linha dura um frame, então chame junto com o raycast, todo frame.
Arquivo ArmaHitscan.cs, tiro instantâneo saindo do centro da câmera, com o raio visível e um impulso no que for físico:
using UnityEngine;
using UnityEngine.InputSystem;
public class ArmaHitscan : MonoBehaviour
{
[SerializeField] private Camera cameraJogador;
[SerializeField] private float alcance = 100f;
[SerializeField] private float forcaImpacto = 8f;
[SerializeField] private LayerMask layersAtingiveis;
[SerializeField] private GameObject prefabImpacto;
private InputAction acaoAtirar;
private void Awake()
{
acaoAtirar = InputSystem.actions.FindAction("Attack");
}
private void Update()
{
Vector3 origem = cameraJogador.transform.position;
Vector3 direcao = cameraJogador.transform.forward;
Debug.DrawRay(origem, direcao * alcance, Color.red);
if (acaoAtirar.WasPressedThisFrame())
{
Atirar(origem, direcao);
}
}
private void Atirar(Vector3 origem, Vector3 direcao)
{
bool acertou = Physics.Raycast(
origem, direcao, out RaycastHit hit, alcance,
layersAtingiveis, QueryTriggerInteraction.Ignore);
if (!acertou)
{
return;
}
Instantiate(prefabImpacto, hit.point, Quaternion.LookRotation(hit.normal));
if (hit.rigidbody != null)
{
hit.rigidbody.AddForceAtPosition(direcao * forcaImpacto, hit.point, ForceMode.Impulse);
}
Debug.Log($"Acertou {hit.collider.name} a {hit.distance:F1} m");
}
}
A ação Attack vem no arquivo de ações padrão de projetos novos do Unity 6 (botão esquerdo do mouse e gatilho do controle). Se o seu projeto não tem, crie a ação ou troque por Mouse.current.leftButton.wasPressedThisFrame.
Para o chão, a mesma estrutura em poucas linhas. Arquivo ChecagemChao.cs:
using UnityEngine;
public class ChecagemChao : MonoBehaviour
{
[SerializeField] private float distancia = 0.2f;
[SerializeField] private LayerMask layersChao;
public bool NoChao { get; private set; }
private void FixedUpdate()
{
Vector3 origem = transform.position + Vector3.up * 0.1f;
NoChao = Physics.Raycast(origem, Vector3.down, distancia + 0.1f, layersChao);
Debug.DrawRay(origem, Vector3.down * (distancia + 0.1f), NoChao ? Color.green : Color.red);
}
}
O objeto com esse script fica nos pés do personagem. A origem sobe 10 cm para não começar dentro do chão (lembra que o raycast ignora o collider em que começa), e a distância compensa esses 10 cm. Raio verde é chão, vermelho é ar.
Tiro e chão com raycast cabem numa tarde. O que leva semanas é encaixar o raycast no resto do jogo: o tiro precisa conversar com o sistema de dano, a animação de recuo, o som, a munição e a interface, e a checagem de chão precisa conversar com pulo, coyote time e animação de queda. O curso de Unity monta essas conexões dentro de um jogo completo, que é onde quem estuda sozinho costuma travar.
Raycast a partir da câmera e do mouse
Para clicar num objeto 3D, o raio sai da câmera passando pelo pixel do mouse. Camera.ScreenPointToRay faz a conta e devolve um Ray. Com o Input System, a posição do mouse vem de Mouse.current.position.ReadValue().
Arquivo SelecionarComMouse.cs:
using UnityEngine;
using UnityEngine.InputSystem;
public class SelecionarComMouse : MonoBehaviour
{
[SerializeField] private Camera cameraPrincipal;
[SerializeField] private float alcance = 200f;
[SerializeField] private LayerMask layersSelecionaveis;
private Transform selecionado;
private void Update()
{
Mouse mouse = Mouse.current;
if (mouse == null || !mouse.leftButton.wasPressedThisFrame)
{
return;
}
Vector2 posicaoTela = mouse.position.ReadValue();
Ray raio = cameraPrincipal.ScreenPointToRay(posicaoTela);
if (Physics.Raycast(raio, out RaycastHit hit, alcance, layersSelecionaveis))
{
selecionado = hit.transform;
Debug.Log($"Selecionou {selecionado.name} em {hit.point}");
}
else
{
selecionado = null;
}
Debug.DrawRay(raio.origin, raio.direction * alcance, Color.yellow, 1f);
}
}
O quarto parâmetro de Debug.DrawRay (1 segundo) mantém a linha na Scene depois do clique, o que ajuda a ver por onde o raio passou. Esse script é a base de jogo de estratégia (clicar na unidade), point and click e de "clicar no chão para andar" (use hit.point como destino).
SphereCast, RaycastAll e NonAlloc
SphereCast. Arrasta uma esfera pela linha em vez de uma linha fina. Use quando o raio fino erra coisas que deveriam contar:
- Mira com tolerância (o jogador mirou "quase" no inimigo pequeno).
- Checagem de chão de um personagem largo, que com um raio no centro acha que está no ar ao pisar na borda.
- Detecção de obstáculo para um corpo com largura (a câmera em terceira pessoa que não deve entrar na parede).
bool acertou = Physics.SphereCast(origem, raioEsfera, direcao, out RaycastHit hit, alcance, mascara);
Como o raycast, o SphereCast não acerta colliders que já estão dentro da esfera no ponto de partida. Para isso existe Physics.OverlapSphere.
RaycastAll. Devolve todos os colliders na linha, não só o primeiro. Útil para bala que atravessa vários inimigos ou para laser que fura paredes finas. A ordem do array não é garantida: ordene por distance se precisar.
RaycastNonAlloc. O RaycastAll cria um array novo a cada chamada. Num laser disparado todo frame, isso vira lixo de memória. Physics.RaycastNonAlloc recebe um array seu, preenche e devolve quantos acertos houve:
private readonly RaycastHit[] acertos = new RaycastHit[8];
private void LaserPerfurante(Vector3 origem, Vector3 direcao)
{
int quantidade = Physics.RaycastNonAlloc(origem, direcao, acertos, 50f, layersAtingiveis);
for (int i = 0; i < quantidade; i++)
{
Debug.Log($"Laser passou por {acertos[i].collider.name}");
}
}
Leia só até quantidade: as posições depois dela guardam resultados antigos. Se o array encher, a documentação avisa que os acertos guardados não são necessariamente os mais próximos, então dimensione com folga.
Erros comuns com raycast
Posição no lugar de direção. Physics.Raycast(transform.position, alvo.position, ...) usa a posição do alvo como direção. O certo é alvo.position - transform.position.
Máscara no lugar da distância. Passada como terceiro argumento, a máscara vira a distância. Passe sempre os dois.
Origem dentro do collider do alvo. O raycast ignora o collider em que começa. Suba a origem um pouco, como no ChecagemChao.
Tiro acertando o próprio jogador. Isso acontece quando a origem sai fora da cápsula do jogador mas o raio cruza o corpo, comum em terceira pessoa. Deixe a layer do jogador fora da máscara.
Trigger bloqueando o tiro. Com Queries Hit Triggers ligado no projeto, zonas invisíveis param o raio. Passe QueryTriggerInteraction.Ignore.
Raycast físico no Update para coisa que depende de física. O resultado é o da última simulação. Para checagem de chão que decide pulo em FixedUpdate, faça o raycast no FixedUpdate.
Raycast demais por frame. Um raycast é barato, mas cem inimigos com cinco raios cada, todo frame, somam. Inimigo que procura o jogador não precisa olhar 60 vezes por segundo: um teste a cada 0,1 a 0,2 segundo é imperceptível para quem joga. Use um temporizador ou uma corrotina com WaitForSeconds, e faça antes os testes baratos (distância com sqrMagnitude, ângulo com produto escalar) para só disparar o raio quando eles passarem.
Próximo passo
Monte uma cena com a ArmaHitscan numa câmera, alguns cubos com Rigidbody e um alvo atrás de um trigger. Teste o tiro com QueryTriggerInteraction.Collide e depois com Ignore. Em seguida troque o raycast por SphereCast com raio 0,3 e veja a mira ficar mais tolerante.
Se você também usa Godot, o post de RayCast3D no Godot mostra os mesmos três casos com o node RayCast3D e a consulta direta ao espaço de física. Comparar as duas versões ajuda a separar o que é conceito (origem, direção, máscara) do que é API.
Perguntas frequentes
O que é RaycastHit na Unity?
RaycastHit é a estrutura que Physics.Raycast preenche quando o raio acerta algo. Ela traz o collider atingido, o ponto do impacto, a normal da superfície, a distância e o Rigidbody, quando existe. Você recebe ela com out RaycastHit hit na chamada.
Como fazer raycast só em uma layer na Unity?
Declare um [SerializeField] private LayerMask no script, marque as layers no Inspector e passe a máscara como parâmetro layerMask do Physics.Raycast. O raio ignora colliders das layers desmarcadas, inclusive o do próprio jogador se ele estiver numa layer fora da máscara.
Por que meu raycast não acerta nada?
As causas comuns são distância curta demais, LayerMask sem a layer do alvo, origem do raio dentro de um collider (o Physics.Raycast não detecta o collider em que começa), alvo sem collider, ou direção errada, como usar uma posição no lugar de uma direção. Desenhe o raio com Debug.DrawRay para ver o que está acontecendo.
Qual a diferença entre Raycast e SphereCast?
Raycast testa uma linha sem espessura. SphereCast arrasta uma esfera ao longo da mesma linha, então acerta coisas que passariam entre duas linhas finas. Use SphereCast para mira com tolerância, checagem de chão em bordas e detecção de obstáculo para um corpo com largura.



