Crossy Road é um daqueles jogos geniais. É simples, viciante, e tem um visual icônico. Para quem está começando na Unity, tentar replicar aquele visual e, principalmente, aquela sensação de movimento, pode ser um pesadelo.
Se você tentar usar um Rigidbody ou um CharacterController padrão, o personagem vai se mover livremente. Mas em Crossy Road, o personagem “salta” de um ponto para o outro, como se estivesse num tabuleiro (ou grid).
Como diabos eles fazem isso?
A boa notícia: não é mágica. É uma combinação inteligente de três coisas: uma câmera específica, um script de movimento simples e um pequeno truque de “juice” para fazer o pulo parecer gostoso.
Vamos quebrar o código e a lógica para você criar o MVP dessa mecânica agora.
Passo 1: A Câmera “3D Falsa” (Ortográfica)
O primeiro segredo do visual do jogo é a câmera. Embora o jogo seja 3D, ele não usa a câmera em “Perspectiva” padrão.
- Crie um novo projeto 3D na Unity.
- Na sua
Main Camera, vá até o Inspector. - Encontre a propriedade Projection e mude de
PerspectiveparaOrthographic. - Ajuste a Rotation da câmera para ter uma visão isométrica. Um bom ponto de partida é: X = 30, Y = 45, Z = 0.
- Use a propriedade Size para controlar o zoom (quanto menor o número, mais “perto” a câmera fica).
Pronto. Agora, qualquer objeto 3D que você colocar na cena terá aquele visual “achatado” e estilizado, sem distorção de perspectiva.
Passo 2: O Cérebro (O Script de Movimento em Grid)
Este é o coração da mecânica. Não vamos usar física. Vamos controlar a posição do jogador diretamente, pulando de um ponto a outro.
- Crie um
Cube3D (ou importe seu personagem) e coloque-o na posição(0, 0, 0). Vamos chamá-lo de “Jogador”. - Crie um novo script C# chamado
MoveGride adicione-o ao seu “Jogador”. - Abra o script. A lógica é a seguinte:
- Não queremos movimento contínuo (
GetKey). Queremos uma ação única por aperto (GetKeyDown). - Quando o jogador apertar “W”, queremos que ele se mova exatamente 1 unidade para frente (
Vector3.forward). - Quando apertar “A”, 1 unidade para a esquerda (
Vector3.left), e assim por diante.
- Não queremos movimento contínuo (
Nosso primeiro rascunho de código dentro do Update() poderia ser este:
// DENTRO DO UPDATE - VERSÃO RUIM (INSTANTÂNEA)
void Update()
{
if (Input.GetKeyDown(KeyCode.W))
{
transform.position += Vector3.forward;
}
if (Input.GetKeyDown(KeyCode.A))
{
transform.position += Vector3.left;
}
// ... etc para S e D
}
Isso funciona. O personagem vai “teleportar” 1 unidade para frente. Mas não é “gostoso”. Não tem peso. Falta o “pulo”.
Passo 3: O “Juice” (O Pulo Suave com Coroutine e Lerp)
Para fazer o movimento parecer um “pulo”, o personagem precisa levar um curto período de tempo (ex: 0.2 segundos) para ir da Posição A para a Posição B.
A melhor forma de fazer isso é com uma Coroutine. Uma coroutine é uma função que pode ser pausada e continuada.
Vamos reescrever nosso script MoveGrid para usar essa técnica:
using System.Collections;
using UnityEngine;
public class MoveGrid : MonoBehaviour
{
// O tempo que o pulo leva, em segundos
public float moveDuration = 0.2f;
// Para evitar que o jogador aperte o botão várias vezes enquanto já está pulando
private bool canMove = true;
void Update()
{
// Só checa por input se o jogador puder se mover
if (canMove)
{
if (Input.GetKeyDown(KeyCode.W))
{
StartCoroutine(MovePlayer(Vector3.forward));
}
else if (Input.GetKeyDown(KeyCode.S))
{
StartCoroutine(MovePlayer(Vector3.back));
}
else if (Input.GetKeyDown(KeyCode.A))
{
StartCoroutine(MovePlayer(Vector3.left));
}
else if (Input.GetKeyDown(KeyCode.D))
{
StartCoroutine(MovePlayer(Vector3.right));
}
}
}
// A Coroutine mágica que move o jogador suavemente
private IEnumerator MovePlayer(Vector3 direction)
{
// Bloqueia o movimento até que o pulo termine
canMove = false;
// Posição inicial
Vector3 startPosition = transform.position;
// Posição final (ex: posição inicial + 1 unidade para frente)
Vector3 endPosition = startPosition + direction;
float elapsedTime = 0f;
// Loop que dura exatamente o tempo de 'moveDuration'
while (elapsedTime < moveDuration)
{
// Lerp (Interpolação Linear) calcula a posição intermediária
transform.position = Vector3.Lerp(startPosition, endPosition, elapsedTime / moveDuration);
// Avança o tempo
elapsedTime += Time.deltaTime;
// Espera até o próximo frame
yield return null;
}
// Garante que o jogador termine exatamente na posição final (corrige imprecisões)
transform.position = endPosition;
// Libera o movimento para o próximo pulo
canMove = true;
}
}
Para adicionar o “pulo” visual: Você pode adicionar um pequeno movimento no eixo Y (altura) dentro da coroutine. Uma forma simples é usar uma curva de animação (AnimationCurve) ou matemática (ex: Mathf.Sin) para fazer o transform.position.y subir e descer durante o moveDuration.
Conclusão: Você Deconstruiu a Mecânica
Pronto. Você acabou de criar o coração de Crossy Road.
Você não construiu o jogo inteiro (isso seria scope creep!), mas você construiu o MVP da mecânica principal. Você aprendeu:
- A usar uma câmera Ortográfica para um visual 3D estilizado.
- A usar
GetKeyDownpara um input que acontece uma vez por aperto. - A usar uma Coroutine com
Vector3.Lerppara criar um movimento suave e “gostoso” que não é instantâneo.
É assim que você deve abordar qualquer jogo que pareça complexo: quebre-o em partes menores, descubra qual é a mecânica central e construa um MVP funcional dela. O resto (geração de mundo, carros, pontuação) é só consequência.
Qual outra mecânica de jogo famoso você gostaria de ver desconstruída? Deixe nos comentários!





