Tutorial Allegro 5 #5 – Utilizando o Mouse

Padrão

Olá, leitores!

Hoje vamos começar a falar sobre como manipular as entradas em nossos jogos. Neste post, veremos como tratar os eventos do mouse e como utilizá-los em nossa aplicação. Pra quem já conheceu a versão anterior da Allegro, verá que com a sua estruturação em eventos, ficou muito mais organizado obter as entradas.

Vamos ao código de nossa aplicação e em seguida discutiremos o que tem de novo nele. A aplicação consiste em um retângulo centralizado na tela e um pequeno retângulo vermelho no canto inferior direito da tela. Se o ponteiro do mouse estiver sobre este retângulo central, ele fica verde. Senão, fica branco. E ao clicar sobre o retângulo vermelho, a aplicação é encerrada.

// Os arquivos de cabeçalho
#include <allegro5/allegro.h>

#include <stdio.h>

// Atributos da tela
const int LARGURA_TELA = 640;
const int ALTURA_TELA = 480;

int main(void)
{
    ALLEGRO_DISPLAY *janela = NULL;
    ALLEGRO_EVENT_QUEUE *fila_eventos = NULL;
    ALLEGRO_BITMAP *botao_sair = NULL, *area_central = 0;
    // Flag que condicionará nosso looping
    int sair = 0;

    if (!al_init())
    {
        fprintf(stderr, "Falha ao inicializar a Allegro.\n");
        return -1;
    }

    janela = al_create_display(LARGURA_TELA, ALTURA_TELA);
    if (!janela)
    {
        fprintf(stderr, "Falha ao criar janela.\n");
        return -1;
    }

    // Configura o título da janela
    al_set_window_title(janela, "Rotinas de Mouse - www.rafaeltoledo.net");

    // Torna apto o uso de mouse na aplicação
    if (!al_install_mouse())
    {
        fprintf(stderr, "Falha ao inicializar o mouse.\n");
        al_destroy_display(janela);
        return -1;
    }

    // Atribui o cursor padrão do sistema para ser usado
    if (!al_set_system_mouse_cursor(janela, ALLEGRO_SYSTEM_MOUSE_CURSOR_DEFAULT))
    {
        fprintf(stderr, "Falha ao atribuir ponteiro do mouse.\n");
        al_destroy_display(janela);
        return -1;
    }

    // Alocamos o retângulo central da tela
    area_central = al_create_bitmap(LARGURA_TELA / 2, ALTURA_TELA / 2);
    if (!area_central)
    {
        fprintf(stderr, "Falha ao criar bitmap.\n");
        al_destroy_display(janela);
        return -1;
    }

    // Alocamos o botão para fechar a aplicação
    botao_sair = al_create_bitmap(100, 50);
    if (!botao_sair)
    {
        fprintf(stderr, "Falha ao criar botão de saída.\n");
        al_destroy_bitmap(area_central);
        al_destroy_display(janela);
        return -1;
    }

    fila_eventos = al_create_event_queue();
    if (!fila_eventos)
    {
        fprintf(stderr, "Falha ao inicializar o fila de eventos.\n");
        al_destroy_display(janela);
        return -1;
    }

    // Dizemos que vamos tratar os eventos vindos do mouse
    al_register_event_source(fila_eventos, al_get_mouse_event_source());

    // Flag indicando se o mouse está sobre o retângulo central
    int na_area_central = 0;
    while (!sair)
    {
        // Verificamos se há eventos na fila
        while (!al_is_event_queue_empty(fila_eventos))
        {
            ALLEGRO_EVENT evento;
            al_wait_for_event(fila_eventos, &evento);

            // Se o evento foi de movimentação do mouse
            if (evento.type == ALLEGRO_EVENT_MOUSE_AXES)
            {
                // Verificamos se ele está sobre a região do retângulo central
                if (evento.mouse.x >= LARGURA_TELA / 2 - al_get_bitmap_width(area_central) / 2 &&
                    evento.mouse.x <= LARGURA_TELA / 2 + al_get_bitmap_width(area_central) / 2 &&
                    evento.mouse.y >= ALTURA_TELA / 2 - al_get_bitmap_height(area_central) / 2 &&
                    evento.mouse.y <= ALTURA_TELA / 2 + al_get_bitmap_height(area_central) / 2)
                {
                    na_area_central = 1;
                }
                else
                {
                    na_area_central = 0;
                }
            }
            // Ou se o evento foi um clique do mouse
            else if (evento.type == ALLEGRO_EVENT_MOUSE_BUTTON_UP)
            {
                if (evento.mouse.x >= LARGURA_TELA - al_get_bitmap_width(botao_sair) - 10 &&
                    evento.mouse.x <= LARGURA_TELA - 10 && evento.mouse.y <= ALTURA_TELA - 10 &&
                    evento.mouse.y >= ALTURA_TELA - al_get_bitmap_height(botao_sair) - 10)
                {
                    sair = 1;
                }
            }
        }

        // Limpamos a tela
        al_clear_to_color(al_map_rgb(0, 0, 0));

        // Colorimos o bitmap correspondente ao retângulo central,
        // com a cor condicionada ao conteúdo da flag na_area_central
        al_set_target_bitmap(area_central);
        if (!na_area_central)
        {
            al_clear_to_color(al_map_rgb(255, 255, 255));
        }
        else
        {
            al_clear_to_color(al_map_rgb(0, 255, 0));
        }

        // Colorimos o bitmap do botão de sair
        al_set_target_bitmap(botao_sair);
        al_clear_to_color(al_map_rgb(255, 0, 0));

        // Desenhamos os retângulos na tela
        al_set_target_bitmap(al_get_backbuffer(janela));
        al_draw_bitmap(area_central, LARGURA_TELA / 2 - al_get_bitmap_width(area_central) / 2,
                       ALTURA_TELA / 2 - al_get_bitmap_height(area_central) / 2, 0);
        al_draw_bitmap(botao_sair, LARGURA_TELA - al_get_bitmap_width(botao_sair) - 10,
                       ALTURA_TELA - al_get_bitmap_height(botao_sair) - 10, 0);

        // Atualiza a tela
        al_flip_display();
    }

    // Desaloca os recursos utilizados na aplicação
    al_destroy_bitmap(botao_sair);
    al_destroy_bitmap(area_central);
    al_destroy_display(janela);
    al_destroy_event_queue(fila_eventos);

    return 0;
}

Bom, vocês podem perceber que temos vários elementos novos neste código. Vamos passo a passo descrevendo o funcionamento de cada coisa. Inicialmente, dentro da função main() temos a declaração da tela e dois bitmaps que representarão os dois retângulos que teremos em nossa janela. Além disso, temos uma variável chamada sair que será a condição para sair do looping principal. Na parte de inicializações, temos o comando para configurar o título da janela. Como já foi dito anteriormente, a Allegro 5 tem suporte a várias janelas. Assim, precisamos especificar na função al_set_window_title() a janela que receberá a alteração e qual o nome que desejamos configurar. Mais à frente, na linha 35, temos a inicialização do mouse. Com a chamada à função al_install_mouse() dizemos ao nosso aplicativo para relevar os dados vindos do mouse. Em seguida, fazemos uma chamada à função al_set_system_mouse_cursor(), atribuindo um dos ponteiros padrão do sistema. Passamos como argumento a janela que vamos utilizar o ponteiro e qual ponteiro utilizar. No exemplo utilizamos o cursor padrão do sistema, através da constante ALLEGRO_SYSTEM_MOUSE_CURSOR_DEFAULT, mas é possível configurar qualquer outro (aqui você tem a lista de constantes). Em seguida, nas linhas 51 e 60 criamos os dois bitmaps referentes aos dois retângulos que farão parte da nossa janela, através da função al_create_bitmap() que recebe, respectivamente, a largura e a altura do bitmap em pixels. Em seguida criamos a nossa fila de eventos e, na linha 78, dizemos que vamos tratar os eventos originados no mouse (já havíamos visto como registrar os eventos da janela no tutorial 3). Em seguida temos o nosso looping principal, condicionado à variável sair. Dentro do looping, verificamos se a fila de eventos não está vazia, através da função al_is_event_queue_empty(). Caso haja eventos, retiramos o primeiro evento e verificamos se ele é originário do movimento do mouse (ALLEGRO_EVENT_MOUSE_AXES) ou do clique do botão, que nesse caso eu tratei como o movimento de soltar o botão (ALLEGRO_EVENT_MOUSE_BUTTON_UP), evitando uma sobrecarga de eventos caso o usuário mantenha o botão pressionado.

Caso o evento seja de movimento, verificamos o posicionamento do mouse em relação ao retângulo central. Como sabemos que desenhamos o retângulo de maneira centralizada, comparamos utilizando as características da tela e do quadrado. Cabe destacar que o evento nos fornece uma série de dados, como por exemplo, a posição do mouse no momento em que o evento ocorreu. No exemplo, acessamos as coordenadas através de evento.mouse.x e evento.mouse.y. Outro ponto importante são as funções para obter-se o tamanho de um bitmap. A função al_get_bitmap_width() retorna a largura do bitmap passado como parâmetro e a função al_get_bitmap_height() nos retorna a sua altura, ambos os valores em pixels. Em nossa lógica, caso o ponteiro do mouse esteja dentro do retângulo central, atribuímos um valor à variável. Já se o evento seja um clique do mouse, verificamos se o clique foi dentro da área que corresponde ao retângulo vermelho. Caso positivo, atribuímos o valor à flag de saída.

Completando o looping, temos o trecho de código que se encarrega de atualizar a tela. Primeiramente “apagamos” o conteúdo da tela, pintando-a de preto. Após isso, modificamos o nosso bitmap de destino para a área central, chamando a função al_set_target_bitmap(). Lembra que eu comentei que a Allegro funcionava como uma máquina de estados? Então, é através disso que a modificamos para o bitmap de destino que desejamos. Após a troca, colorimos o retângulo de acordo com o conteúdo da flag. Em seguida mudamos para o bitmap que representa o botão de sair e o colorimos de vermelho. No final voltamos à tela (para acessar o bitmap correspondente à tela, precisamos chamar a função al_get_backbuffer()) e aplicamos os dois retângulos a ela.

Por fim, fora do looping, encerramos a aplicação desalocando os bitmaps e destruindo a tela e a fila de eventos. E pronto!

Para compilar, basta linkar com o núcleo da Allegro:

  • Windows: -lallegro-5.0.4-mt
  • Linux: -lallegro

Como podem ter percebido, a complexidade dos nossos aplicativos estão aumentando gradativamente. Mas podem ficar tranquilo que não deve ficar muito “pior” que isso daqui pra frente 😉

Só uma notícia: saiu a versão 5.0.5 da Allegro (podem baixar aqui). Então, a partir dos próximos posts, vou compilar utilizando essa versão nova. Na verdade, é só modificar o a versão na hora de colocar a configuração do linker. Nada mais que isso.

Bom pessoal, é isso! Até a próxima! 😀

8 comentários sobre “Tutorial Allegro 5 #5 – Utilizando o Mouse

  1. Jefferson S. Almeida

    tentei fazer algo com o seu projeto, mas não deu muito certo, eu tenho um menu: http://i42.tinypic.com/ajtpqb.jpg

    Minha ideia é colocar quadrados futuramente invisíveis onde teria que clicar com o mouse, uma espécie de mapeamento, mas não sei se essa é a melhor opção para isso.
    Tentei algo abaixo com seu exemplo, aparece quadrado branco, mas ele não muda de cor quando passo o mouse em cima.

    http://pastebin.com/mbdcrAqU

    • Rafael

      Você por um acaso registrou os eventos do mouse à fila de eventos? Também não estou vendo onde você está fazendo o controle de retirar os eventos da fila…

    • Rafael

      Como você está passando a variável ev para a função mouse()? Não achei isso no código. Pensei que talvez ela fosse global, mas também está declarada dentro do looping… muito provavelmente é isso: os eventos estão sendo desenfileirados na ev declarada dentro do looping e, como você não passou ela como parâmetro pra função mouse, ela está considerando a ev global, que na verdade não possui evento algum.

  2. Lucas

    Rafael, poderia me explicar apenas uma coisa? o C executa linha por linha, como você pintou o retangulo? sendo que o looping esta acima da “pintura” do retângulo?

  3. Aquiles Maior

    Olá Rafael boa tarde,
    cara seus tutoriais são excelentes parabéns. Poderia me adar uma ajuda, como seria o evento para arrastar o objeto e soltar usando o mouse, tipo aquele esquema do angry birds. Obrigado!!!

  4. Marcio H Machado

    Opa, quando executo o exemplo, dá erro programa parou de funcionar e fica branca a tela e fecha :/
    ps: estou utilizando alegro 5.0.9

Deixe uma resposta