Câmera seguindo o jogador em um mundo 2D
Uma das funcionalidades mais comuns em jogos digitais, principalmente quando
falamos de jogos plataforma ou de movimentação lateral (side scrolling), é a movimentação da câmera seguindo o jogador. E já que essa é uma funcionalidade tão comum, vamos falar um pouco sobre ela e
ver como podemos implementá-la de forma fácil em nossos jogos.
|
Câmera seguindo o jogador durante a movimentação
|
Neste exemplo vamos utilizar o conjunto de Assets chamado
A platformer in the forest, desenvolvido por
Buch e disponível no site
OpenGameArt.Org.
Pronto para começar? Então, vamos lá!
Vamos iniciar a implementação da nossa lógica de movimentação da câmera
criando uma classe, chamada de ControladorCamera, que será
responsável por identificar a posição atual do jogador e
movimentar a câmera em direção a ele.
Agora que sabemos o que precisa ser feito, precisamos entender
como fazer. Afinal, como podemos mover a câmera em direção ao
jogador?
Lerp
Uma das formas mais simples de realizarmos essa movimentação da câmera é
utilizar o método Lerp para calcular a nova posição da câmera
(para onde a câmera deve mover) a cada frame do jogo.
Hmm...mas o que é o Lerp?
Lerp é como é chamado o método de Interpolação Linear na
Unity.
...e o que é a Interpolação Linear?
Podemos descrever a Interpolação Linear como uma forma de encontrar
posições intermediárias entre dois pontos, ou seja, a partir de
uma posição de origem (posição atual da câmera) e uma
posição de destino (posição atual do jogador), nós conseguimos
determinar posições intermediárias por onde meu objeto (câmera) deve
passar, até chegar ao destino (seguir o jogador). Ficou mais claro?!
Legal! E como o Lerp funciona?
O método Lerp precisa de três parâmetros para ser executado:
- Posição de origem
- Posição de destino
- Passo
No nosso exemplo, a posição de origem é a
posição atual da câmera
(onde ela está no frame atual do jogo), a posição de destino é a
posição para onde queremos mover a câmera, ou seja, a posição
atual do jogador (onde o jogador está no frame atual do jogo) e o
passo representa o quanto nós queremos mover
(interpolar) entre a posição de origem e a posição de destino. O
passo pode ser utilizado para definir a
velocidade de movimentação da câmera enquanto ela segue o
jogador.
Vamos tentar tornar esse entendimento um pouco mais visual, olhando para o
exemplo abaixo:
|
Simulação do Lerp da câmera
|
A bola vermelha (movendo a partir da câmera) representa a
posição de origem (posição da
câmera) durante cada etapa da movimentação. A
bola verde representa a
posição de destino (a posição do
jogador) e a
bolinha (tamanho menor) azul
representa a
próxima posição da câmera calculada pelo método Lerp. Se reproduzirmos o mesmo exemplo, mas
desta vez também executando a movimentação da câmera, o resultado é o
seguinte:
|
Simulação do Lerp da câmera com a câmera em movimento
|
Podemos expandir o exemplo para visualizarmos a movimentação da câmera
e a representação visual do cálculo da próxima posição
do
Lerp enquanto movimentamos o jogador, conforme pode ser
visto abaixo, onde na direita temos a cena (scene) e na esquerda o jogo em
execução:
|
Simulação do Lerp da câmera com movimentação do jogador
|
Obs: No exemplo exposto anteriormente, a câmera está
propositalmente com um delay de atualização da movimentação, para
facilitar a visualização e o entendimento do Lerp.
Agora que estamos entendidos, vamos começar a nossa implementação. Primeiro,
precisamos criar as variáveis que serão utilizadas no cálculo do Lerp
(posição de origem, posição de destino e passos), que no nosso caso são
respectivamente: posição da câmera, posição do jogador e velocidade de
movimentação.
O nosso script ControladorCamera está sendo construído para ser
associado com o GameObject da câmera, dessa forma, nós poderemos identificar
a posição atual da câmera e fazer alterações nela sem precisar criar uma
nova variável, já que utilizaremos a posição do próprio GameObject através
da propriedade position (this.transform.position). Sendo
assim, precisamos criar apenas duas novas variáveis: jogador e
velocidadeMovimentacao.
/// <summary>
/// Transform do jogador que será seguido pela câmera
/// </summary>
[SerializeField]
private Transform jogador;
/// <summary>
/// Velocidade de movimentação da câmera para seguir o jogador
/// </summary>
[SerializeField]
private float velocidadeMovimentacao;
A variável jogador foi declara com o tipo
transform para que possamos associar o Transform do
jogador (na Hierarquia da Unity) com a nossa variável, permitindo a consulta
da posição atual do jogador sempre que necessário. Qualquer
transform que for associado com a variável será utilizado como
objeto alvo da movimentação da câmera.
A variável velocidadeMovimentacao será utilizada como o
passo do Lerp, ou seja, o quanto nós queremos
mover a câmera a cada frame do jogo. Utilizando essas duas
variáveis, o Lerp pode ser utilizado da seguinte forma:
Vector3 posicaoFinal = Vector3.Lerp(
this.transform.position, // Posição de origem
this.jogador.position, // Posição de destino
(this.velocidadeMovimentacao * Time.deltaTime) // Passo
);
Em seguida, basta utilizar o Lerp e atualizar a posição da
câmera periodicamente e tudo estará resolvido...certo?
private void FixedUpdate() {
Vector3 posicaoFinal = Vector3.Lerp(
this.transform.position, // Posição de origem
this.jogador.position, // Posição de destino
(this.velocidadeMovimentacao * Time.deltaTime) // Passo
);
// Move a câmera para a nova posição
this.transform.position = posicaoFinal;
}
Antes de testarmos, precisamos configurar o nosso ControladorCamera, no Inspector. Basta adicionarmos o script ControladorCamera no GameObject da câmera e em seguida selecionar o objeto que será seguido pela câmera (jogador) e posteriormente a velocidadeMovimentacao de acordo com o comportamente desejado pela câmera.
|
Configuração do ControladorCamera no Inspector
|
Ops...algo errado não está certo. O jogo sumiu!
|
Testando o a movimentação da câmera com Lerp...e com erro
|
Sim! O jogo sumiu. Mas o que de fato está acontecendo?
Se olharmos para atenção para o transform da câmera durante a
execução do Lerp podemos perceber que a posição
z da câmera mudou de -10 para um valor bem próximo de 0. Você
sabe por que isso acontece?
|
Alteração do eixo Z da câmera durante o Lerp
|
Bom, lembra que o
Lerp faz uma
interpolação entre duas posições? No nosso exemplo, essa
alteração do eixo
z pode ser considerada um efeito colateral do
Lerp, já que o jogador (posição de destino) está localizado na
posição 0 no eixo z, dessa forma, durante o
Lerp, a
câmera tende a mover-se em direção ao valor 0 no eixo
z, para ser
aproximar da posição do jogador.
Entendi! Mas temos como corrigir isso?
Sim! E é bem simples. Precisamos apenas alterar nosso código para manter a
câmera na mesma posição z que está estava antes de executar o
Lerp. Isso pode ser feito adicionando o código abaixo antes de
alterarmos a posição da câmera:
// Mantém a câmera na mesma posição Z, para continuar
// visualizando os objetos do jogo
posicaoFinal.z = this.transform.position.z;
Com essa alteração, o nosso FixedUpdate ficará da seguinte
forma:
private void FixedUpdate() {
Vector3 posicaoFinal = Vector3.Lerp(
this.transform.position, // Posição de origem
this.jogador.position, // Posição de destino
(this.velocidadeMovimentacao * Time.deltaTime) // Passo
);
// Mantém a câmera na mesma posição Z, para continuar
// visualizando os objetos do jogo
posicaoFinal.z = this.transform.position.z;
// Move a câmera para a nova posição
this.transform.position = posicaoFinal;
}
O resultado final é a câmera seguindo o jogador enquanto ele se move nos eixos x (para os lados) e y (para cima e para baixo):
|
Movimentação da câmera seguindo o jogador |
O script completo da classe ControladorCamera pode ser visto
abaixo:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ControladorCamera : MonoBehaviour
{
/// <summary>
/// Transform do jogador que será seguido pela câmera
/// </summary>
[SerializeField]
private Transform jogador;
/// <summary>
/// Velocidade de movimentação da câmera para seguir o jogador
/// </summary>
[SerializeField]
private float velocidadeMovimentacao;
private void FixedUpdate() {
Vector3 posicaoFinal = Vector3.Lerp(
this.transform.position, // Posição de origem
this.jogador.position, // Posição de destino
(this.velocidadeMovimentacao * Time.deltaTime) // Passo
);
// Mantém a câmera na mesma posição Z, para continuar
// visualizando os objetos do jogo
posicaoFinal.z = this.transform.position.z;
// Move a câmera para a nova posição
this.transform.position = posicaoFinal;
}
}
O projeto completo, desenvolvido para este post, pode ser baixado
aqui.
Gostou? Deixa o seu comentário com dúvidas e sugestões. Até mais! o/
Comentários
Postar um comentário