Como fazer um menu com Allegro - parte 1
Hoje iremos aprender a fazer um menu simples em Allegro, utilizando alguns bitmaps e um pouco de lógica podemos ter um menu perfeitamente funcional, flexível e fácil de fazer.
Ao término do tutorial teremos o seguinte menu:

O menu com todas as suas opções
Ao passar o mouse sobre o item do menu teremos um pequeno efeito:

Opção do menu com o mouse em cima
Recursos
O nosso menu terá os seguintes “recursos”:
- inserção de itens em qualquer lugar da tela;
- cada item poderá ter uma imagem diferente quando o mouse passar em cima;
- ação personalizada para cada item do menu.
Implementação
Agora que vimos a teoria vamos à implementação:
- Para iniciar crie um projeto chamado Menu e utilize a base inicial deste tutorial;
- Baixe este arquivo e extraia para a pasta do seu projeto (ou utilize suas próprias imagens);
- No arquivo main.cpp, após a linha de inclusão da biblioteca allegro (#include <allegro.h>), insira as linhas abaixo:
#include <string> #include <vector> using namespace std;
Estas três linhas incluem as bibliotecas que utilizaremos (string e vector) e declara que utilizaremos o espaço de nomes std (maiores explicações depois).
Após a declaração da variável *bmp (BITMAP *bmp) vamos inserir o código das classes:
/*
=======
MenuItem
Representa o item de um menu
=======
*/
class MenuItem {
private:
int x, y; // localizacao deste item de menu na tela
// esta funcao verifica se o cursor do mouse esta em cima (dentro) deste item de menu
// se estiver, retorna true, caso contrario retorna false
bool mouseDentro() {
int x1 = x;
int y1 = y;
int x2 = x1 + pic->w;
int y2 = y1 + pic->h;
if (mouse_x > x1 && mouse_x < x2 && mouse_y > y1 && mouse_y < y2)
return true;
else
return false;
}
public:
BITMAP *pic; // imagem do item do menu
BITMAP *picMouseDentro; // imagem do item do menu (quando o mouse estiver em cima)
bool isMouseDentro; // flag que indica se o mouse esta ou nao dentro do menu
void (*funcao)(); // ponteiro para a funcao que sera executada quando o usuario clicar neste menu
MenuItem(int posX, int posY, string imagem, string imagemDentro, void (*pFuncao)()) {
isMouseDentro = false;
// define a posicao
x = posX;
y = posY;
// carrega as figuras do menu
pic = load_bitmap(imagem.c_str(), NULL);
picMouseDentro = load_bitmap(imagemDentro.c_str(), NULL);
// se especificou uma funcao, passa ela
if (pFuncao)
funcao = pFuncao;
}
// acao que este item de menu ira realizar
void realizaAcao() {
if (funcao != NULL)
(funcao)(); // chama a funcao
}
// atualiza este item de menu
void atualiza() {
// se o mouse passar em cima, executa a acao deste menu
if (mouseDentro()) {
isMouseDentro = true;
// se clicar realiza a acao
if (mouse_b & 1) realizaAcao();
} else {
isMouseDentro = false;
}
}
// desenha este item de menu na tela
void desenha(BITMAP *bmp) {
if (isMouseDentro)
draw_sprite(bmp, picMouseDentro, x, y);
else
draw_sprite(bmp, pic, x, y);
}
~MenuItem() {
}
};
/*
=======
Menu
Classe que gerencia os menus
=======
*/
class Menu {
private:
vector<MenuItem> items; // representa os itens do menu
public:
Menu() {}
// verifica a logica para todos os itens do menu
void atualiza() {
int totalItems = items.size();
for (int i = 0; i < totalItems; i++) {
items[i].atualiza();
}
}
// desenha os itens de menu
void desenha(BITMAP *bmp) {
int totalItems = items.size();
for (int i = 0; i < totalItems; i++) {
items[i].desenha(bmp);
}
}
// adiciona um item ao menu
void adicionaItem(MenuItem menuItem) {
items.push_back(menuItem);
}
~Menu() {
int totalItems = items.size();
for (int i = 0; i < totalItems; i++) {
// desaloca a memoria alocada para este item
destroy_bitmap(items[i].pic);
destroy_bitmap(items[i].picMouseDentro);
}
}
};
// ===================
// funcoes dos menus
// ===================
void mnuNewGame() {
}
void mnuOptions() {
}
void mnuHighScores() {
}
void mnuExit() {
fimJogo = true;
}
// ===================
// fim das funcoes dos menus
// ===================
Agora que temos as classes definidas e explicadas é hora da inicialização.
Após a chamada da função Inicializa na função main, insira o código abaixo:
// inicializa o gerenciador do menu
Menu *menu = new Menu();
// agora adiciona os itens do menu
// new game
MenuItem itemNewGame(187, 51, "mnuNewGame.bmp", "mnuNewGameM.bmp", mnuNewGame);
menu->adicionaItem(itemNewGame);
// options
MenuItem itemOptions(187, 122, "mnuOptions.bmp", "mnuOptionsM.bmp", mnuOptions);
menu->adicionaItem(itemOptions);
// highscores
MenuItem itemHighScores(187, 199, "mnuHighscores.bmp", "mnuHighscoresM.bmp", mnuHighScores);
menu->adicionaItem(itemHighScores);
// exit
MenuItem itemExit(187, 274, "mnuExit.bmp", "mnuExitM.bmp", mnuExit);
menu->adicionaItem(itemExit);
Já temos o menu inicializado, o que precisamos agora é fazer o gameloop atualizar a lógica do menu e desenhá-lo na tela.
Dentro do loop iniciado por:
while (ticks && !fimJogo) {
e após a linha:
if (key[KEY_ESC]) fimJogo = true;
insira o código:
menu->atualiza();
e para desenhar o menu, antes da linha:
textprintf(bmp, font, 0, 0, -1, "FPS: %i", fps);
insira o código:
// limpa a tela com a cor branca
clear_to_color(bmp, makecol(255, 255, 255));
Agora já temos a tela branca, só falta exibir o menu e o cursor do mouse, para isso após a linha:
textprintf(bmp, font, 0, 0, -1, "FPS: %i", fps);
insira:
// desenha o menu
menu->desenha(bmp);
// exibe o cursor do mouse na tela
show_mouse(bmp);
e para finalizar, antes da chamada à função Finaliza, insira o código abaixo para destruir corretamente o menu e liberar a memória alocada por ele:
delete menu;
Funcionamento
Basicamente o menu possui duas classes: Menu que cuida para gerenciar todos os itens do menu, e MenuItem que representa um item de menu.
A classe Menu possui os itens de menu em um vector, permitindo assim inserirmos dinamicante quantos itens forem necessários. Esta classe simples apenas possui métodos para adicionar um item de menu, atualizar a lógica dos mesmos e desenhá-los na tela.
A classe MenuItem é o núcleo do menu. Cada item do menu possui algumas propriedades chave tais como posição (int x, y), uma imagem para ser exibida normalmente e outra para ser exibida quando o mouse passa em cima, uma flag para indicar se o mouse está passando em cima e o mais importante: um ponteiro para uma função que será chamada assim que o menu for clicado.
Esta classe também implementa métodos para desenhar o item de menu e atualizar a sua imagem caso o mouse esteja em cima.
Algumas observações: eu deixei o código todo no mesmo arquivo (não aconselhável exceto para exemplos mais triviais) para ajudar as pessoas que tem dificuldades em compilar arquivos separados e evitar complicações com variáveis não definidas, variáveis externas, etc…
Se quiser o projeto pronto baixe este arquivo (arquivo de projeto para o Dev-C++).
Exercícios
Agora que você já viu uma implementação básica, tente os exercícios abaixo:
- faça o menu ser controlável por teclado;
- implemente animação nos itens do menu;
- toque um som ao passar o mouse em cima do menu ou ao clicar em um item;
- faça o item do menu “se animar” ao passar o mouse em cima;
- as possiblidades são grandes, experimente…
Na segunda parte veremos em detalhes como controlar os “estados” do jogo para poder trocar mais facilmente as telas (podendo inclusive implementar um menu em cada uma ou iniciar o jogo).
Espero que tenham gostado.
PS: estou pensando em distribuir os códigos fontes com arquivo de projeto do Microsoft Visual C++ Express Edition 2008 ao invés de utilizar o Dev-C++, se alguém tiver alguma observação agora é a hora.
1 comentário September 23rd, 2008
