Tutorial Allegro 5 #3 – Eventos

Padrão

Olá pessoal! Aqui estamos novamente pra mais um post dessa nossa série sobre programação de jogos usando Allegro 5. No post de hoje, iremos incrementar a estrutura utilizada nos dois primeiros posts, além de darmos uma olhada no tratamento de eventos.

No tutorial de hoje, vamos tratar um evento simples. Faremos um aplicativo que, quando o usuário clicar sobre o botão fechar (aquele “xis” no canto superior da janela), ela se feche. Coisa simples, mas que nos dará base para utilizarmos os eventos com mais naturalidade nos posts seguintes.

Bom, vamos dar uma olhada no código e ver o que temos de novo. Ainda estamos utilizando a linguagem C.

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

// Para utilizarmos o fprintf
#include <stdio.h>

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

int main(void)
{
    ALLEGRO_DISPLAY *janela = NULL;
    ALLEGRO_BITMAP *imagem = NULL;
    ALLEGRO_EVENT_QUEUE *fila_eventos = NULL;

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

    if (!al_init_image_addon())
    {
        fprintf(stderr, "Falha ao inicializar add-on allegro_image.\n");
        return -1;
    }

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

    imagem = al_load_bitmap("imagem.png");
    if (!imagem)
    {
        fprintf(stderr, "Falha ao carregar o arquivo de imagem.\n");
        al_destroy_display(janela);
        return -1;
    }

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

    al_register_event_source(fila_eventos, al_get_display_event_source(janela));

    al_draw_bitmap(imagem, 0, 0, 0);

    al_flip_display();

    while (1)
    {
        ALLEGRO_EVENT evento;
        ALLEGRO_TIMEOUT timeout;
        al_init_timeout(&timeout, 0.05);

        int tem_eventos = al_wait_for_event_until(fila_eventos, &evento, &timeout);

        if (tem_eventos && evento.type == ALLEGRO_EVENT_DISPLAY_CLOSE)
        {
            break;
        }

        al_draw_bitmap(imagem, 0, 0, 0);
        al_flip_display();
    }

    al_destroy_display(janela);
    al_destroy_event_queue(fila_eventos);

    return 0;
}

No início do código, a única mudança é a inclusão do arquivo de cabeçalho stdio.h. Precisamos dele para utilizarmos a função fprintf() que neste exemplo é utilizada para escrevermos mensagens de erro na saída padrão. Além disso, definimos os atributos LARGURA_TELA e ALTURA_TELA como duas constantes. Essa prática facilita o acesso a esses atributos à medida que o tamanho do programa cresce.

Ao iniciarmos a função main(), temos as variáveis já conhecidas que representam a tela e a imagem de fundo, além de uma variável do tipo ALLEGRO_EVENT_QUEUE. Essa estrutura, no programa chamada de fila_eventos, irá armazenar os eventos que o nosso programa disparar. É uma estrutura do tipo fila (queue) que vai armazenando tudo que acontece no nosso programa. É através dessa estrutura que trataremos a tentativa do usuário de fechar a janela. Mais adiante veremos como se dá o seu uso.

Seguindo o código, podemos ver que agora estamos tratando o retorno das funções de inicialização, indicando a ocorrência de algum erro e encerrando a execução do aplicativo na ocorrência deles. Isso é útil para evitar algum comportamento inesperado da aplicação caso ela não tenha sido inicializada corretamente. No caso do nosso exemplo, estamos direcionando as mensagens de erro para a saída de erros padrão (geralmente o console), definida por stderr, além de encerrar o aplicativo com um código atípico (-1). Realizamos a inicialização da biblioteca Allegro, do add-on para manipulação de imagens, inicializamos a janela, carregamos a imagem de fundo e instanciamos a nossa fila de eventos, com a função al_create_event_queue() em o nosso ponteiro fila_eventos. Logo em seguida, direcionamos todos os eventos ocorridos em nossa janela, chamada janela para a nossa fila_eventos, através da função al_register_event_source(). Com essa chamada, todos os eventos que ocorrerem na janela serão enfileirados na estrutura fila_eventos. Desenhamos a imagem na tela e entramos em nosso looping.

Todo jogo possui um looping. O looping é uma repetição indeterminada, que ficará condicionada a uma ação do jogador. No caso do nosso aplicativo, o looping permanecerá até que o usuário clique no botão para fechar a janela. Vamos agora analisar o que acontece dentro do nosso looping.

Primeiramente, declaramos uma variável do tipo ALLEGRO_EVENT, chamada evento. É esta variável que armazenará os eventos que retirarmos da nossa fila. Em seguida, temos uma variável do tipo ALLEGRO_TIMEOUT nomeada timeout, que em nossa aplicação servirá para definir por quanto tempo aguardaremos por eventos até prosseguir com a execução.

Na linha seguinte, inicializamos a variável timeout chamando a função al_init_timeout(). Passamos como parâmetros o ponteiro da variável (por isso o &) e o tempo do timeout. No caso, utilizaremos 50ms que corresponde a 0,05 segundos. Prosseguindo, chamamos a função al_wait_for_event_until(). Esta função verifica a fila de eventos passada no primeiro parâmetro durante o tempo estipulado pelo timeout passado no terceiro parâmetro. Caso seja encontrado algum evento neste tempo, retorna-se um valor verdadeiro (diferente de 0) e retira-se o primeiro evento da fila, colocando-o na variável passada no segundo argumento. No código, criamos uma flag chamada tem_eventos, que receberá o retorno da função.

Se caso existirem eventos e o tipo do evento for ALLEGRO_EVENT_DISPLAY_CLOSE (que indica que o usuário fechou a janela), saímos do looping com o comando break. A cada giro do looping, redesenhamos a imagem de fundo e atualizamos a tela. Ao sair do looping, destruímos a janela e a fila de eventos, usando o comando al_destroy_event_queue() que recebe a fila de eventos.

Bom, é isso! Aguardem, pois logo logo tem mais um post. Até lá 😀

8 comentários sobre “Tutorial Allegro 5 #3 – Eventos

  1. Tiago Augusto Ferreira

    Olá Rafael, você poderia me explicar este trecho de código abaixo:

    int tem_eventos = al_wait_for_event_until(fila_eventos, &evento, &timeout);

    if (tem_eventos && evento.type == ALLEGRO_EVENT_DISPLAY_CLOSE)

    não consegui entender o por que da criação da variavel tem_eventos e sua utilização dentro do if, sendo que sem ela o programa não apresenta nenhum erro. E outra duvida é sobre qual parâmetro é utilizado para saber se o evento é do tipo ALLEGRO_EVENT_DISPLAY_CLOSE.

    Desde ja, obrigado.

    • Olá Tiago!

      A variável retorna um valor diferente de 0 (ou seja, verdadeiro em C), para o caso de, ao fim do timeout ter ocorrido algum evento. O uso dessa variável é para evitar utilizar uma referência inválida em evento.type. Acredito que isso já ajude a responder sua pergunta. O que ocorre é que, se um evento ocorreu dentro do tempo de timeout, ele é colocado na variável passada no segundo parâmetro (no nosso caso, evento).

      Nos posts posteriores, utilizo uma abordagem diferente para manipulação de eventos, que acredito que possa clarificar melhor o uso.

      Obrigado pela visita! 🙂

      • Tiago Augusto Ferreira

        Ola,

        muito obrigado pela resposta, compreendi perfeitamente o que disse. Estou tentado começar a programar com allegro novamente e desta vez pretendo conseguir dar continuidade aos meus estudos.

        Novamente muito obrigado e parabéns pelo blog.

  2. Tiago Augusto Ferreira

    Ola Rafael, estou trabalhando em um código para converter uma imagem para tons de cinza, só que não estou conseguindo, você poderia me dar uma dica onde posso modificar meu código para dar certo, com este código a imagem sai amarela. Desde ja, obrigado.

    altura_bitmap = al_get_bitmap_height(imagem);
    largura_bitmap = al_get_bitmap_width(imagem);
    fprintf(stderr,”%d,%d\n”,largura_bitmap,altura_bitmap);

    //declaração de variaveis
    ALLEGRO_COLOR cor_pixel,temp_pix;
    ALLEGRO_BITMAP *temp_bitmap = al_create_bitmap(largura_bitmap,altura_bitmap);
    ALLEGRO_DISPLAY *temp_display = al_get_current_display();
    int x,y;
    int h;

    al_lock_bitmap(imagem,al_get_bitmap_format(imagem),ALLEGRO_LOCK_READWRITE);
    al_set_target_bitmap(temp_bitmap);

    for(y=0;y<altura_bitmap;y++)
    {
    for(x=0;x<largura_bitmap;x++)
    {
    cor_pixel = al_get_pixel(imagem,x,y);
    h = ((0.30*(255*cor_pixel.r)) + (0.59*(255*cor_pixel.g)) + (0.11*(255*cor_pixel.b)));
    //h = ((cor_pixel.r) + (cor_pixel.g) + (cor_pixel.b))/3;
    temp_pix.r = h;
    temp_pix.g = h;
    temp_pix.b = h;
    al_put_pixel(x,y,temp_pix);
    fprintf(stderr," p = %d\n",h);
    }
    }

  3. Jonathan

    Ótimo tutorial, Rafael Toledo. Só devo notar que as quatro primeiras linhas de código dentro do with estão sendo desnecessariamente repetidas, ou estou engando?

  4. Lucas Welter

    ola Rafael estou tendo problemas com o carregamento das imagens, estou usando o visual studio, se puder me ajudar agradeço.
    abs.

Deixe uma resposta