Inicia-se agora o derradeiro episódio de nossa odisseia espacial!
Terceira Fase: Estruturas de Dados e Interação com o Usuário
Nesta última etapa de desenvolvimento do projeto, vamos finalizar o jogo, dando um acabamento final, implementando a interação com o usuário, pontuação dos jogadores, vidas,
telas inicial e final, etc.
Além disso, vamos implementar uma estrutura de dados (ED) eficiente para o gerenciamento dos deslocamentos
dos asteróides e dos cristais, assim como sua posterior visualização na tela.
Por fim, vamos elaborar testes automatizados
para validar a correção da ED e preparar um relatório final.
Interação com o Usuário
Teclas das Espaçonaves
Relembrando as descrições do jogo e das teclas, fornecidas em EP2:
cada um dos dois jogadores controla uma das duas espaçonaves (uma de cada cor) e ambos têm a tarefa de
recolher a maior quantidade possível de cristais. Os jogadores devem desviar-se dos perigosos
asteróides ou então destruí-los: para isso cada espaçonave está equipada com um
poderoso phaser e com um campo esférico desintegrador, de antimatéria.
Cada jogador controla a sua nave pressionando determinadas teclas, sendo que as teclas de cada um ficam nas
extremidades opostas do teclado. São elas:
Quatro teclas para a movimentação da nave:
uma tecla para propulsão e outra para freio (com simulação
física);
duas teclas para girar no sentido horário e anti-horário (sem
simulação física, i.e., gira numa taxa fixa para cada aperto de tecla).
Uma tecla para disparar o phaser;
Uma tecla para acionar o campo esférico desintegrador, de antimatéria.
Em xwc, a captura de eventos de teclado pela janela do jogo é descrita no exemplo teste2.c,
que acompanha a biblioteca.
Já em Allegro, antes de mais nada, inicialize o handler de teclado com a função
install_keyboard(). A biblioteca conta com um vetor de chars chamado key, onde cada elemento
representa se uma tecla do teclado foi acionada. Se, por exemplo, key[KEY_LEFT] devolve um valor diferente de zero,
então a seta esquerda foi acionada num instante de tempo anterior. Consulte o manual e os tutoriais de
Allegro para mais informações.
Funcionamento do phaser
O phaser é um pequeno feixe de energia, com o comprimento similar ao diâmetro de um asteróide;
uma espaçonave pode disparar quantas vezes quiser, dentro de um intervalo pré-determinado entre os tiros (por exemplo, 0,5 segundo entre dois tiros).
Quando um disparo de phaser atinge um asteróide ou cristal, ele é destruído. Se um
disparo atinge o outro jogador, nada acontece pois as naves são equipadas com campos de força inteligentes.
Por fim, um disparo não atinge outro (ou seja, não há colisão entre dois disparos).
Dica opcional: trate os disparos do phaser como uma quarta variante do tipo objetos na tela (as outras
três seriam nave, asteróide e cristal).
Funcionamento do campo esférico desintegrador
O campo esférico desintegrador de anti-matéria é uma esfera que protege a espaçonave contra asteróides. Quando acionado, ele deve ser representado como uma circunferência de energia em volta da nave: qualquer asteróide ou cristal é imediatamente desintegrado (sem explosão) se encostar no escudo.
Quem quiser caprichar, pode utilizar um efeito de
transparência para que de fato pareça que a nave está envolta por uma esfera de energia (imaginem
o campo de força da Mulher Invisível, de "Os Quatro Fantásticos" :-). Outro detalhezinho seria
desenhar um efeito legal para a "desintegração"!
O campo esférico desintegrador tem um funcionamento parecido com os "nitros" de jogos de corrida: quando acionado, ele funciona por um período (digamos, 3 segundos) e depois existe uma "carência" para acioná-lo
novamente (por exemplo, 10 segundos). Cabe a vocês calibrarem esses dois parâmetros, de tal modo que a
jogabilidade fique boa.
Pontuação, vidas e dinâmica do jogo
Cada jogador começa com zero pontos e 3 vidas extras (ambos são inteiros não-negativos); tais
valores são alterados conforme os eventos que ocorrem durante o jogo:
se uma nave é atingida por um asteróide sem estar protegida pelo escudo: o jogador perde
uma vida;
se o jogador A acerta com seu phaser o jogador B: a nave do jogador B não é
destruída e este não perde uma vida, mas o jogador A é penalizado em 100 pontos
(princípio da não-violência ;-);
quando um cristal é recolhido, o jogador ganha 10 * m pontos, onde m é a
massa do cristal;
quando um cristal é destruído por um disparo do phaser, o jogador que efetuou o
disparo é penalizado em 5 * m pontos, onde m é a massa do cristal;
se um jogador acumula 1000 pontos, ganha uma vida.
O jogo termina quando todos os cristais são recolhidos, ou quando ambas as naves são destruídas
e não contam mais com vidas extras. Ganha o jogador que fizer mais pontos ao final do jogo.
Ajuste fino da Jogabilidade
Vocês podem fazer ajustes nos diversos parâmetros do jogo (taxa de atualização da tela, velocidade e tamanho dos objetos, tempo de acionamento do escudo, etc.) com o objetivo de melhorar a "jogabilidade" de seu joguinho. Lembrem-se que "jogabilidade" também será levada em consideração na avaliação de seu EP.
Importante: todos os parâmetros do jogo devem ser definidos na forma de constantes (#define),
nos headers dos módulos do programa ou em variáveis apropriadas (ou seja, evite "números mágicos" no meio do seu código). Se preferir passar alguns parâmetros por linha de comando,
não se esqueçam de acrescentar uma opção de "ajuda". Em ambos os casos, expliquem os
diversos valores (mínimo e máximo) para cada parâmetro que o seu programa aceita.
Estruturas de Dados
Visando aumentar a eficiência de nosso joguinho, tanto na manipulação dos objetos e de seus
eventos quanto em sua posterior impressão na tela, vamos implementar uma Estrutura de Dados (ED) que
faça tudo isso de forma mais eficiente que utilizando estratégias "inocentes". Dessa forma, nesta
seção descrevemos a ED e como atualizá-la para cada tipo de evento, de tal forma que a
estrutura permaneça sempre correta.
Árvores de Busca Binária
Seja S um conjunto de pontos (de zero à 600) que se movimentam no plano. Existem dois
principais tipos de pontos em S, asteróides e cristais. Vocês deverão
armazenar os pontos de S em uma árvore multinível, que deverá ser, obrigatoriamente,
uma árvore de busca binária (ABB). Cada ponto possui uma velocidade, uma direção e um
sentido.
Assim como nos EPs anteriores, a tela de nosso jogo será fixa e conterá todos os pontos do
domínio. Ou seja, quando um asteróide ou cristal surge em alguma borda da tela, ele deve ser
inserido na ABB. Analogamente, asteróides e cristais devem ser removidos obrigatoriamente da estrutura de
dados, nas seguintes situações:
quando qualquer um deles sai da tela;
quando qualquer um deles é destruído por um disparo de phaser;
quando qualquer um deles é desintegrado pelo campo de força das naves;
quando um cristal é recolhido por uma das naves.
Resumindo, na estrutura de dados só estarão os pontos que estão na tela.
O principal motivo de mantermos uma ABB será para detectarmos de forma mais eficiente as colisões.
Custo esperado de uma operação da ABB
Como todos os dados serão aleatorizados, espera-se que mesmo não implementando o balanceamento, na
média, a altura da árvore será semelhante à altura da árvore balanceada; assim, o
custo esperado de uma operação de busca, inserção ou de remoção será
de O(log n). Exercício opcional: provar esse fato ou encontrar um contra-exemplo.
Implementação opcional (valendo 1 ponto extra): implementar de fato uma árvore
de busca binária balanceada (pode ser uma ABB AVL, rubro-negra, etc.).
Iteração
Dessa forma, com a presença da ED descrita acima, uma iteração de nosso joguinho poderia ser
resumida aos seguintes passos:
(1) fazer os movimentos de todos os objetos, baseados em suas velocidades vetoriais;
(2) tratar os eventos, atualizando a ED sempre que necessário;
(3) pintar os pontos na tela, utilizando a ED;
(4) dormir e depois voltar para (1).
Vale lembrar que os pontos devem ser pintados na tela ao menos 10 vezes por segundo.
Testes Automatizados
Para este EP, vamos elaborar alguns testes automatizados para a validação da ED utilizada para manipulação dos objetos na tela; os pontos que devem ser testados são:
Robustez: a ED funciona corretamente com muitos pontos (como por exemplo, 1000 pontos ou
até um pouco mais)? E com poucos pontos (zero, por exemplo)?
Correção: a ED é atualizada corretamente, para todos os eventos listados na seção "Estruturas de Dados"?
Portanto, a bateria de testes deve verificar a correção e robustez da ED. Tal qual foi feito em EP2 de MAC211, as funções de teste devem estar em um arquivo separado do "main" do programa principal.
Relatório
Junto com o joguinho, cada grupo deverá entregar um relatório, organizado nas seguintes seções:
introdução: apresentação do documento e do trabalho desenvolvido.
manual do usuário: quais são os pré-requisitos do joguinho, como compilá-lo, quais são os seus parâmetros, etc.
implementação: a estruturação do código e as EDs empregadas, o programa de testes automatizados, etc.
conclusões: o relato da experiência adquirida ao longo do desenvolvimento do projeto "Protetores do Espaço 2", do EP2 até o EP4.
Sejam bem objetivos em cada seção do relatório; o documento precisa ter entre 2 e 5 páginas e deve ser escrito (obrigatoriamente) em LaTeX.
Melhorias Opcionais
Algumas melhorias opcionais podem ser feitas para aumentar a qualidade do joguinho; elas não são
obrigatórias para a maioria dos grupos (vejam a próxima seção); todavia, uma boa
implementaçãao delas, devidamente documentadas, podem render ao grupo um ou dois pontos extras
(dependendo da quantidade dos extras implementados). Caso vocês implementem uma ou mais melhorias, devem se lembrar de deixá-las claras em seu relatório final.
Alguns extras sugeridos seriam:
quando todos os cristais são recolhidos, passa-se para uma nova fase, mais
difícil;
poder definir no início o tamanho da tela (atualmente o default é
1024x768);
a opção de jogo para 1 jogador (i.e., jogar contra o computador);
editar número de vidas inicial, dificuldade e nomes dos jogadores e guardá-los, juntamente
com os recordes e configurações do jogo, em um arquivo, que é lido sempre que o
jogo é inicializado;
utilizar simulação física para os movimentos laterais das naves;
quando o phaser atinge um asteróide qualquer, caso a massa dele seja maior do que 1, ele
é dividido em 2 asteróides de massa igual e o vetor velocidade de cada um desses novos
asteróides é igual ao vetor antigo mais um certo valor de forma que a soma do diferencial acrescentado
após a explosão seja nula;
implementar efeitos sonoros;
implementar interação do usuário através de outros dispositivos
(por exemplo, joystick).
Data de entrega (e o que deve ser entregue)
A última fase deverá ser entregue até o dia 30 de junho (terça-feira).
Dúvidas
podem ser discutidas no fórum da disciplina no Moodle,
para o aproveitamento de todos.
Devem ser entregues no tar.gz:
o código-fonte do joguinho, bem modularizado, estruturado e comentado;
o programa de testes automatizados, devidamente documentado;
um relatório, explicando como compilar e jogar seu jogo, a implementação do programa e dos testes (entregar o .tex e o .pdf);
as estruturas de dados aqui descritas, devidamente implementadas no código-fonte do
joguinho (obrigatório para grupos com um ou mais componentes matriculados em MAC323);
ao menos 5 das 8 melhorias opcionais listadas acima (obrigatório para grupos com
nenhum componente matriculado em MAC323);