====== Jogo gráfico ======
{{ pong.gif|Pong}}
O objetivo deste projeto é construir um jogo monousuário com interface gráfica 2D. O aluno deve escolher um destes jogos ((Diversos outros jogos foram desconsiderados porque existem muitas implementações prontas usando C e Allegro.)):
Clássicos:
* [[https://www.arcade-museum.com/game_detail.php?game_id=10026|Battle City]]
* [[https://en.wikipedia.org/wiki/Super_Mario_Bros.|Super Mario Bros]]
* [[https://en.wikipedia.org/wiki/Maziacs|Maziacs]]
* [[http://codeincomplete.com/projects/boulderdash|Boulder Dash]]
* [[https://en.wikipedia.org/wiki/Donkey_Kong_(video_game)|Donkey Kong]]
Recentes:
* [[https://play.google.com/store/apps/details?id=com.ketchapp.ballz&hl=pt-BR|Ballz]]
* [[https://play.google.com/store/apps/details?id=de.stollenmayer.philipp.Pop_1_1_Android|Okay?]]
* [[https://play.google.com/store/apps/details?id=com.robtopx.geometryjumplite|Geometry Dash]]
* [[https://play.google.com/store/apps/details?id=com.ketchapp.zigzaggame|ZigZag]] (fazer em 2D)
Você pode propor outros jogos ao professor para análise.
/*
Muito batidos ou muito fáceis:
* [[https://en.wikipedia.org/wiki/Tetris|Tetris]]
* [[https://pt.wikipedia.org/wiki/Space_Invaders|Space Invaders]]
* [[https://en.wikipedia.org/wiki/Pac-Man|Pac-man]]
* [[https://en.wikipedia.org/wiki/Bomberman|Bomberman]]
* [[https://en.wikipedia.org/wiki/Flappy_Bird|Flappy Bird]]
* [[https://en.wikipedia.org/wiki/Frogger|Frogger]]
* [[https://en.wikipedia.org/wiki/Galaga|Galaga]]
* [[https://en.wikipedia.org/wiki/River_Raid|River Raid]]
* [[https://en.wikipedia.org/wiki/Breakout_(video_game)|Breakout]]
*/
===== Requisitos =====
Requisitos do jogo:
* Executar no Linux, em uma janela gráfica
* Interagir com o usuário através do teclado e/ou mouse
* Usar imagens coloridas (//sprites//), não somente formas geométricas (pode usar figuras prontas obtidas na Internet)
* Tocar sons nos principais eventos (pode usar sons prontos da Internet)
* Suportar ao menos **três níveis** com mapas, ações e/ou objetos diferentes
* Calcular a pontuação do jogador e manter um //score// persistente (salvo em disco) das melhores pontuações obtidas, apresentado ao usuário ao finalizar o jogo
* As teclas ''h'' ou ''F1'' abrem uma tela de ajuda com as instruções do jogo, nome do autor e outras informações
* Ter um //[[https://en.wikipedia.org/wiki/Easter_egg_(media)|Easter Egg]]// ou um //cheat code// (como o [[https://pt.wikipedia.org/wiki/C%C3%B3digo_Konami|Código Konami]])
* Executar a 60 FPS (//frames per second//)
/*
:NEW: Requisitos solicitados pelo prof. Menotti (2021/2):
* Suportar ao menos 10 níveis com mapas, ações e/ou objetos diferentes
* Permitir que o usuário avance entre todos os mapas/níveis disponíveis usando teclas //page down// / //page up//
* //Easter egg// - um arquivo ''readme'' deve ser entregue explicando onde achá-lo
*/
Requisitos do código:
* Ser desenvolvido em C padrão C99 (''-std=c99'', ''-std=gnu99'' ou ''-std=gnu11'')
* Usar a biblioteca gráfica **Allegro 5** ((Não é a mais poderosa, mas tem um bom compromisso entre funcionalidades e facilidade de uso.))
* Separar o código em vários arquivos ''.c'' e ''.h'' separados, respeitando as regras de [[organização de código]]
* Usar [[alocação de memória]] dinâmica
* Usar [[estruturas]]
* Compilar com o flag ''-Wall'' sem erros nem avisos
* Usar [[o sistema Make]] para compilação, com ao menos os seguintes alvos:
* ''all'': compila e gera o executável
* ''clean'': remove os arquivos temporários (mantém o executável)
* ''purge'': remove tudo, deixando somente os fontes
* Todos os arquivos que não forem código-fonte (imagens, sons, fontes, mapas, ...) devem ser colocados em um subdiretório ''resources/'' dentro do diretório do código.
Critérios de avaliação
* cumprimento dos requisitos acima
* fidelidade ao jogo original
* qualidade do código (estrutura, clareza, comentários, endentação)
===== A biblioteca Allegro =====
A biblioteca gráfica [[http://liballeg.org/|Allegro]] permite a manipulação de gráficos simples e áudio, sendo bem adaptada para a construção de jogos 2D. É uma biblioteca mais simples e limitada que a famosa biblioteca [[https://www.libsdl.org|SDL]], sendo mais adequada para iniciantes e para projetos menores.
Algumas de suas características:
* multiplataforma: Linux, Windows, MacOS, Android, iOS
* pode ser usada em C e outras linguagens
* usa aceleração gráfica (através de OpenGL ou DirectX)
* manipulação de áudio e vídeo
* leitura de mouse, teclado e joystick
Instalação da biblioteca Allegro 5 em Linux (Ubuntu, Mint, Debian):
sudo apt-get install liballegro5-dev
Uso:
* [[https://liballeg.org|Página principal]]
* [[https://liballeg.org/a5docs/trunk|Referência das funções]]
* [[https://github.com/liballeg/allegro_wiki/wiki/Allegro-Vivace|Tutorial de jogo em Allegro]] :-)
* [[https://www.allegro.cc/|Galeria de jogos]]
Bibliotecas gráficas como a Allegro e SDL operam usando um esquema interno de **buffer duplo**, ou seja, dois //buffers// gráficos distintos que são usados em conjunto:
* buffer1: //buffer// onde as operações gráficas são aplicadas
* buffer2: //buffer// cujo conteúdo é mostrado na tela
Esses //buffers// devem ser periodicamente alternados pelo programador (pseudocódigo):
fill_color (black) ; // operações feitas no buffer 1, não são visíveis
draw (...) ; // na tela neste momento.
draw (...) ;
...
flip_buffers () ; // troca buffer 1 com 2, mostrando o novo conteúdo
As técnicas des [[https://en.wikipedia.org/wiki/Multiple_buffering#Double_buffering_in_computer_graphics|buffer duplo]] e [[https://docs.oracle.com/javase/tutorial/extra/fullscreen/doublebuf.html|page flipping]] são implementadas nativamente na biblioteca Allegro, fornecendo imagens mais estáveis e melhor desempenho gráfico.
===== Estrutura básica de um jogo =====
A maioria dos jogos segue uma estrutura de código similar. O funcionamento do jogo segue uma "máquina de estados" que define o momento atual do jogo e um laço principal que implementa o jogo em si. Ambos são explicados brevemente nesta seção.
==== Estado das entidades ====
Cada entidade do jogo (jogador, bola, etc) tem um conjunto de atributos que a representam, que normalmente é implementado em um ''struct''. Por exemplo, no jogo [[https://pt.wikipedia.org/wiki/Pong|Pong]] a bola poderia ser representada pela seguinte estrutura:
// estrutura de uma bola
typedef struct
{
short x, y ; // posição atual
short sx, sy ; // velocidade atual
short w, h ; // dimensões
color_t color ; // cor atual
}
ball_t ;
// cria uma bola
ball_t ball ;
Estruturas como essa devem ser construídas para cada entidade ativa do jogo.
==== Máquina de estados ====
O funcionamento do jogo é gerenciado através de uma //máquina de estados//. Cada estado do jogo define o que deve ser feito naquele momento, as transições possíveis entre estados e os eventos que levam de um estado a outro. Por exemplo, a máquina de estados do jogo [[https://pt.wikipedia.org/wiki/Pong|Pong]] é composta dos seguintes estados:
* **inicio**: desenha a tela inicial do jogo e aguarda 5 segundos.
* **servindo**: põe a bola no centro e aguarda 2 segundos.
* **jogando**: implementa o jogo propriamente dito (laço principal).
* **fim partida**: um dos jogadores atingiu a pontuação máxima.
* **fim jogo**: o jogo encerra.
O relacionamento entre esses estados pode ser representado graficamente:
{{ game-state-machine.png |}}
Essa máquina de estados pode ser facilmente implementada em C:
// estados possíveis do jogo Pong
enum {INICIO, SERVINDO, JOGANDO, FIMPART, FIMJOGO} state ;
// programa principal do jogo
int main ()
{
state = INICIO ;
for (;;)
switch (state)
{
case INICIO : state_init () ; break ;
case SERVINDO: state_serve () ; break ;
case JOGANDO : state_play () ; break ;
case FIMPART : state_over () ; break ;
case FIMJOGO : state_close () ; break ;
default: break ;
}
}
Dessa forma, as funções ''state_init()'' e demais são usadas para implementar o comportamento de cada etapa do jogo. A variável ''state'' pode ser modificada dentro dessas funções para trocar de estado conforme necessário.
==== O laço principal ====
Durante o jogo propriamente dito, o programa deve continuamente ler as entradas de dados (teclado, mouse, ...), atualizar o estado dos jogadores e demais entidades do jogo de acordo com essas entradas e desenhar a tela. Esse comportamento é chamado o **laço principal** do jogo.
O pseudocódigo a seguir dá um exemplo do laço principal do jogo Pong (simplificado):
// define estado inicial das entidades
inicia_jogadores ()
inicia_bola ()
// laço principal
faça
ler_entrada (teclado, mouse, ...)
atualiza_estado_jogadores ()
atualiza_estado_bola ()
desenha_tela ()
até fim da partida
Esse ciclo deve ser repetido rapidamente (dezenas de vezes por segundo) para dar a impressão de fluidez do jogo.
O [[https://github.com/liballeg/allegro_wiki/wiki/Allegro-Vivace|tutorial de jogo da biblioteca Allegro]] explica com detalhes o funcionamento do laço principal.
==== Controle da taxa de quadros ====
A tela do jogo deve ser atualizada com velocidade suficiente para dar fluidez ao jogo. De preferência, a taxa de atualização da tela (taxa de quadros por segundo ou //frame rate//) deve ser constante, para que a animação seja consistente.
Em um jogo simples, a quantidade de cálculo a efetuar antes de cada atualização de estado e de tela é geralmente pequena, se comparada à capacidade de processamento do computador. Por isso, é interessante **pausar a execução** por alguns milissegundos a cada ciclo, para garantir uma taxa de quadros constante (e também diminuir o consumo de CPU).
Uma forma de controlar a taxa de quadros é através de pausas (pseudocódigo):
// taxa de quadros por segundo
frame_rate = 60
// duração de cada quadro, em ms
t_quadro = 1000 / frame_rate
// laço principal do jogo
repita
t_inicio = relogio_ms ()
// processamento do jogo
...
// espera um tempo para completar o quadro
t_final = relogio_ms ()
t_pausa = t_quadro - (t_final - t_inicio)
pausa_ms (t_pausa)
até o fim da partida
Outra forma, que é usada frequentemente na Allegro 5, é a **programação por eventos**. Neste caso específico, programa-se um temporizador (//timer//) para gerar um evento a cada quadro; quando o evento ocorre, o processamento do jogo é feito:
// taxa de quadros por segundo
frame_rate = 60
// duração de cada quadro, em ms
t_quadro = 1000 / frame_rate
// define timer (um evento a cada X ms)
define_timer (t_quadro)
// laço principal do jogo
repita
// espera o próximo evento do timer
espera_evento (timer)
// processamento do jogo
...
até o fim da partida
==== Links ====
Desenvolvimento de jogos:
* [[https://www.youtube.com/playlist?list=PLWKjhJtqVAbluXJKKbCIb4xd7fcRkpzoz|CS50's Introduction to Game Development - Harvard]] (assistir pelo menos as duas primeiras aulas)
* [[https://github.com/liballeg/allegro_wiki/wiki/Allegro-Vivace|Tutorial de jogo da biblioteca Allegro]]
* [[https://web.archive.org/web/20161213145118/http://www.kathekonta.com:80/rlguide/|Beginner's Guide to Roguelikes]] (ler ao menos a parte 1)
* [[https://gameprogrammingpatterns.com/game-loop.html|Game Loop]]
* Canal [[https://www.youtube.com/user/xan4545|Jogos & Programação]] no Youtube
Sprites e sons:
* [[https://opengameart.org|Open Game Art]]
* [[http://github.grumdrig.com/jsfxr/|JSFXR sound generator]]