Como Criar um Jogo de Terror na Unity: o Básico
Para criar um jogo de terror na Unity você precisa de cinco peças: controle em primeira pessoa, uma lanterna com bateria, um cenário escuro que continua legível, som 3D que diz de onde vem o perigo e eventos disparados por trigger, como a porta que range ou o susto no fim do corredor. Este guia monta cada uma em C# para o Unity 6.3 LTS com a URP, e no fim explica a diferença entre susto barato e tensão de verdade.
A parte técnica é pequena. O difícil num jogo de terror é a dosagem: escuro demais vira frustração, susto demais vira previsível. Por isso cada seção traz os números de partida que eu uso e o motivo deles.
O mínimo de um jogo de terror
Um protótipo de terror que assusta cabe numa casa com três cômodos e um corredor. A lista mínima:
- Jogador em primeira pessoa que anda devagar (correr tira a tensão).
- Lanterna que o jogador controla e que pode acabar.
- Ambiente escuro com névoa curta e pós-processamento discreto.
- Som ambiente constante e sons pontuais posicionados no espaço.
- Uma porta que abre com interação.
- Um evento de susto disparado quando o jogador cruza um ponto.
O que fica de fora: inimigo com IA, inventário, quebra-cabeças. Essas peças vêm depois que o clima funciona. Se você quer entender o lado de design, o que de fato dá medo e como o ritmo funciona, o post como fazer um jogo de terror indie trata disso sem depender de engine. Aqui o foco é a implementação na Unity.
Crie o projeto com o template Universal 3D. Ele já vem com a URP configurada, um Volume global na cena de exemplo e o Input System ativo.
Controle em primeira pessoa
O controle usa CharacterController, que resolve colisão com paredes e degraus sem física de corpo rígido. A câmera é filha do jogador, na altura dos olhos (y = 1,6). As ações Move e Look já existem no mapa Player do asset padrão de input do Unity 6.
Arquivo FirstPersonController.cs:
using UnityEngine;
using UnityEngine.InputSystem;
[RequireComponent(typeof(CharacterController))]
public class FirstPersonController : MonoBehaviour
{
[SerializeField] private Transform cameraPivot;
[SerializeField] private float walkSpeed = 2.2f;
[SerializeField] private float lookSensitivity = 0.12f;
[SerializeField] private float gravity = -15f;
private CharacterController controller;
private InputAction moveAction;
private InputAction lookAction;
private float pitch;
private float verticalVelocity;
private void Awake()
{
controller = GetComponent<CharacterController>();
moveAction = InputSystem.actions.FindAction("Player/Move");
lookAction = InputSystem.actions.FindAction("Player/Look");
Cursor.lockState = CursorLockMode.Locked;
}
private void Update()
{
Vector2 look = lookAction.ReadValue<Vector2>() * lookSensitivity;
transform.Rotate(Vector3.up, look.x);
pitch = Mathf.Clamp(pitch - look.y, -80f, 80f);
cameraPivot.localRotation = Quaternion.Euler(pitch, 0f, 0f);
Vector2 input = moveAction.ReadValue<Vector2>();
Vector3 move = transform.right * input.x + transform.forward * input.y;
if (controller.isGrounded && verticalVelocity < 0f)
{
verticalVelocity = -2f;
}
verticalVelocity += gravity * Time.deltaTime;
Vector3 velocity = move * walkSpeed + Vector3.up * verticalVelocity;
controller.Move(velocity * Time.deltaTime);
}
}
A velocidade de 2,2 m/s é de propósito: é uma caminhada cautelosa. Se o jogador consegue atravessar a casa em 10 segundos, nenhum corredor dá medo. Para um controle com corrida, pulo e agachar, veja o FPS controller em primeira pessoa na Unity; aqui ele foi reduzido ao que o terror pede.
Jogo de terror na Unity: lanterna com Spot Light
A lanterna é uma Spot Light filha da câmera, posicionada um pouco à direita e abaixo (x = 0,25, y = -0,2), como se estivesse na mão. Valores de partida no Inspector:
- Range: 18
- Spot Angle (Outer): 45, Inner: 25
- Intensity: 8 (com a unidade padrão da URP)
- Shadow Type: Soft Shadows
- Color: levemente amarelada, algo como RGB 255, 240, 210
O cone com borda suave e sombra é o que transforma a lanterna em ferramenta de medo: cada objeto que passa na frente projeta uma sombra que se mexe.
Arquivo Flashlight.cs:
using UnityEngine;
using UnityEngine.InputSystem;
[RequireComponent(typeof(Light))]
public class Flashlight : MonoBehaviour
{
[SerializeField] private float maxBattery = 120f;
[SerializeField] private float drainPerSecond = 1f;
[SerializeField] private float maxIntensity = 8f;
[SerializeField] private float lowBatteryThreshold = 0.2f;
[SerializeField] private AudioSource clickSound;
private Light spot;
private float battery;
private bool isOn = true;
public float Battery01 => battery / maxBattery;
private void Awake()
{
spot = GetComponent<Light>();
battery = maxBattery;
}
private void Update()
{
if (Keyboard.current != null && Keyboard.current.fKey.wasPressedThisFrame)
{
isOn = !isOn;
if (clickSound != null) clickSound.Play();
}
if (isOn && battery > 0f)
{
battery = Mathf.Max(0f, battery - drainPerSecond * Time.deltaTime);
}
spot.enabled = isOn && battery > 0f;
spot.intensity = CalculateIntensity();
}
private float CalculateIntensity()
{
float charge = Battery01;
if (charge > lowBatteryThreshold) return maxIntensity;
float dim = Mathf.Lerp(0.3f, 1f, charge / lowBatteryThreshold);
float flicker = Mathf.PerlinNoise(Time.time * 8f, 0f) > 0.25f ? 1f : 0.2f;
return maxIntensity * dim * flicker;
}
public void Recharge(float amount)
{
battery = Mathf.Min(maxBattery, battery + amount);
}
}
Dois minutos de bateria com a lanterna sempre acesa, e pilhas espalhadas pelo mapa chamando Recharge. Abaixo de 20% a luz enfraquece e começa a piscar com Mathf.PerlinNoise, que gera um tremor irregular e não um pisca-pisca de metrônomo. A bateria é a primeira decisão real que o jogador toma: economizar luz ou enxergar.
Iluminação escura: post processing e névoa no URP
O erro mais comum em terror é deixar a cena preta. Preto absoluto não dá medo, dá vontade de aumentar o brilho do monitor. A meta é: o jogador enxerga o contorno do cômodo sem a lanterna, mas não os detalhes.
Luz ambiente. Abra Window > Rendering > Lighting, aba Environment. Em Environment Lighting, troque Source para Color e use um azul bem escuro (algo como RGB 12, 14, 22). O azul frio no escuro e o amarelo quente da lanterna criam contraste de cor, que lê melhor que contraste de brilho.
Névoa. Na mesma aba, em Other Settings, marque Fog, modo Exponential Squared, cor igual à da luz ambiente e densidade entre 0,06 e 0,1. A névoa esconde o fim dos corredores e faz a lanterna "furar" o escuro. A névoa da URP é a névoa clássica por distância; névoa volumétrica nativa, com raios de luz visíveis no ar, é recurso da HDRP. Na URP, quem quer esse efeito recorre a pacotes da Asset Store ou a soluções da comunidade.
Pós-processamento. A URP usa o sistema de Volumes. Se a cena não tem um, crie em GameObject > Volume > Global Volume, crie um perfil em New e adicione os overrides em Add Override > Post-processing:
- Vignette: intensidade 0,35, escurece as bordas e puxa o olho para o centro.
- Film Grain: intensidade 0,2 a 0,3, esconde o banding do escuro e dá textura.
- Color Adjustments: Post Exposure entre -0,5 e 0, Saturation -20.
- Tonemapping: modo ACES ou Neutral, para o escuro não virar cinza lavado.
Confira se a câmera tem Post Processing marcado no componente Camera; sem isso, nenhum override aparece. Teste sempre com o jogo em tela cheia e com o brilho do monitor no padrão. Se puder, ofereça um ajuste de brilho no menu ligado ao Post Exposure.
Som 3D e ambiente
Som é metade do terror. Duas camadas:
Ambiente: um AudioSource na cena com Spatial Blend: 0 (2D), Loop marcado, volume baixo, tocando um drone grave ou vento. Ele nunca para, e é o silêncio dele (quando você desliga de propósito) que avisa que algo vai acontecer.
Sons posicionados: passos no andar de cima, uma torneira pingando, o rangido da porta. Cada um num AudioSource com Spatial Blend: 1 (3D), Min Distance 1 e Max Distance entre 10 e 20, Volume Rolloff: Logarithmic. O jogador ouve de qual lado vem o som porque o Audio Listener está na câmera. Use fone ao testar.
O post sobre áudio e sound design para jogos aprofunda camadas, mixagem e onde achar sons livres. Para o protótipo, 5 ou 6 sons bem escolhidos fazem mais que 50 genéricos.
Com lanterna, escuro e som funcionando, você já tem clima. O que vem a seguir é onde muita gente para: amarrar eventos, ritmo, inimigo, menu e salvar o progresso num jogo que alguém termina, e ajustar tudo a partir do teste com outras pessoas. Fazer isso sozinho, sem um roteiro de produção, costuma terminar num corredor bonito e nenhum jogo.
Portas, interação e eventos por trigger
A interação usa um raycast curto saindo da câmera. Quando o jogador aperta a tecla de interagir olhando para algo com o componente Interactable, o método é chamado.
Arquivo Interactor.cs (na câmera):
using UnityEngine;
using UnityEngine.InputSystem;
public class Interactor : MonoBehaviour
{
[SerializeField] private float reach = 2.2f;
[SerializeField] private LayerMask interactMask;
private InputAction interactAction;
private void Awake()
{
interactAction = InputSystem.actions.FindAction("Player/Interact");
}
private void Update()
{
if (!interactAction.WasPressedThisFrame()) return;
Ray ray = new Ray(transform.position, transform.forward);
if (Physics.Raycast(ray, out RaycastHit hit, reach, interactMask))
{
Door door = hit.collider.GetComponentInParent<Door>();
if (door != null)
{
door.Toggle();
}
}
}
}
Arquivo Door.cs (num objeto vazio no eixo da dobradiça, com a malha da porta como filha):
using System.Collections;
using UnityEngine;
public class Door : MonoBehaviour
{
[SerializeField] private float openAngle = 95f;
[SerializeField] private float openDuration = 1.6f;
[SerializeField] private AudioSource creak;
private bool isOpen;
private bool isMoving;
private Quaternion closedRotation;
private void Awake()
{
closedRotation = transform.localRotation;
}
public void Toggle()
{
if (isMoving) return;
StartCoroutine(Swing(!isOpen));
}
private IEnumerator Swing(bool open)
{
isMoving = true;
if (creak != null)
{
creak.pitch = Random.Range(0.9f, 1.1f);
creak.Play();
}
Quaternion from = transform.localRotation;
Quaternion to = open ? closedRotation * Quaternion.Euler(0f, openAngle, 0f) : closedRotation;
float t = 0f;
while (t < 1f)
{
t += Time.deltaTime / openDuration;
float eased = Mathf.SmoothStep(0f, 1f, t);
transform.localRotation = Quaternion.Slerp(from, to, eased);
yield return null;
}
isOpen = open;
isMoving = false;
}
}
A porta leva 1,6 segundo para abrir, não 0,3. Porta lenta com rangido é suspense; porta rápida é só porta. O pitch aleatório evita que o jogador decore o som.
Para o susto, um trigger: um BoxCollider com Is Trigger no meio do corredor. O jogador precisa de um Rigidbody ou CharacterController para disparar eventos de trigger; o CharacterController resolve.
Arquivo ScareTrigger.cs:
using UnityEngine;
using UnityEngine.Events;
[RequireComponent(typeof(Collider))]
public class ScareTrigger : MonoBehaviour
{
[SerializeField] private UnityEvent onTriggered;
[SerializeField] private float delay = 0f;
[SerializeField] private bool oneShot = true;
private bool fired;
private void OnTriggerEnter(Collider other)
{
if (fired && oneShot) return;
if (!other.CompareTag("Player")) return;
fired = true;
Invoke(nameof(Fire), delay);
}
private void Fire()
{
onTriggered.Invoke();
}
}
O UnityEvent deixa ligar qualquer coisa no Inspector: tocar um som 3D atrás do jogador, apagar a luz do cômodo, bater uma porta, ativar uma silhueta no fim do corredor que some 2 segundos depois. Um mesmo script cobre todos os eventos do jogo.
Tensão vs jump scare
Jump scare é o som alto com imagem súbita. Funciona uma vez e cobra caro: depois do terceiro, o jogador espera o próximo e o medo vira irritação. Tensão é a expectativa de que algo vai acontecer, e ela se constrói com as ferramentas que você já montou:
- Silêncio: desligue o som ambiente 5 segundos antes de um evento. O jogador percebe a ausência.
- Pista sonora: passos no andar de cima antes de o jogador subir. Ele sobe sabendo que algo está lá.
- Recurso escasso: bateria em 15% no corredor mais longo.
- Falso alarme: um gato derrubando uma lata. Alivia, e o próximo evento pega o jogador relaxado.
- Evento que não ataca: a silhueta que aparece e some. Nada aconteceu, e o jogador passa a olhar para todo canto.
Uma regra prática para o protótipo: no máximo um jump scare a cada 10 minutos de jogo, e sempre depois de um trecho de tensão, nunca do nada.
Erros comuns: escuro demais, susto previsível
- Escuro demais. Se o jogador não acha a porta sem a lanterna, o ambiente está escuro demais. Suba a luz ambiente até os contornos aparecerem.
- Névoa densa demais. Densidade acima de 0,15 esconde o próprio cômodo. A névoa deve engolir o fim do corredor, não a parede ao lado.
- Susto no mesmo ritmo. Se cada sala tem um evento na entrada, o jogador aprende o padrão. Varie posição, intervalo e tipo.
- Trigger que dispara de novo. Sem
oneShot, voltar pelo corredor repete o susto e quebra o efeito. - Som 2D em evento posicionado. Rangido com Spatial Blend 0 toca "dentro da cabeça" e o jogador não sabe para onde olhar.
- Pós-processamento sem Post Processing na câmera. O Volume existe, mas nada muda. Confira a caixa na câmera.
Próximo passo
Monte a casa com três cômodos usando cubos (greybox), coloque a lanterna, a névoa, dois sons posicionados, uma porta e um único evento de susto. Peça para alguém jogar sem você explicar nada e observe onde a pessoa hesita: é ali que o próximo evento deve morar. Depois, leia o guia de design de terror indie para planejar o ritmo da fase inteira e o de áudio para jogos para refinar a mixagem.
Perguntas frequentes
Qual render pipeline usar num jogo de terror na Unity?
A URP atende bem a maioria dos jogos de terror indie: tem luzes em tempo real, sombras, névoa por distância e pós-processamento com Vignette e Film Grain, e roda em máquinas modestas. A HDRP traz névoa volumétrica nativa e iluminação mais realista, mas pede hardware mais forte e mais tempo de ajuste.
Como fazer uma lanterna na Unity?
Crie uma Spot Light como filha da câmera do jogador, ajuste o ângulo do cone e o alcance, e ligue as sombras. Um script lê uma tecla para ligar e desligar e desconta bateria enquanto ela está acesa, reduzindo a intensidade quando a carga fica baixa.
Como fazer som 3D na Unity?
No AudioSource, coloque Spatial Blend em 1 (3D) e ajuste Min Distance e Max Distance. O som passa a mudar de volume e de lado conforme a posição do Audio Listener, que fica na câmera. Para passos e rangidos, é isso que faz o jogador saber de onde vem o perigo.
Jogo de terror é um bom primeiro projeto 3D?
É um dos melhores, porque o escopo é controlável: uma casa, uma lanterna, alguns sons e um evento bem colocado já assustam. O terror depende mais de ritmo, luz e som do que de muitos sistemas, então você aprende 3D sem precisar de combate, inventário ou IA complexa logo de cara.



