O projeto
Se você quer apenas se divertir, faça o download do binário abaixo (ou baixe o código fonte se quiser fazer um estudo mais aprofundado).

Jogo da velha
(binário para Windows)

Código fonte
(arquivo de projeto Microsoft Visual C++ Express Edition 2008)
Especificações
Nível: intermediário.
Linguagem: C++
Bibliotecas: Allegro 4.2.3 (versão estável).
Ambiente & compilador utilizado: Windows & Microsoft Visual C++ 2008 Express Edition.
O jogo será o clássico jogo da velha com as seguintes regras, limitações e características:
- regras padrões: tabuleiro de 3x3, o primeiro jogador que formar uma linha com 3 itens iguais (horizontal, vertical ou diagonal) ganha o jogo;
- apenas um jogador poderá realizar uma jogada por vez;
- o jogo será apenas entre um jogador (x) e o computador (o), não haverá modo para dois jogadores;
- o controle será realizado através do mouse clicando-se no lugar adequado do grid (mapa);
- não haverá limite de tempo ou dificuldades diferentes;
- arte na resolução de 640x480 (3 canais de 8 bits (24 bytes), rgb sem transparência).
A linguagem utilizada será o C++ juntamente com a biblioteca Allegro 4.2.3 (versão estável).
A estrutura servirá como base para outros jogos da série "jogos clássicos", portanto desenvolveremos o código de uma maneira bem aproveitável.
Apesar do jogo da velha ser um jogo simples, veremos na prática (na seção "Implementação") uma série de conceitos que existem em praticamente todos os jogos, dentre eles:
- o game loop;
- controle de cenas (telas);
- menus;
- desenho de interfaces;
- controle de tempo e fps;
- separação do projeto em diversos arquivos e compartilhamento de código entre eles;
- manipulação de imagens.
Também veremos diversos conceitos de orientação a objetos implementados no C++, incluindo:
- abstração;
- construtores, destrutores;
- polimorfismo;
- herança;
- utilização da STL.
Para realizar o tutorial você já deve ter algum conhecimento básico de C++ (saber declarar classes, conceitos básicos de orientação a objetos, trabalhar com matrizes, ponteiros, etc) e ter um ambiente já configurado.
O tutorial é um pouco longo e complicado, mas é uma boa base para os próximos jogos pois não precisaremos desenvolver novamente os sistemas principais.
O código fonte está em português (incluindo nomes de variáveis, classes, funções e comentários) para facilitar o aprendizado.
Vídeo
Para iniciar, veja um vídeo do jogo em execução:
Estrutura e classes
Vamos começar com a estrutura geral do projeto, observe um diagrama aproximado de classes (neste diagrama não temos a listagem dos métodos):
Onde temos:
- Jogo: uma das classes principais do jogo, representa a interface entre o sistema operacional e a biblioteca Allegro com o nosso jogo;
- Cena: classe base para todas as cenas do jogo, cada cena representa uma tela, contendo seu próprio código para processamento e desenho;
- Menu: representa um menu com seus itens;
- Retangulo: classe utilitária para trabalharmos com retângulo, muito útil para definirmos áreas por exemplo;
- Vetor2D: classe matemática auxiliar que serve para indicar noção de localização e deslocamento no espaço cartesiano (e no nosso sistema de coordenadas da tela);
- Entrada: classe que abstrai algumas funções da entrada efetuada pelo jogador.
- CenaIntroducao: classe com a cena que exibe a introdução do jogo e realiza uma pausa até pressionarmos uma tecla;
- CenaMenu: classe com a cena que exibe o menu principal e permite jogar, ver os créditos ou finalizar o jogo;
- CenaSplash: cena que exibe um pequeno splash screen por alguns segundos;
- CenaCreditos: classe que exibe os créditos do jogo;
- CenaJogo: classe principal onde efetivamente implementamos as regras do jogo;
- Entidade: classe base para todas as entidades que são exibidas na tela;
- MenuItem: representa um item de menu (clicável), possuindo uma imagem normal e outra imagem quando passamos o mouse em cima;
- Mapa: classe que representa o nosso "mapa", no caso representa o grid onde o jogador faz as jogadas, incluindo algumas funções de conversão e desenho;
- Jogador: classe que representa o jogador.
Cada classe terá o seu par de arquivos .h e .cpp, sendo que todos os arquivos referenciaram um arquivo global chamado "globais.h" com definições compartilhadas por todo o projeto.
A classe
Jogo irá executar o game loop no método
executaGameLoop onde será efetuado o gerenciamento de cenas, sendo cada cena responsável por sua entrada, processamento e saída.
O jogo também possui as seguintes características:
Sistemas de coordenadas
O jogo utiliza o sistema de coordenadas da tela, onde a origem é no canto superior esquerdo.
Controle de cenas
O controle de cenas ou telas (representado pelas classes
Cena e suas derivadas,
CenaIntroducao,
CenaMenu, etc) serve para controlarmos o fluxo de telas do jogo, assim definimos qual tela será exibida.
Este controle é parecido quando por exemplo, utilizamos uma máquina de estados finito para controlarmos os estados do jogo, mas no caso do nosso jogo da velha, utilizo uma "pilha de cenas", onde podemos "empilhar" a cena que será executada no topo.
Cada cena serve como um pequeno nodo de processamento independente, onde ela pode receber entrada, processar e exibir uma saída (em cima de uma cena existente por exemplo). Um exemplo de utilização seria fazer uma cena de "pause" onde exibimos uma interface para o pause e podemos fazer outra lógica adicional.
Menus
O menu é implementado em duas classes:
Menu e
MenuItem.
A classe
Menu serve como um conteiner para itens de menu e a classe
MenuItem representa um item de menu, sendo derivada da classe
Entidade, já possui atributos básicos para desenho e posição.
O sistema é bem simples, utilizando funções da camada de entrada, verificamos se houve um clique na área de um item do menu e agimos de acordo.
Timers
Foram utilizados 3 timers no jogo:
- timer executado 60 vezes por segundo para incrementar uma variável utilizada no game loop;
- timer executado a cada milisegundo para servir de apoio para realização de outros cálculos;
- timer executado a cada segundo para atualizar as variáveis que calculam o fps.
Inteligência artificial
Inicialmente a inteligência artificial iria ser implementada utilizando-se um algoritmo Minimax (maiores informações nas referências) mas infelizmente não foi implementada nesta versão.
A inteligência artificial atual apenas faz uma "adivinhação" aleatória da próxima posição e joga em cima.
O código possui um pequeno texto e início da implementação do algoritmo de Minimax(comentado) (fica para um próximo artigo ou exercício para o leitor).
Sistema de entidades
Todas as partes visíveis do jogo são derivadas de uma classe base
Entidade que já possui algumas facilidades para pegar imagens, criar imagens a partir de imagens maiores existentes (sheets), posição e dimensão, etc.
Entrada
Todas as operações de entrada foram abstraídas em uma classe
Entrada para facilitar a manutenção.
Esta classe possui apenas alguns métodos auxiliares para verificar se o jogador pressionou uma tecla do teclado ou do mouse e onde ele pressionou.
O game loop
O game loop foi implementado na classe
Jogo, no método
executaGameLoop.
Basicamente foi utilizada a mesma estrutura dos artigos anteriores, mas a função também possui lógica para o controle de cenas, sendo que cada cena é responsável por seu próprio processamento e desenho.
Implementação
Segue abaixo o código fonte completo do projeto implementando todo o jogo (é necessário clicar em cada arquivo para expandir o conteúdo).
globais.h
matematica.h
matematica.cpp
Jogo.h
Jogo.cpp
Entidade.h
Entidade.cpp
Entrada.h
Entrada.cpp
Cena.h
Cena.cpp
CenaCreditos.h
CenaCreditos.cpp
CenaIntroducao.h
CenaIntroducao.cpp
CenaJogo.h
CenaJogo.cpp
CenaMenu.h
CenaMenu.cpp
CenaSplash.h
CenaSplash.cpp
Jogador.h
Jogador.cpp
Mapa.h
Mapa.cpp
Menu.h
Menu.cpp
MenuItem.h
MenuItem.cpp
main.cpp
Conclusão
Complicado demais para um simples jogo da velha? Talvez, mas a partir de toda esta complicação você tem um jogo completo, funcional e polido, e de quebra, uma boa base para futuros jogos (e um bom entendimento de alguns aspectos base).
Apesar de ser um jogo relativamente simples, com este exemplo foi possível demonstrar diversos conceitos na prática.
Observe que muitas vezes o código responsável pela interface do jogo é relativamente grande em comparação com o código do jogo em si (gameplay) e não deve ser menosprezado o tempo que se leva para desenvolvê-lo.
Com um bom entendimento sobre objetos em C++ (como criá-los, destruí-los, polimorfismo, herança, etc), STL básico e alguns algoritmos é possível facilmente fazer um jogo simples.
O código ficou um pouco extenso, mesmo para um simples jogo da velha, mas serve como base para outros exemplos onde poderemos concentrar apenas na lógica por já ter toda a base pronta.
Até a próxima e bons estudos.
O que poderia ser melhor
- o carregamento de algumas imagens (principalmente a de sprite sheets) está sendo duplicado em algumas cenas;
- melhorar cálculo de posicionamento do menu (ao inves de ser item por item, ter uma maneira mais fácil de ordenar os itens automaticamente);
- seria muito importante abstrair as operações de timer e gerenciamento de recursos.
Referências
- http://pt.wikipedia.org/wiki/Jogo_da_velha
- http://en.wikipedia.org/wiki/Tic-tac-toe
- http://unidev.com.br/phpbb3/viewtopic.php?f=21&t=48340
- http://pt.wikipedia.org/wiki/Minimax
- http://en.wikipedia.org/wiki/Minimax
- http://mathworld.wolfram.com/Tic-Tac-Toe.html
- http://www.din.uem.br/ia/a_multid/jogos/minimax.htm
- http://ai-depot.com/articles/minimax-explained/
- http://www.din.uem.br/ia/a_multid/jogos/programa4.htm
Exercícios
Agora que você já sabe implementar um jogo da velha. Existem outras possibilidades que podemos explorar:
- insira sons e músicas;
- guarde os scores em arquivos separados e os carregue depois (dê uma olhada no tutorial de serialização se quiser complicar);
- faça a inteligência artificial ser imbatível;
- implemente multiplayer;
- fazer todos os recursos (sons e imagens) serem carregados e administrados através de uma classe gerenciadora de recursos (muito interessante).