O Game Loop - parte 2

October 3rd, 2007

Conforme prometido, vamos começar a partir do básico o ensino dos conceitos. O “Game Loop” (Laço do jogo) é o principal deles.

Atenção: depois de entender o conceito do Game Loop, você poderá programar pequenas “animações”, terá uma noção geral de como se estrutura um jogo e principalmente terá um pouco de experiência. Mais tarde com esse conceito “dominado”, se você aprender um pouco de detecção de colisão e como pegar a entrada do jogador já poderá fazer jogos simples.

O foco deste artigo será no desenvolvimento de um Game Loop utilizando a linguagem C e a biblioteca Allegro. Não ensinarei como você poderá fazer para compilar o código fonte e nem instalar o ambiente (veja no final do artigo a seção de Referência para maiores detalhes).

Para estudar este artigo eu recomendo:

  • digitar os códigos fontes (ao invés de apenas ler e colar)
  • ler com calma e ir praticando (isso não fará sentido em um ou dois dias, você não pegará de imediato e conforme for experimentando (ESSENCIAL) aprenderá muito mais do que o artigo ensinou)
  • se possível imprima o conteúdo para poder ir lendo e digitando

O Game Loop é o coração de um jogo, nele se encontra a organização do fluxo que permite um jogo continuar rodando baseado nas condições em que foi programado & exposto. Para entender o Game Loop é necessário primeiro entender um outro conceito básico em programação de computadores:

Fluxo de entrada, processamento e saída

Fluxo de entrada, processamento e saída

Onde:

  • ENTRADA: constitui a parte em que o sistema recebe os dados do ambiente
  • PROCESSAMENTO: parte em que baseado nos dados que o sistema possui, ele irá processá-los
  • SAÍDA: parte em que após o processamento for concluído, o sistema emitirá uma saída com os resultados.

Simples? Sim, na teoria. Este conceito básico nos ajuda a visualizar o Game Loop que nada mais é do que este ciclo executando indefinidamente com uma diferença fundalmental: o ciclo continua executando mesmo que não haja uma entrada no sistema. Conseguiu visualizar? Esta diferença fundamental entre um jogo e um aplicativo convencional nos permite fazer a interatividade de um jogo. Em um Game Loop temos:

  • ENTRADA: aqui temos a parte responsável por pegar todos os dados que o jogador envia via teclado, mouse, sensor ótico, tela touch-screen, etc. Aqui também temos que levar em consideração as limitações do dispositivo de entrada (se houver), tempo de leitura das informações, repetições, etc…
  • PROCESSAMENTO: aqui temos todo o processamento da lógica do jogo: detecção de colisão, física, carregamento de níveis, cálculos diversos, tomada de decisões baseadas na entrada, cálculo de inteligência artificial, leitura de arquivos e uma infinidade de outras coisas.
  • SAÍDA: nesta parte (em alguns casos chamada de renderização) temos todas as informações do jogo exibidos na tela, gravados em um arquivo, etc. Esta área é extremamente extensa e rica (John Carmack que o diga). Podemos ter gráficos 2D, 3D, etc, utilizando APIS gráficas como DirectX, OpenGL, SDL, Allegro… Hoje em dia é comum também a utilização de Shaders para incrementar ainda mais a gama de efeitos de visualização.

Agora que conhecemos como se estrutura um Game Loop, vamos ver na prática como podemos organizar estas informações e testar os nossos próprios conceitos.

O nosso Game Loop se dividirá em duas partes (além das partes descritas acima):

  1. uma parte responsável pela execução da lógica do jogo (o processamento deve ser totalmente executado antes de haver uma saída)
  2. uma parte responsável pela saída dos gráficos na tela (no nosso caso utilizaremos uma técnica chamada “Double Buffering”)

Além das partes citadas acima o nosso Game Loop também conterá:

  1. um contador simples de FPS
  2. double buffer

Vamos ao código (está comentado detalhadamente):

/*
======================================
Exemplo da implementacao de um Game Loop em Allegro

por Vitor Almeida da Silva

10/2007

Este codigo pode ser utilizado somente para fins educacionais.
======================================
*/

// inclui o header da biblioteca Allegro
#include <allegro.h>

// prototipos das funcoes utilizadas
void Inicializa();
void Finaliza();
bool fimJogo = false; // flag que indica o fim do jogo
int ticks = 0;  // esta variavel ira ser um contador de ticks do clock principal
                // do jogo
                // cada tick a mais significa que um ciclo de logica do jogo
                // foi processado

// a funcao do timer do tick apenas incrementa o contador
void ticker() {
    ticks++;
}END_OF_FUNCTION(ticker);
int segundos = 0;   // esta variavel ira ser um contador de segundos
                    // a cada segundo passado este contador ira ser incrementado
                    // basicamente eh a chave para fazer um contador de fps
                    // pois se tivermos a quantidade de frames desenhados
                    // e a quantidade de segundos podemos calcular os frames
                    // por segundo

// a funcao do timer do clock apenas incrementa o contador
void clocka() {
    segundos++;
} END_OF_FUNCTION(clocka);// a partir daqui definimos algumas outras variaveis auxiliares

// dimensoes do video
int vid_largura = 640;      // quantidade de pixels de largura do video
int vid_altura = 480;       // quantidade de pixels de altura do video
int vid_profundidade = 32;  // quantidade de bits de profundidade (numero de bits que cada pixel tera)
int frames = 0; // contador da quantidade de frames desenhados na tela
int fps = 0;    // contador de fps (frames por segundo)
BITMAP *bmp;    // bitmap que ira guardar tudo o que desenharmos (para depois ser "jogado" na tela")
                // este bitmap eh de suma importancia pois cada funcao de desenho
                // que desenha em um bitmap provavelmente devera ser apontada
                // para este bitmap

/*
=======
main

Funcao principal
Aqui temos o ponto de entrada da aplicacao onde o Game Loop sera executado
=======
*/
int main(int argc, char *argv[]) {
    // realiza todas as inicializacoes necessarias
    Inicializa();    // a partir deste ponto temos a aplicacao pronta para iniciar o game loop

    // inicio do game loop
    // neste caso o game loop continua executando enquanto nao pressionarmos
    // a tecla esc, mas voce pode verificar por uma outra variavel ou condicao
    while (!fimJogo) {        // inicio do processamento da logica do jogo
        while (ticks && !fimJogo) {            // aqui iniciamos o processamento da logica do jogo
            // neste ponto, voce devera inserir o codigo responsavel
            // processamento da logica do seu jogo, isto inclui: deteccao de colisao
            // inteligencia artificial, calculos diversos, etc
            // aqui esta o segredo:
            // a variavel abaixo "ticks" eh decrementada, mas lembre-se que ela
            // tambem eh incrementada pela funcao de timer, entao qual eh a logica?
            // Lembre-se que o timer do ticks eh executado em um intervalo fixo
            // entao o decremento tambem ocorrera em um intervalo fixo, isto garante
            // que a logica do jogo sera executada pelo menos N vezes por segundo
            // sendo que N eh o intervalo de tempo do timer dos ticks

            if (key[KEY_ESC]) fimJogo = true; // se pressionou a tecla esc entao finaliza o jogo

            ticks--;
        }        // fim do processamento da logica do jogo

        // quando chegarmos ateh aqui significa que a variavel ticks esta com valor
        // zerado, entao ja executamos toda a logica do jogo, agora eh hora
        // de exibir o resultado na tela
        // para exibir o resultado na tela, toda a saida deve ser desenhada
        // no bitmap bmp declarado anteriormente
        // para auxiliar, exibimos o fps, note que estamos desenhando no bmp
        // e utilizando a fonte padrao (font), e fundo transparente (-1)
        // textprintf(bmp, font, 0, 0, -1, " FPS: %i " , fps); // descomente se quiser exibir o fps

        // depois que estiver como frame no bmp, copiamos o conteudo inteiro dele
        // para a tela
        // isto eh realizado pois como esta copia eh feita em memoria, eh muito
        // mais rapido
        blit(bmp, screen, 0, 0, 0, 0, vid_largura, vid_altura);

        // apos a saida concluida, incrementamos o contador de frames, pois
        // temos mais um frame desenhado na tela
        frames++;

        // agora iremos calcular o total de frames por segundo
        // a logica eh a seguinte:

        // se na aplicacao ja passou um segundo
        if (segundos) {
            // calcula o fps sendo que o fps sera a quantidade de frames desenhados
            // dividido pelo numero de segundos passados (no caso 1)
            fps = frames / segundos;
            frames = 0; // como ja calculamos o fps, pode zerar a quantidade de frames desenhados
            segundos = 0; // e zera tambem o numero de segundos, isso garante que o proximo
            // calculo de fps sera executado um segundo depois
        }
    }

    // executa todas as finalizacoes necessarias
    Finaliza();

    return 0;
}END_OF_MAIN();

/*
=======
Inicializa

Realiza qualquer procedimento necessario para iniciar o jogo
Isto inclui iniciar a janela e deixa-la preparada para ser desenhada, iniciar
o estado do jogo, posicoes iniciais, nivel inicial e tudo o que for necessario
=======
*/
void Inicializa() {
    int res;
    allegro_init();
    set_color_depth(vid_profundidade);
    res = set_gfx_mode(GFX_AUTODETECT_WINDOWED, vid_largura, vid_altura, 0, 0);

    if (res != 0) {
        allegro_message(allegro_error);
        exit(-1);
    }

    install_timer();
    install_keyboard();
    install_mouse();

    // aqui inicializamos o nosso timer de ticks
    // trancando o espaco de memoria da variavel ticks e da funcao timer
    // assim elas nao sofrem alteracoes externas
    // note que a funcao ticker sera chamada 60 vezes por segundo, isto eh,
    // a logica do nosso jogo sera executada 60 vezes em um segundo
    LOCK_VARIABLE(ticks);
    LOCK_FUNCTION(ticker);
    install_int_ex(ticker,BPS_TO_TIMER(60));

    // aqui ocorre o mesmo processo para o timer dos segundos
    // note que o timer sera chamado apenas uma vez por segundo
    LOCK_VARIABLE(segundos);
    LOCK_FUNCTION(clocka);
    install_int_ex(clocka,BPS_TO_TIMER(1));

    // cria um bitmap com uma dimensao grande o suficiente para caber na tela
    bmp = create_bitmap(SCREEN_W,SCREEN_H);
    set_window_title("VSoftGames - Teste do Game Loop");
}

/*
=======
Finaliza

Realiza qualquer procedimento de limpeza para finalizar o jogo
=======
*/
void Finaliza() {
    clear_keybuf();
    // destroy_bitmap(bmp);

    // neste ponto voce devera adicionar qualquer tipo de finalizacao que for
    // necessario, limpeza, etc.
}

Após rodar o programa acima, você verá uma tela como a de baixo (observe o contador de fps no canto superior esquerdo):

Game loop em execução

Conclusão

Em um próximo artigo veremos algumas possiblidades com as funções básicas de renderização de “primitivas” no Allegro e em seguida estudaremos algumas técnicas para entrada, assim já será possível fazer jogos bem simples (mas divertidos).

Fique ligado.

Dicas:

  • não tente decorar as funções do Allegro, entenda o conceito, você não conseguirá escrever sem olhar no manual (tive que olhar no manual várias vezes para escrever este código)
  • agora que você possui a estrutura básica de um Game Loop, estude as funções de desenho “primitivas” da biblioteca Allegro e tente desenhar alguma coisa que se mova (ou que não se mova)
  • se você estudar a função “rand” da biblioteca matemática “math.h” poderá incrementar ainda mais estas animações com efeitos aleatórios
  • lembre-se de testar e fazer diversos exemplos.
  • não se preocupe em entender todos os detalhes do código agora, o importante é entender o conceito e ir testando com cada parte para solidificar na mente, não perca tempo com decoreba de estruturas complicadas da linguagem e nome de funções (que você só lembrará as que mais utilizar, isto é, as que você digitou com mais frequência :)

Referências:

  • Allegro Vivace: Um dos melhores tutoriais que eu já vi sobre Allegro, se você não viu, veja agora. Em inglês.
  • Allegro para Iniciantes: Artigo muito bom do site Unidev que ensina como instalar uma IDE para desenvolvimento em C++ com Allegro e ensina também o básico para desenvolver em Allegro (recomendado, este artigo ensina algumas coisas que ainda não comentei no blog).
  • Iniciação de Allegro em C++: Outro artigo, serve como complemento do primeiro.
  • BDJogos: Excelente site nacional com diversos tutoriais sobre Allegro. Altamente recomendado.

Espero que tenham gostado. Até o próximo artigo.

Share/Save/Bookmark

Posts Relacionados

Entrada preenchida em: Tutoriais sobre programação de jogos

2 Comentários Adicione um comentário

  • 1. VSoftGames » Trigon&hellip  |  November 13th, 2007 em 2:43 pm

    [...] a parte prática, recomendo a leitura dos artigos “O game loop parte 1″, “O game loop parte 2″ e “Dicas para aprender a programar jogos eletrônicos”, recomendo também uma visita ao [...]

  • 2. VSoftGames » Como f&hellip  |  September 23rd, 2008 em 1:17 am

    [...] Para iniciar crie um projeto chamado Menu e utilize a base inicial deste tutorial; [...]

Deixe um Comentário

Obrigatório

Obrigatório, hidden

HTML permitido:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Rastreie este post  |  Se inscreva para receber os comentários via RSS


Novidades e Atualizações

Categorias

Posts Recentes

Tags

Veja Também

Comentários Recentes

Parceiros

Links