Unity 2D Tutorial: Como Fazer um Jogo 2D na Unity
Neste unity 2d tutorial você monta um jogo 2D completo de uma tela: o jogador anda de um lado para o outro no chão, blocos caem do céu cada vez mais rápido, encostou é game over e a tecla R reinicia. São quatro scripts curtos em C# do Unity 6, Rigidbody2D, colisão por trigger, spawner, pontuação e reinício de cena.
O foco não é só o código. É a ordem de montagem. A maioria dos iniciantes trava no meio do primeiro jogo 2D porque começa pelo menu, pela arte ou pelo sistema de pontos, e só depois descobre que o movimento não funciona. Aqui cada etapa termina com algo que você testa no Play antes de passar para a próxima.
O jogo 2D que vamos montar
Regras:
- O jogador fica no chão e só anda na horizontal (A/D ou setas).
- Blocos aparecem numa altura fixa, em posição horizontal aleatória, e caem.
- A velocidade de queda aumenta com o tempo.
- A pontuação é o tempo sobrevivido em segundos.
- Encostou num bloco: o jogo congela, aparece "Game Over" e R reinicia.
Escolhi um jogo de desviar e não um de plataforma de propósito. Plataforma exige pulo, ground check e ajuste fino de gravidade, e isso afoga o primeiro projeto. Desviar ensina os mesmos fundamentos (física, colisão, instanciar objetos, estado de jogo) com metade das armadilhas. Se você quer outras ideias desse tamanho, a lista de ideias de jogos simples para iniciantes tem várias.
A ordem de montagem que vamos seguir:
- Projeto e cena.
- Jogador que anda e não sai da tela.
- Um obstáculo que cai.
- Spawner que cria vários.
- Colisão e game over.
- Pontuação e reinício.
Criar o projeto Universal 2D
No Unity Hub, vá em Projects > New project, confira que a versão é a Unity 6.3 LTS e escolha o template Universal 2D. Ele vem com a URP configurada com o renderizador 2D e com a câmera em modo ortográfico, que é o que um jogo 2D precisa. Nomeie como Desvia e crie.
Com o projeto aberto, crie no Project as pastas Scripts, Sprites e Prefabs dentro de Assets. Parece burocracia, mas com quatro scripts e um prefab você já agradece.
Selecione a Main Camera e confira no Inspector: Projection em Orthographic e Size em 5. Com size 5, a câmera mostra 10 unidades de altura (de -5 a 5 no eixo Y). Em tela 16:9, isso dá por volta de 17,8 unidades de largura, então o X visível vai de cerca de -8,9 a 8,9. Esses números guiam onde colocar paredes e spawner.
Importar sprites e montar a cena
Você pode usar formas prontas ou arte sua.
Com formas prontas: na Hierarchy, botão direito, 2D Object > Sprites > Square. Serve para jogador, chão e obstáculos, mudando a cor no Sprite Renderer.
Com arte sua: arraste os PNGs para a pasta Sprites. No projeto 2D eles já entram com Texture Type em Sprite (2D and UI). Se for pixel art, selecione o arquivo e ajuste no Inspector: Pixels Per Unit igual ao tamanho do tile (ex.: 16 para sprites de 16x16), Filter Mode em Point (no filter) e Compression em None. Clique em Apply. Sem isso, pixel art fica borrada.
Monte a cena:
- Jogador: um Square na posição
(0, -4, 0). Renomeie paraJogador. - Paredes invisíveis: dois objetos vazios (Create Empty) chamados
ParedeEsquerdaem(-9.5, 0, 0)eParedeDireitaem(9.5, 0, 0). Em cada um, adicione um Box Collider 2D com Size(1, 12). Sem Sprite Renderer, elas não aparecem, mas a física enxerga. - Obstáculo: um Square vermelho em
(0, 6, 0), escala(0.8, 0.8, 1). Renomeie paraObstaculo. Vamos transformá-lo em prefab daqui a pouco.
Script do jogador com Rigidbody2D e Input System
No Jogador, adicione Rigidbody 2D e Box Collider 2D. No Rigidbody 2D:
- Body Type: Dynamic (a física resolve a batida nas paredes).
- Gravity Scale:
0(ele não cai, fica no chão por definição). - Constraints: marque Freeze Position Y e Freeze Rotation Z.
Crie o script em Create > Scripting > MonoBehaviour Script. Arquivo JogadorDesvio.cs:
using UnityEngine;
using UnityEngine.InputSystem;
public class JogadorDesvio : MonoBehaviour
{
[SerializeField] private Rigidbody2D rb;
[SerializeField] private GerenciadorDeJogo gerenciador;
[SerializeField] private float velocidade = 8f;
private InputAction acaoMover;
private float direcaoX;
private void Awake()
{
acaoMover = InputSystem.actions.FindAction("Move");
}
private void Update()
{
direcaoX = acaoMover.ReadValue<Vector2>().x;
}
private void FixedUpdate()
{
rb.linearVelocity = new Vector2(direcaoX * velocidade, 0f);
}
private void OnTriggerEnter2D(Collider2D outro)
{
if (outro.CompareTag("Obstaculo"))
{
gerenciador.FimDeJogo();
}
}
}
Por que assim:
- A ação Move vem pronta no asset
InputSystem_Actionsdos projetos novos da Unity 6. Ela devolve umVector2; usamos só ox. - Input é lido no
Update, física é aplicada noFixedUpdate. Misturar os dois causa movimento irregular. linearVelocityé a propriedade de velocidade do Rigidbody2D na Unity 6. Código de tutorial antigo comvelocitygera aviso de API obsoleta.- Como o corpo é Dynamic, as paredes invisíveis seguram o jogador sem nenhum
Mathf.Clamp.
O script ainda não compila porque GerenciadorDeJogo não existe. Tudo bem: crie os outros scripts e o erro some. Se quiser se aprofundar em aceleração, atrito e pulo, o post de movimento de personagem 2D na Unity vai além do básico.
Obstáculos com spawner e colisão
O obstáculo
No objeto Obstaculo:
- Adicione Box Collider 2D e marque Is Trigger.
- Adicione Rigidbody 2D com Body Type em Kinematic. Kinematic se move pela velocidade que você define, sem gravidade e sem ser empurrado.
- No topo do Inspector, Tag > Add Tag, crie
Obstaculoe aplique.
Arquivo Obstaculo.cs:
using UnityEngine;
public class Obstaculo : MonoBehaviour
{
[SerializeField] private Rigidbody2D rb;
[SerializeField] private float velocidadeQueda = 4f;
[SerializeField] private float limiteInferiorY = -7f;
public void DefinirVelocidade(float novaVelocidade)
{
velocidadeQueda = novaVelocidade;
}
private void Start()
{
rb.linearVelocity = Vector2.down * velocidadeQueda;
}
private void Update()
{
if (transform.position.y < limiteInferiorY)
{
Destroy(gameObject);
}
}
}
Anexe ao obstáculo, arraste o Rigidbody 2D para o campo Rb e aperte Play: o bloco cai e some ao sair da tela. Funcionou? Arraste o Obstaculo da Hierarchy para a pasta Prefabs e apague o da cena.
O spawner
Crie um objeto vazio Spawner em (0, 6, 0). Arquivo Spawner.cs:
using UnityEngine;
public class Spawner : MonoBehaviour
{
[SerializeField] private Obstaculo prefabObstaculo;
[SerializeField] private float intervalo = 0.8f;
[SerializeField] private float metadeLargura = 8f;
[SerializeField] private float velocidadeInicial = 4f;
[SerializeField] private float aumentoPorSegundo = 0.15f;
private float cronometro;
private float tempoDeJogo;
private void Update()
{
tempoDeJogo += Time.deltaTime;
cronometro += Time.deltaTime;
if (cronometro < intervalo)
{
return;
}
cronometro = 0f;
float x = Random.Range(-metadeLargura, metadeLargura);
Vector3 posicao = new Vector3(x, transform.position.y, 0f);
Obstaculo novo = Instantiate(prefabObstaculo, posicao, Quaternion.identity);
novo.DefinirVelocidade(velocidadeInicial + tempoDeJogo * aumentoPorSegundo);
}
}
Arraste o prefab para o campo Prefab Obstaculo. Como DefinirVelocidade é chamado logo depois do Instantiate e o Start do obstáculo só roda depois, a velocidade nova já vale na primeira queda. A dificuldade sobe sozinha: a cada segundo, os blocos caem 0,15 unidade por segundo mais rápido.
Game over, pontuação e reinício da cena
Crie a interface:
- Hierarchy, botão direito, menu UI (Canvas), item de texto do TextMeshPro. Aceite Import TMP Essentials na primeira vez. Renomeie para
TextoPontose ancore no topo. - Crie um Panel no mesmo Canvas, renomeie para
PainelGameOvere coloque dentro dele um texto "Game Over. Aperte R".
Crie um objeto vazio Gerenciador. Arquivo GerenciadorDeJogo.cs:
using TMPro;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.SceneManagement;
public class GerenciadorDeJogo : MonoBehaviour
{
[SerializeField] private TMP_Text textoPontos;
[SerializeField] private GameObject painelGameOver;
private float pontos;
private bool acabou;
private void Start()
{
Time.timeScale = 1f;
painelGameOver.SetActive(false);
}
private void Update()
{
if (acabou)
{
Keyboard teclado = Keyboard.current;
if (teclado != null && teclado.rKey.wasPressedThisFrame)
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
return;
}
pontos += Time.deltaTime;
textoPontos.text = Mathf.FloorToInt(pontos).ToString();
}
public void FimDeJogo()
{
if (acabou)
{
return;
}
acabou = true;
Time.timeScale = 0f;
painelGameOver.SetActive(true);
}
}
Ligue as referências no Inspector: texto e painel no Gerenciador; Gerenciador no campo do Jogador.
Dois detalhes que fazem o reinício funcionar:
Time.timeScale = 0fcongela física e tudo que usaTime.deltaTime. OUpdatecontinua rodando, por isso a leitura da tecla R funciona com o jogo parado. OStartvolta o valor para 1 quando a cena recarrega.SceneManager.LoadScenecom índice só funciona se a cena estiver na lista de cenas do build. Abra File > Build Profiles, selecione o perfil ativo, abra a Scene List e clique em Add Open Scenes.
Pronto: aperte Play e jogue. Você fechou um jogo 2D com começo, meio, fim e recomeço.
Um jogo de uma tela com quatro scripts é administrável. O salto difícil vem quando o projeto pede personagem animado, várias fases, menu, save, som e câmera, tudo conversando sem virar um emaranhado. Organizar isso numa estrutura que cresce sem quebrar é a parte que quase ninguém aprende sozinho em vídeo solto.
Erros comuns no primeiro jogo 2D na Unity
O jogador atravessa a parede: a parede não tem collider, ou o Rigidbody 2D do jogador está em Kinematic. Kinematic não é parado por colisores estáticos.
O obstáculo não cai: faltou arrastar o Rigidbody 2D para o campo Rb do script, ou o Body Type ficou Static.
Encosta no bloco e nada acontece: confira três coisas. A tag do prefab é Obstaculo (com a mesma grafia do código)? O collider do obstáculo está com Is Trigger? O jogador tem Rigidbody 2D? Trigger 2D só dispara se pelo menos um dos dois objetos tiver Rigidbody 2D.
R não reinicia: a cena não está na Scene List do Build Profile. O Console mostra um erro dizendo que a cena não pode ser carregada.
O jogo reinicia congelado: faltou o Time.timeScale = 1f no Start.
Blocos se acumulam abaixo da tela: o limiteInferiorY está abaixo do que eles alcançam, ou o script não está no prefab. Veja na Hierarchy durante o Play se os clones somem.
Sprites borrados: arte em pixel com Filter Mode em Bilinear. Troque para Point (no filter).
Próximo passo depois deste unity 2d tutorial
Agora melhore o mesmo jogo antes de começar outro. Cada item ensina um assunto novo sem perder o que você já tem:
- Melhor pontuação salva com
PlayerPrefs. - Tela inicial com botão Jogar, numa segunda cena.
- Som ao desviar por pouco e no game over.
- Partículas quando o jogador morre.
- Arte no lugar dos quadrados, com animação de corrida.
Se ainda está pesando se a Unity é a melhor ferramenta para o seu jogo 2D, o comparativo de melhor engine para jogos 2D põe Unity, Godot e outras lado a lado para esse tipo de projeto.
Perguntas frequentes
Qual template escolher para fazer jogo 2D na Unity?
Use o Universal 2D no Unity Hub. Ele cria um projeto vazio com a URP e o renderizador 2D já configurados, câmera ortográfica e importação de imagens como Sprite. É o ponto de partida recomendado para jogo 2D na Unity 6.
Preciso saber desenhar para fazer um jogo 2D na Unity?
Não para começar. A Unity cria formas simples (quadrado, círculo, triângulo) direto pelo menu 2D Object, e dá para fazer o jogo inteiro com elas. Arte entra depois, quando a mecânica já está divertida.
Por que meu jogo 2D na Unity não reinicia com SceneManager.LoadScene?
Quase sempre a cena não está na Scene List do Build Profile ativo, ou o Time.timeScale ficou em zero depois do game over. Adicione a cena em File > Build Profiles > Scene List e volte o timeScale para 1 no Start.
Rigidbody2D Dynamic ou Kinematic para o jogador?
Dynamic quando você quer que a física resolva colisões com paredes e chão, como neste tutorial. Kinematic quando você mesmo controla toda a posição por código e a física só serve para detectar contato.



