Câmeras para jogos 3D

Uma parte fundamental da criação de jogos em 3D é o uso de câmeras para mostrar o ambiente e os personagens da forma mais adequada ao gameplay desejado. O controle de câmeras pode ter diversos níveis de complexidade e nem sempre uma câmera mais complexa necessariamente é mais eficiente para um jogo. A seguir, vamos ver como implementar e as principais características de alguns tipos de câmeras para jogos 3D. O exemplo abaixo está disponível para download aqui.

Movimento relativo à câmera

Antes de falar de câmeras em si, um primeiro passo seria o de tornar o movimento do personagem ou avatar relativo à câmera, ou seja, apertar para frente move o jogador para a frente em relação à câmera. Para isso, vamos utilizar o código abaixo.

void Update () {
    _input = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
    _input = Vector3.ClampMagnitude(_input, 1f);
    Vector3 camForward = Camera.main.transform.forward;
    _velTemp = Input.GetAxis("Vertical") * camForward + Input.GetAxis("Horizontal") * Camera.main.transform.right;
    _velTemp.y = 0f;
    _velTemp = _velTemp.normalized * _input.magnitude;
    _anim.SetFloat("speed", _velTemp.magnitude);
}

void FixedUpdate()
{
    if (_input.sqrMagnitude <= 0f)
    {
        return;
    }
    var vel = _velTemp * velocidade;
    //var velAlvo = new Vector3(vel.x, _rb.velocity.y, vel.z);
    transform.forward = Vector3.Slerp(transform.forward, _velTemp.normalized, Time.fixedDeltaTime * suavizar);
    var finalVel = transform.forward * velocidade * _input.magnitude;
    finalVel.y = _rb.velocity.y;
    _rb.velocity = finalVel;
}

Ponto fixo, ângulo fixo

Essa seria uma câmera totalmente estática, posicionada de forma a sempre mostrar as mesmas coisas. É a mais simples de configurar e é bastante previsível.

Ponto fixo, ângulo variável

Essa câmera rotaciona para acompanhar um alvo, mas mantendo uma certa posição. Permite bastante controle por parte dos designers, mas mantém o centro da atenção no avatar.

void LateUpdate()
{
    transform.LookAt(alvo);
}

Distância fixa, ângulo fixo

Nesse tipo de câmera, a distância e o ângulo entre a câmera e o avatar são sempre iguais, ou então tentam ser na medida do possível (pode-se usar suavização para evitar movimentos bruscos). É bastante simples de configurar, mas pode ser bastante interessante dependendo do tipo de movimento e espaço que o jogo apresenta.


void Start()
{
    offset = transform.position - alvo.position;
}

void FixedUpdate()
{
    Vector3 posicaoDesejada = alvo.position + offset;
    posicaoDesejada = Vector3.Lerp(transform.position, posicaoDesejada, Time.fixedDeltaTime * suavizar);
    transform.position = posicaoDesejada;
    if(rotacionar)
    {
        transform.LookAt(alvo);
    }
}

Distância fixa, ângulo variável

Essa é uma câmera bastante dinâmica, que segue o personagem, além de permitir uma pequena variação de enquadramento através de um offset. É bastante utilizada, mas, por sua complexidade, também apresenta uma série de problemas com colisões, suavização e uso contextual (curvas, controle do usuário, etc).

void Start()
{
    offset = alvo.transform.position - transform.position;
}

void LateUpdate()
{
    float anguloAtual = transform.eulerAngles.y;
    float anguloDesejado = alvo.transform.eulerAngles.y;
    float angle = Mathf.LerpAngle(anguloAtual, anguloDesejado, Time.smoothDeltaTime * suavizar);

    Quaternion rotacao = Quaternion.Euler(0, angle, 0);
    transform.position = alvo.transform.position - (rotacao * offset);

    transform.LookAt(alvo.transform.position + offsetVisao);
}

Referências

O vídeo de John Nesky, programador de câmeras de Journey, é uma ótima referência para discutir a diversidade de problemas que câmeras em jogos 3D podem criar, assim como suas possibilidades expressivas.