Outrun
Este tutorial detalha a implementação do jogo "Outrun", um simulador de corrida com perspectiva 3D, desenvolvido em C++ e SFML. Abordaremos os conceitos fundamentais por trás da criação de uma estrada infinita e da projeção de objetos para simular um ambiente tridimensional.
O que é Outrun
"Outrun" é um jogo de corrida clássico onde o jogador controla um carro em uma estrada que se estende ao horizonte, criando uma ilusão de profundidade e movimento. O objetivo é dirigir pela estrada, desviando de obstáculos e outros veículos, enquanto a paisagem se move para simular velocidade.
As principais características que exploraremos são:
Perspectiva 3D: Como a estrada e os objetos são renderizados para simular uma visão em três dimensões.
Geração de Estrada: A criação de uma estrada "infinita" com curvas e elevações.
Controle de Veículo: A movimentação do carro do jogador e o controle de velocidade.
Objetos na Estrada: A inclusão de elementos como árvores e carros oponentes.
A Base do Jogo: Segmentos da Estrada e Projeção 3D
O coração do "Outrun" é a forma como ele simula um ambiente 3D usando segmentos de estrada e projeção. Em vez de um modelo 3D complexo, o jogo desenha a estrada como uma série de quadriláteros, cada um representando um pequeno segmento da pista.
A Estrutura Line
Cada segmento da estrada é representado por uma instância da estrutura Line
. Esta estrutura armazena as coordenadas 3D do centro do segmento (x
, y
, z
), bem como suas propriedades na tela após a projeção (X
, Y
, W
).
x, y, z
: Coordenadas 3D do centro do segmento.z
é a profundidade,y
a elevação ex
a posição horizontal.X, Y, W
: Coordenadas na tela após a projeção.X
é a posição horizontal,Y
a vertical eW
a largura do segmento na tela.curve
: Determina a curvatura do segmento da estrada. Um valor positivo curva para a direita, negativo para a esquerda.spriteX
: Posição horizontal de um sprite associado a este segmento (por exemplo, uma árvore ou um carro oponente).project(camX, camY, camZ)
: Este método é a chave para a projeção 3D. Ele calcula as coordenadasX
,Y
eW
na tela com base na posição da câmera (camX
,camY
,camZ
) e na profundidade (z
) do segmento. Quanto maiorz
(mais distante), menor oscale
e, consequentemente, menor o segmento na tela, criando a ilusão de profundidade.drawSprite(RenderWindow &app)
: Desenha um sprite associado a este segmento na posição correta na tela, levando em conta a perspectiva.
Desenhando a Estrada: drawQuad
A função drawQuad
é responsável por desenhar os quadriláteros que compõem a estrada. Ela recebe as coordenadas de dois pontos (superior e inferior) e suas respectivas larguras, e desenha um ConvexShape
(um quadrilátero) entre eles.
No loop principal do jogo, esta função é chamada repetidamente para desenhar a grama, o acostamento e a pista, com cores diferentes para cada parte.
O Loop Principal do Jogo
O main
function contém o loop principal do jogo, onde a lógica é atualizada e a tela é redesenhada a cada frame.
Geração da Estrada
A estrada é gerada como um vetor de objetos Line
. Cada Line
tem uma posição z
(profundidade) e pode ter uma curve
(curvatura) e y
(elevação) para criar um percurso variado. Sprites (árvores, carros oponentes) são adicionados a segmentos específicos da estrada.
Input do Jogador
O jogo responde às teclas de seta para controlar a posição horizontal do jogador (playerX
) e a velocidade (speed
). A tecla Tab
atua como um "boost" de velocidade.
Atualização da Posição
A variável pos
representa a posição atual do jogador na estrada. Ela é incrementada pela speed
a cada frame. O uso do operador %
(pos % (N*segL)
) garante que a estrada seja "infinita", reciclando os segmentos quando o jogador atinge o final.
Desenho da Estrada e Objetos
O loop de desenho itera sobre os segmentos da estrada visíveis. Para cada segmento, ele:
Projeta o segmento para as coordenadas da tela usando
l.project()
.Calcula a curvatura acumulada (
x
edx
) para simular a perspectiva da estrada.Desenha a grama, o acostamento e a pista usando
drawQuad
, com cores alternadas para criar um efeito de faixas.Desenha os sprites associados a cada segmento (árvores, carros oponentes) usando
lines[n%N].drawSprite(app)
.
O desenho dos objetos é feito em ordem inversa (n=startPos+300; n>startPos; n--
) para garantir que os objetos mais distantes sejam desenhados primeiro, e os mais próximos por cima, mantendo a ordem de profundidade correta.
Carros Oponentes
Os carros oponentes são implementados como sprites associados a segmentos específicos da estrada. Eles são adicionados durante a fase de geração da estrada:
Atualmente, eles usam object[1]
como um sprite placeholder e são posicionados em diferentes faixas da estrada usando spriteX
.
Extensões Possíveis
Este jogo serve como uma excelente base para futuras extensões:
Detecção de Colisão: Implementar a lógica para detectar quando o carro do jogador colide com os carros oponentes ou outros objetos na estrada.
Sistema de Pontuação: Adicionar um sistema de pontuação baseado na distância percorrida, velocidade ou desvio de obstáculos.
Tipos de Oponentes Variados: Introduzir diferentes tipos de carros oponentes com comportamentos distintos.
Múltiplas Pistas/Cenários: Criar diferentes ambientes e layouts de estrada para aumentar a variedade.
Efeitos Sonoros: Adicionar sons para o motor, colisões e música de fundo.
Interface de Usuário: Exibir informações como velocidade, pontuação e distância percorrida.
Este tutorial cobriu os aspectos fundamentais da implementação do jogo "Outrun", desde a projeção 3D da estrada até a inclusão de objetos e o controle do jogador. Com esta base, é possível expandir o jogo com diversas funcionalidades para torná-lo mais rico e interativo.