Introdução
1. Parte Logica
2. Parte Gráfica
3. Comunicação entre as partes Lógica e Gráfica
4. Fluxograma do Programa
5. Download do Código Fonte
Conclusão
A Batalha Naval é um jogo clássico que pode ser jogado em um videogame, computador, tabuleiro, ou mesmo em uma simples folha de papel. Geralmente, a batalha naval é jogada por duas pessoas, das quais irá ganhar aquela que afundar primeiro todos os navios do adversário, após várias rodadas. O jogo consiste de um tabuleiro formado por várias linhas e colunas, no qual são posicionados os navios de cada jogador. Em uma rodada, cada jogador tem direito de dar um tiro (que é uma coordenada do tabuleiro, como por exemplo: A3, representando um tiro no quadrante localizado na primeira linha e terceira coluna) no tabuleiro do outro jogador e, obter como resposta, três ações possíveis:
Existem algumas variações desse jogo, contudo, todas seguem a mesma idéia. No caso de algumas versões para computador, jogadas por uma única pessoa, o computador posiciona aleatoriamente os navios no tabuleiro e cabe ao jogador afundá-los com a menor quantidade de tiros possíveis. Esta versão será implementada visando simplificar a parte lógica do jogo.
Dessa forma, primeiro será explicado como foi implementado a parte lógica do jogo (destacando os mapeamentos feitos e
a função que gerencia o estado do tabuleiro), para em seguida explicar a parte gráfica (que consiste no desenho dos tabuleiros e na
modelagem dos navios) e, por fim, a comunicação entre estas partes que resulta no jogo efetivamente, que será modelado
pela arquitetura do programa.
Existem algumas variáveis e constantes que têm como função apenas auxiliar o processo de gerenciamento do jogo. Dentre elas, destacam-se três constantes que representam cada tipo de navio: BOTE, CARRIER e DESTROYER, que possuem os valores 3, 4 e 5, respectivamente. Além delas, há três estruturas: pBote, pCarrier e pDestroyer, que servem para armazenar as posições dos navios quando o jogo começa.
O tabuleiro possui 10 linhas e 10 colunas, sendo mapeado por uma matriz de inteiros com as mesmas dimensões. Na implementação esta matriz é denominada Tab e definida como int Tab[10][10]. É interessante observar que por ser implementado na linguagem C, tal declaração faz com que os campos de cada dimensão da matriz Tab começem em 0 e acabem em 9. Por convenção, os caracteres de A a J representam as linhas e os números de 0 a 9 representam as colunas, conforme a figura abaixo:
Inicialmente todas as posições da matriz Tab são atribuídas ao valor zero, indicando que todos os quadrantes devem ser desenhados normalmente. À medida que o jogador dispara os tiros, a função Atira atualiza as posições da matriz Tab de acordo com as convenções criadas, que são:
Um navio só será afundado depois que todas as suas posições forem acertadas, ou seja, um tiro resulta em afundar um navio somente se apenas a posição daquele tiro ainda não foi acertada. Neste caso, todas as posições da matriz Tab relativas as posições do navio são setadas conforme a convenção acima.
Como exemplo, supondo que o navio Bote seja o único no tabuleiro e esteja localizado nas posições C3 e C4. Se um tiro for dado na posição C5, resultará em água, o que implica que a matriz Tab[2][5] será igual a 1. Na hipótese de um tiro ser dado na posição C3 resultará no acerto de uma parte do Bote e, portanto, Tab[2][3] será igual a 2. Caso um novo tiro seja dado na posição C4, acertando a outra parte do Bote, ele será afundado, implicando que Tab[2][3] e Tab[2][4] será igual a BOTE (valor 3).
Em relação ao gerenciamento de entrada, um tiro deve ser dado digitando as coordenadas do quadrante em que se deseja atirar pelo teclado. Usando o exemplo acima, para executar um tiro na posição C5 deverá ser teclado primeiro a linha, no caso o caracter C, e depois a coluna, 5. Há 3 variáveis de controle referentes à entrada de dados, que são eLetra, cLetra e cNumero, que servem apenas para impedir que o tabuleiro se altere quando digitado comandos inválidos ou tiros repetidos.
Para cada um dos navios foi criada uma display list com o nome do navio, a qual é chamada no momento em que o navio em questão é afundado. Além dessas 3 listas, há uma lista denominada TABULEIRO, que é chamada no início do jogo para desenhar o tabuleiro. Esta é a parte gráfica mais simples do jogo, pois é formado de 11 linhas verticais e 11 horizontais, formando 100 quadrantes. Todas as linhas são desenhadas no plano z = 0, variando-se apenas as coordenadas do eixo x para as colunas, e do eixo y para as linhas.
O navio Bote foi modelado com o uso de cubos escalonados e cilindros, enquanto que para os navios Carrier e Destroyer foram usados cubos escalonados, cilindros e esferas. A seguir, serão exposto alguns screenshots de cada navio junto com suas display lists:
Destroyer
Figura 3: Destroyer visto de frente
Figura 4: Destroyer visto de cima
Carrier
Figura 6: Carrier visto de frente
Figura 7: Carrier visto de cima
Bote
Figura 9: Bote visto de frente
Figura 10: Bote visto de cima
Por convenção, o Bote ocupa 2 quadrantes, o Carrier 3 e o Destroyer 4.
Por fim, às ações correspondentes aos tiros são desenhadas na função display, verificando o conteúdo de cada célula da matriz Tab. Caso o valor da célula seja 1 ou 2, um "X" é desenhado no quadrante, a água, os erros, é representada pela cor azul e o navio, os acertos, pela cor vermelha. Como a janela criada pelo glut possui a dimensão de 500 x 500 pixels, com o referencial localizado exatamente no centro da janela, foi preciso uma função de mapeamento entre as células da matriz Tab e as posições da janela, chamada MapCoord, para identificar as coordenadas dos vértices de cada uma das duas retas que formam o"X".
As duas próximas figuras mostram telas do jogo em diferentes estados, enquanto que as duas últimas mostram a visualização do jogo com diferentes câmeras. Para mudar o ângulo da câmera basta pressionar as teclas x, y e z, sendo rotacionado o tabuleiro do jogo em relação ao eixo teclado. Para mudança de zoom basta pressionar as teclas + e - .
Figura 12: Jogo com os 3 navios já afundados
Figura 13: Tabuleiro visto com rotações
Figura 14: Tabuleiro visto com zoom e rotações
Houve uma dificuldade no momento de desenhar o modelo do navio afundado em virtude de todas as posições da matriz Tab estarem com o valor do navio afundado, ocasionando a repetição do desenho do navio para cada posição ocupada por ele. Este problema foi resolvido adicionando 3 variáveis booleanas, dBote, dCarrier e dDestroyer, que indicavam se o navio já havia sido desenhado e, caso já houvesse, o laço era desviado. O trecho de código que faz esta comunicação pode ser visualizado aqui.
Além disso, é interessante observar que para a construção de um simples modelo 3D não é preciso necessariamente saber desenhar, é possível usar a imaginação e criar um modelo utilizando apenas definições geométricas.
Por fim, a característica de portabilidade da biblioteca openGL pôde ser comprovada com a migração do jogo desenvolvido em Borland CBuilder++ 5.0 na plataforma Windows XP para o compilador gcc do Unix e Linux, sem grandes problemas, bastando compilar o arquivo bNaval.c na nova plataforma. Para tanto, o jogo escrito utiliza apenas as bibliotecas glut.h (que incorpora as bibliotecas gl.h e glu.h) e stdlib.h, não referenciando nenhum biblioteca que esteja amarrada a uma determinada plataforma.