101
Marvson Allan 21 - Organização de Código Ao terminar a leitura do capitulo 20, você já deve ter uma ótima noção de programação. - Essa noção que você ganhou vale para qualquer linguagem de programação. - Essa noção que você ganhou vale também para iniciar a produção de um jogo em qualquer linguagem. Todas as linguagens usam IF, FOR, arrays e ponteiros.. principalmente funções. Por isso não interessa muito em qual linguagem você está programando, e sim, o que você sabe fazer com ela. Se você está lendo a biblioteca, você com certeza já escreveu algum código em C++ para treinar sua programação e sua forma de pensar. Antes de sair por ai programando jogos, precisamos ter uma certa organização do código fonte dos programas, para que consigamos ler o mesmo código algum outro dia.

Biblioteca Allegro

Embed Size (px)

Citation preview

Page 1: Biblioteca Allegro

Marvson Allan

21 - Organização de Código

Ao terminar a leitura do capitulo 20, você já deve ter uma ótima noção de programação.

- Essa noção que você ganhou vale para qualquer linguagem de programação.

Page 2: Biblioteca Allegro

- Essa noção que você ganhou vale também para iniciar a produção de um jogo em qualquer linguagem.

Todas as linguagens usam IF, FOR, arrays e ponteiros.. principalmente funções.Por isso não interessa muito em qual linguagem você está programando, e sim, o que você sabe fazer com ela.

Se você está lendo a biblioteca, você com certeza já escreveu algum código em C++ para treinar sua programação e sua forma de pensar.

Antes de sair por ai programando jogos, precisamos ter uma certa organização do código fonte dos programas, para que consigamos ler o mesmo código algum outro dia.

VOCÊ PRECISA SABER...

- A organização de código é necessária para que você possa entender o que escreveu.

- A organização de código é obrigatória para que você possa reutilizar seu código fonte.

Para organizar o código você precisa seguir regras fundamentais e obrigatórias.

Declarando variáveis

1 - Ao declarar variáveis, você deve tomar cuidado para não agrupar uma linha muito grande de variáveis.Utilize sempre várias linhas, dando um enter entre os tipos de variáveis.

Exemplo totalmente errado que não deve ser usado:

int a, b, c, valor, numero, xab, contador, teste, coisa;int a = 10, b, c, d=15, valor, coisa, variavel21, contador=0; 

int numero_jogadores, posicao_peca_x, vari;

Exemplo correto:

int a = 0;int b = 0;

int valor = 0;int xab   = 0;

int numero_jogadores = 0;

int posicao_peca_x = 0;

2 - Observe sempre de separar com espaços variáveis que não combinam com as de cima ( por exemplo, posicionamento de peças com números de jogadores ).Isto torna a leitura mais fácil.

3 - Sempre inicie as variáveis.

4 - Sempre deixe espaços ao iniciar as variáveis

Exemplo totalmente errado que não deve ser usado:

int a=0;b=30;

Page 3: Biblioteca Allegro

Exemplo correto:

int a = 0;int b = 30;

 

5 - Ao iniciar as variáveis, mantenha uma ordem nas atribuições;

Exemplo totalmente errado que não deve ser usado:

int aviao = 0;int posicao_x = 0;int posi = 0;

Exemplo correto:

int aviao      = 0;int posicao_x  = 0;int posi       = 0;

 

Usando o exemplo acima, variáveis que tem nome menor ficam longe das que tem nome maior Isso mantem o código ainda mais organizado

Exemplo totalmente errado que não deve ser usado:

int aviao = 0;int b=0; int posicao_x = 0;int x=0; int posi = 0;

Exemplo correto:

int aviao      = 0;int posicao_x  = 0;int posi       = 0;

int b = 0;int a = 0;

 

6 - Tente sempre escrever o nome da variável por exemplo, usando o separador _.Se não for possível, digite um comentário sobre o que a variável faz;

Exemplo totalmente errado que não deve ser usado:

int pos_per;int px;int py;

Exemplo correto:

int posicao_personagem;int personagem_px; // posição x do personagem; int personagem_py; // posicao y do personagem;

Page 4: Biblioteca Allegro

VOCÊ PRECISA SABER...

Abreviar nome das variáveis é preguiça.. se você tem preguiça ao programar, então desista de programar jogos.

7 - Mantenha distancia em números na hora das operações matemáticas.Utilize mais variáveis para ajudar nas operações, de forma que a operação fique fácil de ler 

Exemplo totalmente errado que não deve ser usado:

int num=0; num = 10*(4/2*(3-1));

Exemplo correto:

int num      = 0;int auxiliar = 0;

auxiliar = ( ( 4 / 2 ) * ( 3- 1 ) );num      = 10 * auxiliar;

8 - IMPORTANTE: Indentar sempre os códigos com extruturas de controle.

Exemplo totalmente errado que não deve ser usado:

if(a==1){ ....... }

while (ab!=true) { ....} 

for (i=0;i<10;i++) {....}

Todas as 3 formas acima estão erradas

Exemplo correto:

// Observe os espaços no parenteses, e use sempre a quebra de linha com a identação if ( a == 1 ) {      ........}

while ( ab != true ){       // nunca abra as chaves na linha de cima        ......}

// observe os espaços entre os parametros do for for ( i=0; i<10; i++ ){      .....}

9 - Cuidar da identação quando o código for muito grande

Page 5: Biblioteca Allegro

Exemplo totalmente errado que não deve ser usado: ( exemplo com código maior ilustrativo, você nao precisa entender o que está escrito )

bool selecao_direta( fila *pprimeiro ) {fila *proximo,*atual,*menor;int chave;while (pprimeiro != NULL) {   proximo=(*pprimeiro).proximo;   atual=pprimeiro;   menor=atual;

  while ( proximo != NULL )  {     if ( (*proximo).numero<(*menor).numero)           menor=proximo;           proximo=proximo->proximo;  }    chave = atual->numero;  atual->numero = menor->numero;  menor->numero = chave;   pprimeiro = pprimeiro->proximo;}return true;}

Exemplo correto:

bool selecao_direta( fila *pprimeiro ){

  fila *proximo, *atual, *menor;

  int chave;

  while ( pprimeiro != NULL )  {      proximo  = (*pprimeiro).proximo;      atual    = pprimeiro;      menor    = atual;

      while ( proximo != NULL )      {                  // nesse caso é muito importante usar as chaves " { } " e identar o if           if ( (*proximo).numero < (*menor).numero )          {              menor = proximo;          } 

         proximo = proximo->proximo;      }

      chave = atual->numero;      atual->numero = menor->numero;      menor->numero = chave;

      pprimeiro = pprimeiro->proximo;  }

  return true;}

 

10 - Não utilizar o comando Do WhileO comando do while deixa as linguas de programação confusas, por isso, não é interessante utilizar

Page 6: Biblioteca Allegro

Qualquer código em qualquer linguagem pode ser escrito seguido essas observações importantes.

21 - Adicional 1 - Organização de Código 

Este capítulo é para quem já possuí experiência em programação.

Recentemente fora levantado um tópico no nosso forum sobre a organização do código.No capítulo 21 nós aprendemos a melhor forma de se programar um jogo, organizando o código como programação e não como um projeto.

Com este tutorial adicional, pretendo dar mais dicas para tornar a programação de jogos mais organizada.

Fazer um jogo é um projeto que contém várias etapas.Nem sempre precisamos seguir todas as etapas, mais algumas são indispensáveis para a conclusão sem atrasos ( perda de tempo ) do projeto.

Este tipo de conteúdo é discutido em vários sites e em várias outras áreas, simplesmente por que é um assunto relacionado ao tempo!Quanto mais tempo gasto, mais caro se torna a sua idéia.

Quanto mais organizada for a sua ideia e o seu projeto, melhor ele será implementado e mais rápido o jogo será concluído.

Eu vou abordar aqui a criação de um jogo no estilo Arkanoid.

1 - A idéia:

Como a idéia é criar um jogo ao estilo do Arkanoid,precisamos estudar como este jogo funciona e descrever todos os funcionamentos e idéias do jogo.

Se você está criando um jogo que ainda não foi inventado, o processo será o mesmo.

A descrição da idéia é muito importante para sabermos o que realmente queremos fazer e para adotar mudanças no projeto.

Exemplo:

- Um jogo aonde o jogador controla uma raquete ( ou shape ) que vai rebater uma ou várias bolas.- O objetivo do jogo será destruir todas as peças do cenário, fazendo com que a bola entre em contato com essas peças.- Cada peça vai fazer com que uma ação seja disparada no jogo, criando novas bolas, aumentando a velocidade, etc.- Quanto todas as peças forem destruidas, o jogador é enviado diretamente para a proxima fase.- Quando a bola não for rebatida, o jogador perde uma vida.

Observe que especificamos detalhes do jogo, sua descrição e regras.A descrição inicia da idéia não deveria ser mudada durante a execução do projeto.

Observe que na minha idéia de Arkanoid, mudei alguns conceitos do jogo original.Neste ponto também, são definidas algumas historias e cenarios do jogo, não precisam ser completas.

 

2 - Descrição de Regras

Agora iremos definir as regras do jogo.Essas regras podem sofrer alterações durante a execução do projeto,por que as vezes nem sempre conseguimos programar tudo o que queremos.

- O Jogo vai ter uma historia ?

Page 7: Biblioteca Allegro

- O jogo vai ter uma animação inicial ?? Como ela vai ser ? 

- O Jogo vai ter uma tela inicial ? - Quais serão as opções do menu ?

- Quais serão as telas do jogo ? - Descrever as telas do jogo..

- Descrição das regras para o Jogador - Descrição das regras para o Jogador para cada tela ou cenário

- Descrição das regras do jogo para cada tela ou cenario - Descrição das regras para upgrades, inimigos, jogabilidade

 

Quando você for definir as regras, você não precisa levar em consideração tecnologias que serão utilizadas.Isto por que, as regras poderão ser alteradas mais tarde.

Esta não é a hora de pensar em qual linguagem programar ou qual programa de modelagem utilizar.

 

3 - Identificando atores e tarefas

Esta aqui é uma das partes mais importantes ao se fazer um projeto pois ela define como será feita a programação.Você deve identificar atores de ações nas regras e idéias descritas acima.

Nada muito técnico ainda.

Exemplo:

- Bola

A bola pode se mover pelo cenarioA bola pode colidir com uma peça e destrui-laA bola pode colidir com a raquete do jogador, que vai rebatela para outra direção

- Raquete do jogador

A raquete do jogador pode se mover apenas na horizontal para os ladosA raquete do jogador pode se mover mais rapido ou mais devagarA raquete do jogador pode ter tamanhos diferentesA raquete do jogador pode rebater a bola

- Cenario

O cenario pode ter tamanhos diferentesO cenario pode ter várias peçasO cenario pode ter uma imagem de fundoO cenario pode ter apenas um jogadorO cenario pode ter várias bolasO cenario deve controlar para que as bolas não saiam de seus limites

 

Eu identifiquei alguns atores e tarefas deles acima.Nem sempre se consegue identificar todos os atores, por isso talvez você vá precisar alterar este parte mais tarde.

4 - Criando Classes e métodos

Este método de desenvolvimento não é igual ao UML, Por favor, não confunda! Os atores no UML são usuários e/ou outros meios externos que desenvolvem algum papel em relação ao sistema.

Page 8: Biblioteca Allegro

Você pode usar a UML para ajudar no seu projeto também. Além de definir os atores e possíveis tarefas, você vai definir diagramas que irão ajudar você a identificar quem serão as classes.

Estude UML também!   http://pt.wikipedia.org/wiki/UML

Após identificar os atores do passo 3, podemos procurar quais deles serão classes, e quais funções cada classe vai executar.

Pense da seguinte maneira! Você vai criar uma classe para alguma coisa que irá ter várias variáveis e que irá desempenhar várias tarefas no seu jogo.

Bola, Jogador e Cenário desempenham várias tarefas, como vimos acima, então ficou fácil de saber quem serão nossas classes.

Com as tarefas de cada um, também podemos conhecer de antemão quais serão os metodos que cada um vai fazer.

Exemplo:

Classe Bola

metodo movermetodo desenhar no cenariometodo colisao com peçasmetodo colisao com jogador

Neste ponto, nem sempre vamos descobrir todos os métodos e todas as classes.Porém, neste ponto já podemos descidir como vamos fazer as coisas e se conseguimos fazer tais coisas.

Agora é hora de descidir a linguagem de programação e como será programado o jogo.Agora também é hora de decidir quais classes se comunicarão com outras classes.

Por exemplo:

A classe bola vai ter o método que verifica a colisão? ou a classe cenario ?Quem vai controlar as vidas ? O cenário ou o jogador ?

 

Exemplo de código:

CÓDIGO...

#ifndef BOLAS_H#define BOLAS_H

#include <allegro.h>

class Bolas {

private:

   // contadores auxiliares   int i;   int c;

public:

   int posicao_x;

Page 9: Biblioteca Allegro

   int posicao_y;

   int tamanho;    int cor;

   // controlam a velocidade da bola em x e em y   // desta forma ela pode ir para lugares diferentes na tela   int velocidade_x;    int velocidade_y;

   // informam a direção de movimentação da bola   // pelo cenario

   // para esquerda bola->direcao_x = -1;   // para cima bola->direcao_y = -1;    int direcao_x;   int direcao_y;

   Bolas(); // função construtor   ~Bolas(); // função destrutor

   void iniciar( int pos_x, int pos_y ); // inicia objeto   void destrutor(); // reinicia variáveis do objeto

   void mostrar( BITMAP * fundo ); // imprime a bola no vetor fundo   void refresh(); // atualiza posição da bola

   void adiciona_velocidade_x();   void remove_velocidade_x();

};

#endif

FIM DE CÓDIGO...

 

5 - Programação

Agora você vai criar as classes que encontrou e tentar juntar todas as classes de forma que o jogo rode no final.Bom.. essa minha explicação foi bem superficial, já que isto é bem complicado.

Então vou mostrar como isto é feito na prática! 

- Para programar um jogo você deve ter noção de Orientação a Objeto básica para criar as classes. - Para programar um jogo você precisa ter alguma noção de UML para identificar eventos e classes.

http://pt.wikipedia.org/wiki/Orienta%C3%A7%C3%A3o_a_objetos   http://pt.wikipedia.org/wiki/UMLhttp://bdjogos.com/adrw/c/classes.htm

 

Eu criei um jogo no estilo do Arkanoid para você baixar.O código fonte está incluído e você poderá alterar como quiser.

Neste código fonte existem todos os comentários que você vai precisar para entender como ler o código e como organizar o projeto.

 

Page 10: Biblioteca Allegro

Clique aqui para fazer o download do código fonte

 

Esta forma de programar jogos com certeza não é a melhor forma que existe.Eu apenas abordei um modo prático de programar jogos, procure pesquisar mais sobre isto.

21 - Adicional 2 - Jogo da memória com código fonte

Para esse artigo preparei o código fonte de um jogo da memória simples e objetivo a fim de revisar e fixar os artigos vistos até o momento.

Parece óbvio, mas mesmo assim é bom deixar claro que essa não é a única forma de fazer um jogo da memória.O código varia muito de cada programador. 

Para quem não lembra o jogo da memória consiste em encontrar o par da carta que você acabou de virar. Ao final do código, teremos um resultado simples conforme abaixo:

O Jogo

Precisamos ter em mente como nosso jogo irá funcionar antes de começar a programar ele. 

É bom sempre fazer alguns rascunhos antes de organizar as idéias oficiais. Dessa forma teremos uma visão clara do que iremos precisar durante o desenvolvimento.

Nesse caso, as características desse jogo devem ser as seguintes:

1. Teremos um baralho de 32 cartas. 16 pares de desenhos.

2. A pontuação base inicial é 100.

Page 11: Biblioteca Allegro

3. O Jogador inicia com 0 pontos.

4. Logo no inicio o jogo deve mostrar todas as cartas para que o jogador possa memorizar.

5. Assim que o tempo de memorização das cartas terminar o relógio para marcar o tempo de jogo deve ser disparado.

6. A cada erro o jogador perde uma chance e espera 3 segundos para memorizar as duas cartas erradas. Se zerar as chances o jogo termina e a tabela de Recordes deve ser mostrada.

7. A cada seqüência de acertos a pontuação base deve ser multiplicada pela quantidade de seqüências de acertos até o momento. Por exemplo, se acertar 2 pares seguidos deve ser acrescentado na pontuação 2x100.

O jogo terá 3 níveis de dificuldade. Em cada nível teremos as seguintes alterações:

 

Nível 1:

1. Pontuação base deve ser 100.2. Tempo para mostrar as cartas 15 segundos.

Nível 2:

1. Pontuação base deve ser 150.2. Tempo para mostrar as cartas 10 segundos.

Nível 3:

1. Pontuação base deve ser 200.2. Tempo para mostrar as cartas 5 segundos.

o A cada troca de nível devem ser acrescentadas mais duas chances ao jogador.

o Cada nível de dificuldade terá um fundo e os desenhos das cartas alterados.

o Ao fim do jogo quando o jogador zerar os 3 níveis a tabela de recordes deve aparecer.

o A tabela de recordes deve ter 9 posições e ser organizada pelo nível e pontuação mais altos.

 

 

 

Clique aqui para fazer o download do código fonte

 

 

Biblioteca Allegro

A biblioteca allegro fornece várias rotinas de baixo nível para programadores de C/C++, normalmente

Page 12: Biblioteca Allegro

necessárias na programação de jogos, como entrada do teclado, manipulação de gráficos, manipulação de sons e cronômetros (timer). 

É uma plataforma transversal e trabalha com muitos compiladores diferentes. Desenvolvido originalmente por Shawn Hargreaves, é agora um projeto com contribuições do mundo inteiro. 

Algumas características chaves incluem:

Facilidade de utilização - Vem com documentação e exemplos detalhados.

Portátil - Se a funcionalidade interna não for bastante, há muitos add-ons disponíveis em todas plataformas - sem mudar uma única linha do código, você pode criar versões para Windows, OS X, Linux, DOS e outros! Trabalhe com muitos compiladores, incluindo DJGPP e VC++ Fonte aberta - qualquer um pode contribuir, inclusive você! 

Livre - não lhe custará uma moeda de dez centavos, e não há nenhuma limitação em seu uso.

Para mais informações referente a biblioteca allegro visite o site: http://www.allegro.cc

O objetivo de Allegro.cc é fornecer uma riqueza da informação moderna que pertence à programação e ao gaming Allegro. Eles confiam pesadamente na participação dos colaboradores Allegro e do público geral. Nesse mesmo site existem publicações de vários jogos, quase todos são freewares e sharewares. 

Antes de instalar qualquer jogo o Allegro recomenda ler a documentação completa de cada um porque eles não serão responsáveis por qualquer problema que ocorrer após a instalação.

VOCÊ PRECISA SABER...

Em outras palavras Allegro é uma biblioteca desenvolvida para ajudar os programadores iniciantes no desenvolvimento de jogos sem que seja necessário ler toneladas de livros referentes o assunto.

Basicamente, para que você possa desenvolver um jogo simples e divertido é necessário ter um bom conhecimento de qualquer linguagem de programação, criatividade e um bom conhecimento em bibliotecas gráficas como Allegro, Open GL, Direct x entre outros.

ENTENDO O LAÇO PRINCIPAL DO JOGO

Logo acima vimos que a biblioteca Allegro parece ser uma ferramenta com várias funcionalidades no processo de desenvolvimento de um jogo. Só o fato de não precisarmos ler toneladas de livros sobre matemática, física e APIs já poupa um tempo muito precioso. 

Isso não quer dizer que para desenvolver um jogo comercial de sucesso basta aprender Allegro. 

O conhecimento vai muito mais além da simplicidade. De qualquer forma você vai precisar estudar muito para desenvolver um jogo bom.

Vamos começar pelo inicio do inicio, entendendo o laço principal e utilizando a biblioteca Allegro para chamar as funções. O código abaixo já é um bom começo:

Ao executar o programa você vai ver apenas uma tela do windows com a cor de fundo em preto.Para fechar o programa pressione ESC.

CÓDIGO

//Exemplo de código em Allegro#include <allegro.h>

int main(){   allegro_init();   install_keyboard();

Page 13: Biblioteca Allegro

   set_color_depth(16);   set_gfx_mode( GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0 );

   // Laço principal   while( !key[KEY_ESC] )   {      //Código   }

   allegro_exit();   return 0; 

}END_OF_MAIN();

FIM DE CÓDIGO...

#include <allegro.h>

Esse comando é o mais importante. Sem declarar essa linha não vamos conseguir usar nenhuma função da biblioteca Allegro. Ao chamar as funcões do Allegro e tentar compilar o jogo sem essa linha o compilador irá apresentar uma mensagem de erro dizendo que não conseguiu encontrar as funções. 

VOCÊ PRECISA SABER...

Esse comando indica ao compilador onde ele deve encontrar as funções do allegro.

int allegro_init();

Macro responsável por iniciar a biblioteca allegro. Sem esse comando não conseguimos utilizar nenhuma função do allegro.

Existem pessoas que utilizam a função allegro_exit(), mas o manual do allegro 4.2.1 informa que esse comando não precisa ser usado explicitamente. O allegro chama essa função toda vez que o jogo é fechado. Dessa forma o processo de liberação das bibliotecas da memória tornam-se automáticos. 

int install_keyboard();

Esse comando é responsável por instalar e tornar as interrupções do teclado disponíveis para o nosso projeto. 

Mais adiante vamos ver um topico de como manipular as interrupções do teclado. 

void set_color_depth(int depth);

Essa função seta a profundidade das cores de todas as imagens que você vai carregar durante o projeto. O allegro suporta:

8 bits 256 cores

Page 14: Biblioteca Allegro

16 bits 65.536 mil

24 bits 16,7 milhões

32 bits 16,7 milhões

Exemplo: set_color_depth(32);

O modo 32 bits proporciona o mesmo número de cores que o modo 24 bits, mas gráficos de 32 bits podem ser manipulados muito mais rapidamente do que gráficos de 24 bits. Além disso, gráficos de 32 bits requerem cerca de 25% a mais de memória.

int set_gfx_mode(int card, int w, int h, int v_w, int v_h);

O comando acima é responsável por detectar a placa de vídeo, setar o tamanho da tela em pixel e o posicionamento inicial x,y. 

Para saber o que utilizar no argumento card segue uma tabela abaixo explicando cada opção:

Card Descrição

GFX_TEXT Fecha toda a modalidade de gráficos aberta previamente com set_gfx_mode.

GFX_AUTODETECT O allegro escolhe o drive mais apropriado.

GFX_AUTODETECT_FULLSCREEN

Força o allegro a exibir o projeto em tela cheia.

GFX_AUTODETECT_WINDOWED Força o allegro a exibir o projeto em uma janela.

GFX_SAFE Modo especial para quando você quiser ajustar confiantemente uma modalidade de gráficos e não quer se importar a resolução.

GFX_BWINDOWSCREEN_ACCEL Modalidade exclusiva de Tela cheia. Suporta definições de pixels somente acima ou igual a 640x480.Suporta também aceleração de hardware.

GFX_BWINDOWSCREEN Modalidade idêntica a anterior mas não suporta aceleração de hardware.

GFX_BDIRECTWINDOW Modo gráfico rápido para utilização de janelas porque usa a classe BdirectWindow. Mas nem todas as placas de vídeos suportam.

GFX_BWINDOW Modo normal de trabalho em janela utilizando a classe Bwindow. Esse modo é muito lento.

GFX_BWINDOW_OVERLAY Modalidade de tela cheia usando BWindow como uma folha de prova BBitmap. Esta modalidade não é suportada por todos as placas de vídeos, a profundidade de cores sugeridas é 15, 16 e 32 bits.

Exemplo:

set_gfx_mode(GFX_AUTODETECT_WINDOWED,640, 480, 0, 0); 

O exemplo acima define a tela do jogo como sendo auto detectável em modo janela do windows, 

Page 15: Biblioteca Allegro

640x480 pixels de resolução a partir da posição x= 0 e y=0.

void vsync();

Espera para que um retrace vertical começa. 

O retrace acontece quando o feixe de elétron em seu monitor alcançou o fundo da tela e está movendo para trás para o superior, e se apronta para uma outra varredura. 

Durante este período curto o cartão dos gráficos não está emitindo nenhum dado ao monitor, assim você pode fazer-lhe as coisas que não são possíveis em outras vezes, tais como alterar o palette de cores sem causar cintilar (neve). 

O Allegro esperará automaticamente um retrace antes de alterar o palette ou de fazer algum desdobramento da ferragem, embora, assim que você não necessita normalmente se incomodar com esta função.

Essa explicação é muito importante porque o uso do comando vsync no meio do projeto causa uma perda considerável de desempenho.

while (!key[KEY_ESC]) {}

O comando acima trata-se do loop principal do jogo. Ele quer dizer que fará o loop enquanto ninguém pressionar a tecla ESC. 

Dessa forma ele executa o código do jogo em velocidade de processamento. Mais adiante veremos tópicos relacionados ao timer para controlar o loop do jogo.

Caro leitor, chegamos ao fim do entendimento do laço principal de um jogo. 

Se você Encontrou alguma dificuldade no entendimento desse artigo favor entrar em contato. Agora se prepare porque com essa tela preta dá pra fazer muita coisa!!!

23 - Conceito X e Y

Antes de ler este tutorial, leve em consideração de que você já leu:

- A Biblioteca Allegro- Instalação do Allegro no Dev-CPPInstalação do Allegro no Visual Studio C++ 2005

 

Os números naturais

Números naturais ou números que podem ser contados são os primeiros números que aprendemos na escola.A muito tempo atraz a humanidade precisou inventar uma forma de contar seus alimentos, seus habitantes ou suas criações, desta forma surgio a oportunidade de escrever números em formas de digitos em algum lugar, e assim surgiram:

0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ...

Podemos representa-los em nossa programação atravez do tipo inteiro ( usando principalmente unsigned "sem sinal" ).

Page 16: Biblioteca Allegro

int numero_natural      =  6;int array_numeros[3]  =  { 5, 10, 15 };

 

Os números negativos

Algum tempo depois a humanidade precisou comercializar ou trocar seus objetos ou comida. Ai foi inventado um dos conceitos mais importantes que é o débito de valores ou os números negativos. Os números negativos são representados com um sinal de - "menos" na frente de cada valor.

-1, -2, -3, -4, -5, -6, -7, -8, -9, - ...

Podemos representa-los em nossa programação atravez do tipo inteiro tbm.

int numero_negativo     =  -6;int array_negativos[3]   = { -5, -10, -15 };

Os números inteiros, são somente constituídos dos números naturais {0, 1, 2, ...} e dos seus opostos {-1, -2, -3, ...}.

 

Os números racionais e frações

Em alguma determinada época, também fomos obrigados a dividir comida com os companheiros das cavernas, e foi dai que apareceram as nossas amigas frações. Onde um valor é divido por outro.

10 / 2 = 5;

Quando uma fração não consegue dividir um número, surge os números fracionais.

3 / 2 = 1.5

Estes números são inseridos entre os números inteiros e são representados em nossa programação atravez do tipo float ou double.

doube valor = 1.666;

Um clássico exemplo de número racional é o PI, muito utilizado na programação de jogos.No jogo Worms ele é utilizado para saber aonde um projetil vai cair dependendo de um angulo e força de lançamento.

PI = 3.14159

 

Os número Reais

Page 17: Biblioteca Allegro

Os números reais incluem os números inteiros e os números racionais. Eles são os mais utilizados até hoje pela humanidade, e também são bastante complexos.Os números inteiros são contáveis e os números reais são números incontáveis.

 

O plano cartesiano

O Plano cartesiano é um espaço aonde podes visualizar 2 variáveis importantes para a programação de jogos.As variáveis X e Y.

No desenho acima temos o plano cartesiano, o plano sempre começa da posição 0 que é a origem. Esta origem nem sempre é o meio do plano.Esta origem pode ser por exemplo, o começo de uma fase do jogo Super Mario Bros.

Andando para a direita em linha reta, estamos saindo do ponto de origem e adicionando ao ponto X.Andando para a direita e para cima, estamos saindo do ponto de origem e adicionando ao ponto X e Y.

Resumindo, os pontos X e Y são coordenadas do plano cartesiano, e é com essas coordenadas que iremos escrever gráficos na tela do computador ou do video-game.

Todo ponto é sempre representado por ( X, Y ), exemplo: ( 2, 3 )

Exemplos de vários pontos X,Y em um plano cartesiano que poderia ser a tela de um jogo:

Page 18: Biblioteca Allegro

 

Agora vamos brincar de escrever na tela usando os pontos X e Y.

CÓDIGO...

// Coordenadas X e Y//Código de Exemplo#include <allegro.h>

int main(){   allegro_init();   set_color_depth(16);   install_keyboard();   set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0);

   // nosso programa atual    // Testando coordenadas X e Y    int x = 0;   int y = 0;

   textout_ex(screen, font, "Mensagem em 0,0 da tela", x, y, makecol(255, 0, 0), -1); 

   // Laço principal    while( !key[KEY_ESC] )   {

   }

   allegro_exit();   return 0;}END_OF_MAIN();

FIM DE CÓDIGO...

 

Page 19: Biblioteca Allegro

Neste momento você não precisa saber os comandos que vc não conhece, apenas teste o programa para entender a função da variável X e Y..

VOCÊ PRECISA SABER...

Executando o exemplo acima, percebemos que no allegro, a coordenada inicial do monitor é bem em cima da tela na esquerda. Isto é muito importante, no open gl por exemplo, a coordenada inicial é bem no meio da tela.

O que acontece se almentar a variável X ou Y ? Veja o exemplo:

CÓDIGO...

// Coordenadas X e Y//Código de Exemplo#include <allegro.h>

int main(){   allegro_init();   set_color_depth(16);   install_keyboard();   set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0);            // nosso programa atual    // Testando coordenadas X e Y    int x = 0;   int y = 0;         x = x + 100;   textout_ex(screen, font, "Mensagem em 100,0 da tela", x, y, makecol(255, 0, 0), -1);       x = 0;   y = y + 100;   textout_ex(screen, font, "Mensagem em 0,100 da tela", x, y, makecol(255, 0, 0), -1);       x = x + 150;   y = y + 150;   textout_ex(screen, font, "Mensagem em 150,150 da tela", x, y, makecol(255, 0, 0), -1);       // Laço principal   while( !key[KEY_ESC] )   {      }         allegro_exit();   return 0;}END_OF_MAIN();

24 - Movendo um objeto na tela

Neste tutorial iremos ver como mover um objeto na tela usando as coordenadas x e y.

Todo desenho, imagem ou objeto que pode ser inserido na tela em qualquer linguagem de programação possui a propriedade X e Y.Muitas vezes essas propriedades podem estar com outros nomes ( no caso do VB é Top e Left ).

No código abaixo iremos ver alguns comandos novos. O mais importante são os comandos de reconhecimento de teclado.

Page 20: Biblioteca Allegro

Atravéz desses comandos nós modificaremos o valor de X e de Y e redesenhamos um quadrado na tela na nova posição.

O código é bem fácil de se entender, precione ESC para sair do programa quando for testar ele:

 

CÓDIGO...

// Movendo um objeto nas coordenadas x e y#include <allegro.h>

int main(){   allegro_init();   set_color_depth(16);   install_keyboard();   set_gfx_mode(GFX_AUTODETECT_FULLSCREEN, 640, 480, 0, 0);         // inicio da programação atual   int x = 0;   int y = 0;      // dimensao do quadrado   // variáveis constantes nunca mudam durante a execução do programa   const int tamanho = 100;         // Laço principal   while( !key[KEY_ESC] )   {      if ( key[KEY_UP] )      {         y--;      }            if ( key[KEY_DOWN] )      {         y++;      }            if ( key[KEY_LEFT] )      {         x--;      }            if ( key[KEY_RIGHT] )      {         x++;      }            // esta função limpa a tela ou um objeto que tem buffer no allegro       clear( screen );            // escrevemos um quadrado na tela na posição x e y que podem ter sido modificadas      rectfill( screen, x, y, x+tamanho, y+tamanho, makecol(255, 0, 0) );            // imprimimos as coordenadas x e y na tela para o usuário ver      textprintf_ex( screen, font, 10, 10, makecol(255,0,0), -1, "Variavel X: %d", x);      textprintf_ex( screen, font, 10, 20, makecol(255,0,0), -1, "Variavel Y: %d", y);            // essa função faz o allegro esperar um pouco antes de voltar para o while      rest(10);   }         allegro_exit();   return 0;

Page 21: Biblioteca Allegro

}END_OF_MAIN();

25 - Double Buffer - Técnica de desenhar na tela

Quando começamos a desenhar vários objetos primitivos na tela, e movemos ele atraves do teclado ( na execução do jogo ) ou mesmo animamos figuras .bmp, muitas vezes percebemos que a imagem do jogo fica piscando.

O Nome deste efeito é flicker, onde o monitor acaba imprimindo uma tela em preto antes da nova imagem do jogo ser desenhada na variável screen ( variável que representa o monitor no Allegro ). Formando a impressão de que a tela está piscando.

Para resolver este problema, utilizamos a tecnica de Double Buffer, ou seja, criar um buffer ( lugar de armazenamento ) aonde desenhamos tudo o que deve ser desenhado na tela, e só depois que tudo estiver desenhado no buffer, ai sim, imprimimos o buffer na tela ( screen ).

Desta forma, a tela ( screen ) vai ter sempre a ultima imagem gerada para ela. E enquanto tudo não for desenhado no buffer, a tela continua sendo a antiga. Assim a tela não irá piscar mais.

Esse problema geralmente acontece quando o movimento do objeto for muito rápido ou quando a tela é atualizada antes de todos os objetos serem desenhados no screen. 

A tecnica de double buffer no allegro é bem simples e também vale para qualquer outra biblioteca gráfica ( aqui estamos dizendo, o exemplo está em allegro, mais você pode usar em DX ou Open GL )

 

Exemplo sem Double Buffer:

Possivelmente o quadrado vermelho e os textos ficarão piscando em sua tela

CÓDIGO...

// Colisão com os cantos da tela#include <allegro.h>

int main(){   allegro_init();   set_color_depth(16);   install_keyboard();   set_gfx_mode(GFX_AUTODETECT_FULLSCREEN, 640, 480, 0, 0);         // inicio da programação atual   int x = 0;   int y = 0;      // dimensao do quadrado   // variáveis constantes nunca mudam durante a execução do programa   const int tamanho = 100;         // Laço principal   while( !key[KEY_ESC] )   {      if ( key[KEY_UP] )      {         y--;      }            if ( key[KEY_DOWN] )      {         y++;      }      

Page 22: Biblioteca Allegro

      if ( key[KEY_LEFT] )      {         x--;      }            if ( key[KEY_RIGHT] )      {         x++;      }            // esta função limpa a tela ou um objeto que tem buffer no allegro       clear( screen );            // escrevemos um quadrado na tela na posição x e y que podem ter sido modificadas      rectfill( screen, x, y, x+tamanho, y+tamanho, makecol(255, 0, 0) );            // imprimimos as coordenadas x e y na tela para o usuário ver      textprintf_ex( screen, font, 10, 10, makecol(255,0,0), -1, "Variavel X: %d", x);      textprintf_ex( screen, font, 10, 20, makecol(255,0,0), -1, "Variavel Y: %d", y);            // essa função faz o allegro esperar um pouco antes de voltar para o while      rest(10);   }         allegro_exit();   return 0;}END_OF_MAIN();

FIM DE CÓDIGO...

 

Exemplo com Double Buffer

CÓDIGO...

// Colisão com os cantos da tela#include <allegro.h>

int main(){   allegro_init();   set_color_depth(16);   install_keyboard();   set_gfx_mode(GFX_AUTODETECT_FULLSCREEN, 640, 480, 0, 0);         // inicio da programação atual   int x = 0;   int y = 0;      // dimensao do quadrado   // variáveis constantes nunca mudam durante a execução do programa   const int tamanho = 100;         // Criando BUFFER, ela é um ponteiro para um BITMAP, pode ter qualquer nome    BITMAP *buffer = NULL;   buffer = create_bitmap(800,800);      

Page 23: Biblioteca Allegro

   // Laço principal   while( !key[KEY_ESC] )   {      if ( key[KEY_UP] )      {         y--;      }            if ( key[KEY_DOWN] )      {         y++;      }            if ( key[KEY_LEFT] )      {         x--;      }            if ( key[KEY_RIGHT] )      {         x++;      }            // limpa o nosso novo buffer      clear( buffer );            // escreve o quadrado no buffer      rectfill( buffer, x, y, x+tamanho, y+tamanho, makecol(255, 0, 0) );            // imprimimos as coordenadas x e y na tela para o usuário ver no buffer      textprintf_ex( buffer, font, 10, 10, makecol(255,0,0), -1, "Variavel X: %d", x);      textprintf_ex( buffer, font, 10, 20, makecol(255,0,0), -1, "Variavel Y: %d", y);                  // imprime o buffer na tela      blit(buffer, screen, 0,0,0,0, SCREEN_W, SCREEN_H);            // essa função faz o allegro esperar um pouco antes de voltar para o while      rest(10);   }         allegro_exit();   return 0;}END_OF_MAIN();

26 - Utilizando TILES

Tiles é uma representação gráfica de algo na tela, é um conjunto de tiles( pedaços de imagens ) que formará uma imagem maior.

VOCÊ PRECISA SABER...

É importante que você tenha lido nosso artigo sobre imagens antes de continuar. Caso contrário você pode se ter dificuldades no entendimento de algumas funções de manipulação de imagens que iremos utilizar ao longo do código.

- Desenhando imagens BMP na tela  

Page 24: Biblioteca Allegro

 

Com tiles podemos fazer um cenário, o que geralmente representará uma redução de uso de memória e armazenamento em disco do que utilizar um único bitmap para a tela inteira.

Geralmente, o correto é trabalhar com tiles para as partes fixas do cenário ou com pouca mobilidade, por exemplo: paredes e chão de um cenário, objetos como baús, e outras coisas que não se "mexem" muito e inclusive a água, havendo então uma troca seqüencial das tiles que fazem parte da água de forma a dar a impressão de movimentação desta.

Nesse artigo iremos mostrar como montar um cenário usando apenas tiles estáticos, ou seja, sem movimentação.

ARTE FINAL

 

TILES USADOSApenas amostra Pronto para ser utilizado

Baixar tiles

Converta as imagens de .jpg para .bmp atravez de algum programa de editoração

Com base na ilustração acima devemos esclarecer os seguintes itens:

O tamanho do cenário é 640x480. Totalizando 307.200 pixels. Todas as imagens foram salvas com 24bits de profundidade de cores. No entanto configuramos o

compilador para enxergar até 32bits. O tamanho de cada Tile é 32x32 pixels.

Com base nas informações acima você deve entender que a largura da tela é de 640 pixels e a largura do nosso tile é 32 pixels. Logo, já que nosso tile tem 32 pixels de largura podemos colocar apenas 20 tiles na horizontal. Já na altura, como nós temos 480 pixels podemos colocar apenas 15 tiles.

O tamanho dos tiles você define conforme a necessidade do se projeto. A idéia do artigo é dar uma visão geral de como montar um cenário simples e estático através de tiles.

Page 25: Biblioteca Allegro

DICA...

Para criar os tiles usamos o editor de imagens GLIMP. 

É uma ferramenta muito boa e tem uma opção que torna a vida do design iniciante muito mais prática. Essa opção se chama “Tornar encaixavel”. 

Se você estiver fazendo a textura de uma grama ou um chão,por exemplo, quando você terminar poderá tornar essa textura encaixavel em todos os sentidos. Dessa forma você não terá problema na hora de visualizar os tiles um do lado do outro.

Assim o processo fica mais prático mas não muito profissional porque a imagem sofre uma pequena distorção. Para quem está começando vale a pena verificar.

Para facilitar o aprendizado você poderá baixar os tiles utilizados no exemplo acima.

CÓDIGO...

#include <allegro.h>

int main(){   allegro_init();   install_keyboard();   set_color_depth(32);   set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0);

   // COMENTADO NO FINAL - 1    int i=0;   int x=0;   int y=0;

   const int xTile = 32; // Largura do Tile   const int yTile = 32; // Altura do Tile

     // COMENTADO NO FINAL - 2 

   // Posições dos tiles no mapa bidimencional.   int Mapa[15][20] = {   3,3,3,3,4,5,5,5,5,6,2,2,1,10,10,8,9,9,9,9,   3,3,3,3,4,5,5,5,5,6,2,2,1,7,7,7,9,9,9,9,   3,3,3,3,4,5,5,5,5,6,2,2,1,7,7,7,9,9,9,9,   3,3,3,3,4,5,5,5,5,6,2,2,1,7,7,8,9,9,9,9,   3,3,3,3,4,5,5,5,5,6,2,2,1,7,7,8,8,8,8,8,   3,3,3,3,4,5,5,5,5,6,2,2,1,7,7,7,7,7,7,7,   3,3,3,3,4,5,5,5,5,6,2,2,1,7,7,7,7,7,7,7,   3,3,3,3,4,5,5,5,5,6,2,2,1,7,0,10,7,7,10,0,   3,3,3,3,4,5,5,5,5,6,2,2,1,7,10,0,7,7,10,0,   3,3,3,3,3,4,5,5,5,6,2,2,1,7,0,10,7,7,10,0,   3,3,3,3,3,3,4,5,5,6,2,2,1,7,10,0,7,7,10,0,   3,3,3,3,3,3,4,5,5,6,2,2,1,7,0,10,7,7,10,0,   3,3,3,3,3,3,4,5,5,6,2,2,1,7,10,0,7,7,10,0,   3,3,3,3,3,3,4,5,5,6,2,2,1,7,0,10,7,7,10,0,   3,3,3,3,3,3,4,5,5,6,2,2,1,7,10,0,7,7,10,0};

     // COMENTADO NO FINAL - 3       // Carregando o ponteiro com a imagem que possui todos os tiles   BITMAP *bmpTiles = NULL;   bmpTiles = create_bitmap(224,64);   bmpTiles = load_bitmap("tiles.bmp",NULL);      // Array que guarda as texturas separadas   BITMAP *bmpTexturas[11];

Page 26: Biblioteca Allegro

      // Definindo o tamanho 32x32 para cada tile   for (i=0;i<11;i++)   {      bmpTexturas[i] = create_bitmap(xTile,yTile);   }

   // A rotina abaixo separa cada tile e armazena no array.   blit(bmpTiles, bmpTexturas[0],  64, 0, 0, 0, xTile, yTile );   // Grama normal   blit(bmpTiles, bmpTexturas[1],  32, 0, 0, 0, xTile, yTile );   // Grama com areia   blit(bmpTiles, bmpTexturas[2],  0, 0, 0, 0, xTile, yTile );     // Areia   blit(bmpTiles, bmpTexturas[3],  96, 0, 0, 0, xTile, yTile );   // Agua funda      blit(bmpTiles, bmpTexturas[4],  128, 0, 0, 0, xTile, yTile ); // Agua funda com agua clara   blit(bmpTiles, bmpTexturas[5],  160, 0, 0, 0, xTile, yTile ); // Agua clara   blit(bmpTiles, bmpTexturas[6],  192, 0, 0, 0, xTile, yTile ); // Agua clara com areia   blit(bmpTiles, bmpTexturas[7],  0, 32, 0, 0, xTile, yTile );  // Pedras   blit(bmpTiles, bmpTexturas[8],  32, 32, 0, 0, xTile, yTile ); // Parede   blit(bmpTiles, bmpTexturas[9],  64, 32, 0, 0, xTile, yTile ); // Piso   blit(bmpTiles, bmpTexturas[10], 96, 32, 0, 0, xTile, yTile );  // Flores

     // COMENTADO NO FINAL - 4 

   //Monta o Cenário   for (x=0;x<15;x++)   {      for (y=0;y<20;y++)      {         draw_sprite(screen,bmpTexturas[ Mapa[x][y] ], y*32, x*32 );      }   }

     // COMENTADO NO FINAL - 5 

   //Escreve mercado na parede.   textout_ex(screen, font, "MERCADO", 540, 135, makecol(255,255,0), -1);

   readkey();

   //Desaloca as Texturas   for (i=0;i<11;i++)   {      destroy_bitmap(bmpTexturas[i]);   }

   destroy_bitmap(bmpTiles);      allegro_exit();

   return 0;}

END_OF_MAIN();

FIM DO CÓDIGO

Passos para a preparação dos tiles com base nesse exemplo:

Definição do tamanho da tela de 640x480 pixels. Profundidade de cores de 32 bits. Criação dos tiles no tamanho de 32x32 pixels – 24bits. Dessa forma temos espaço para 20 tiles na

horizontal e 15 tiles na vertical. Organizar todos os tiles em apenas um arquivo de imagem com fundo lilás conforme ilustrado acima.

Dessa forma podemos extrair todas as texturas através da função blit.

Após a preparação do tiles é necessário organizar o código de maneira que fique clara a montagem do cenário usando os tiles criados.

Page 27: Biblioteca Allegro

// COMENTADO NO FINAL - 1

Bom, vamos pular as configurações da tela e ver as 3 variáveis criadas. 

A variávei “i” é apenas um contador auxiliar.

A variável ‘x” e “y” vão ser utilizadas para percorrer o array bidimensional do mapa. 

Logo abaixo das variáveis temos mais duas variáveis constantes que armazenam a largura e a altura do tile. Nesse exemplo possuímos tiles de 32x32 pixels. Declarando o tamanho como sendo global, posteriormente podemos efetuar mudanças sem ter que percorrer o código atrás de referência ao tamanho do tile.

// COMENTADO NO FINAL - 2

A matriz que representará o layout do mapa deve ser bidimensional e deve ter como tamanho a quantidade de tiles que podemos colocar na tela, nesse caso [15]x[20].

Você vai notar que ao informar o tamanho do mapa eu inverti o x pelo y. 

Dessa forma, fica fácil de visualizar o layout do cenário através dos números. Normalmente o código de montagem do cenário monta o cenário da esquerda para direita e de cima para baixo. Se você fizer da mesma forma que eu fiz o cenário será montado de cima para baixo e da esquerda para a direita.

Você terá que iniciar cada posição do mapa com o número do tile que queremos colocar naquela posição. Para isso você já deve ter em mente a organização dos tiles e a numeração que vai identificar cada uma na posição do mapa.

// COMENTADO NO FINAL - 3

Ao invés de carregarmos cada textura do mapa separadamente vamos carregar uma imagem organizada e contendo todas as texturas.

Logo abaixo teremos um array de imagens que vai conter cada textura separada. Como cada textura tem tamanho 32x32 fizemos uma rotina genérica para definir o tamanho de todos seqüencialmente. Em seguida carregamos cada posição do array de texturas com cada imagem correspondente. Para esse efeito usamos a função blit.

// COMENTADO NO FINAL - 4 

Até agora temos o mapa com cada posição iniciada com um número. Esse número é correspondente ao array de texturas. Por exemplo, na posição Mapa[0][0] temos o número 3. Se você pegar a textura[3] você verá a imagem de água escura conforme nossa organização, e assim sucessivamente com as outras posições.

A rotina para montar o cenário é simples. Devemos percorrer todas as posições do array bidimensional “Mapa” identificando o número e representando através de uma textura na tela.

Como nosso tile tem 32 pixels então cada vez que você desenhar uma imagem você deve pular 32 pixels para poder desenhar a próxima (x*xTile, y*yTile).

Caso contrário uma imagem ficará em cima da outra. Imagine a posição x=0 e y=0. Se desenharmos um tile nessa posição o tile vai ocupar 32 pixels na largura e 32 pixels na altura, portanto o próximo tile deve ser desenhado a partir da posição x=32 e y=32. Por esse motivo a rotina de montagem do mapa está multiplicando a posição x,y pelo tamanho de cada tile.

// COMENTADO NO FINAL - 5 

Para finalizar, escrevemos na tela o texto “MERCADO” para caracterizar o local no canto superior direito da tela.Em seguida é fizemos a destruição das imagens e a finalização da biblioteca Allegro.

Caro leitor, chegamos ao fim do entendimento da montagem de tiles. Espero que o exemplo acima tanha ajudado. Qualquer dúvida entre em contato com o pessoal da BDJogos através do fórum.

27 - Detectando colisões

Page 28: Biblioteca Allegro

O Ato de detectar colisões entre desenhos é muito importante para qualquer tipo de jogo.Em um jogo como o Super Mario por exemplo, não podemos sair da tela, nem podemos tocar do lado dos inimigos.

Para isso foi programado uma detecção de colisão, que na verdade é uma formula bem simples.

Todo objeto na tela deve ter uma posição X e Y definida, desta forma,podemos saber se o X e Y de um objeto está dentro da área do X e Y de outro objeto.

Primeiro, vamos fazer a colisão de um objeto com a tela.Sabemos que no Allegro, a posição 0,0 é o limite esquerdo e do topo.

Usaremos uma variável da Allegro para saber qual é o tamanho máximo da tela para pegar-mos o X e Y do limite direito e final da tela.

 FÓRMULA

- A posição X do objeto não pode ser menor que zero - A posição de X + o tamanho do objeto ( em X ) não pode ser maior que o limite da tela. Se for maior, o objeto vai sair da tela. 

- A posição Y do objeto não pode ser menor que zero - A posição de Y + o tamanho do objeto ( em Y ) não pode ser maior que o limite da tela. Se for maior, o objeto vai sair da tela.

Tome cuidado quando o objeto tiver um tamanho X diferente do tamanho Y

 

CÓDIGO...

// Colisão com os cantos da tela#include <allegro.h>

int main(){   allegro_init();   set_color_depth(16);   install_keyboard();   set_gfx_mode(GFX_AUTODETECT_FULLSCREEN, 640, 480, 0, 0);         // inicio da programação atual   int x = 0;   int y = 0;      // dimensao do quadrado   // variáveis constantes nunca mudam durante a execução do programa   const int tamanho = 100;         // Laço principal   while( !key[KEY_ESC] )   {      if ( key[KEY_UP] )      {         y--;      }            if ( key[KEY_DOWN] )      {         y++;      }            if ( key[KEY_LEFT] )

Page 29: Biblioteca Allegro

      {         x--;      }            if ( key[KEY_RIGHT] )      {         x++;      }            // limitando o quadrado dentro da tela      if ( x < 0 )      {         x = 0;      }            if ( (x+tamanho) > SCREEN_W )      {         x = SCREEN_W - tamanho;      }            if ( y < 0 )      {         y = 0;      }            if ( (y+tamanho) > SCREEN_H )      {         y = SCREEN_H - tamanho;      }                  // esta função limpa a tela      clear( screen );            // escrevemos um quadrado na tela na posição x e y      rectfill( screen, x, y, x+tamanho, y+tamanho, makecol(255, 0, 0) );            // imprimimos as coordenadas x e y na tela para o usuário ver      textprintf_ex( screen, font, 10, 10, makecol(255,0,0), -1, "Variavel X: %d", x);      textprintf_ex( screen, font, 10, 20, makecol(255,0,0), -1, "Variavel Y: %d", y);            // essa função faz o allegro esperar um pouco antes de voltar para o while      rest(10);   }         allegro_exit();   return 0;}END_OF_MAIN();

FIM DE CÓDIGO...

Basicamente:

Imagine que as linhas pretas são os limites da tela.Temos que, o x de nosso quadrado nunca pode ser menor que o x limite da tela.Para resolver isso fizemos:

      if ( x < 0 )      {

Page 30: Biblioteca Allegro

         x = 0;      }

O mesmo para o y.

Agora, para testar os limites finais da tela:

Imagine que em x=100 o quadrado já está tocando no final da tela.Para testar isso, fizemos o seguinte:

      if ( (x+tamanho) > SCREEN_W )      {         x = SCREEN_W - tamanho;      }

Se imaginarmos que o SCREEN_W seja de 200, podemos dizer quex ( 100 ) + tamanho ( 100 ) nunca pode ser maior que o SCREEN_W.

O mesmo acontece para y.

 Dica

Fique esperto com estes testes quando o tamanho do objeto for difrente para X e Y.

 

 

Agora vamos inserir um quadrado no meio da tela, e vamos fazer um teste de colisão neste quadrado.

 

CÓDIGO...

// Colisão com os cantos da tela#include <allegro.h>

int main(){   allegro_init();   set_color_depth(16);   install_keyboard();   set_gfx_mode(GFX_AUTODETECT_FULLSCREEN, 640, 480, 0, 0);      // inicio da programação atual   int x = 0;   int y = 0;   const int tamanho = 50;      const int obj_tam = 100;   const int obj_x = ( SCREEN_W - (obj_tam / 2) ) / 2;   const int obj_y = ( SCREEN_H - (obj_tam / 2) ) / 2;      bool colide = false;      // Criando BUFFER para double buffer

Page 31: Biblioteca Allegro

   BITMAP *buffer = NULL;   buffer = create_bitmap(800,800);         // Laço principal   while( !key[KEY_ESC] )   {         if ( colide == false )      {         if ( key[KEY_UP] )         {            y--;         }                  if ( key[KEY_DOWN] )         {            y++;         }                  if ( key[KEY_LEFT] )         {            x--;         }                  if ( key[KEY_RIGHT] )         {            x++;         }      }                        // limpa o nosso novo buffer      clear( buffer );            // escreve o quadrado no buffer      rectfill( buffer, x, y, x+tamanho, y+tamanho, makecol(255, 0, 0) );            rectfill( buffer, obj_x, obj_y, obj_x+obj_tam, obj_y+obj_tam, makecol(0, 0, 255) );            // imprimimos as coordenadas x e y na tela para o usuário ver no buffer      textprintf_ex( buffer, font, 10, 10, makecol(255,0,0), -1, "Variavel X: %d", x);      textprintf_ex( buffer, font, 10, 20, makecol(255,0,0), -1, "Variavel Y: %d", y);            textprintf_ex( buffer, font, 10, 30, makecol(255,0,0), -1, "obj X: %d", obj_x);      textprintf_ex( buffer, font, 10, 40, makecol(255,0,0), -1, "obj Y: %d", obj_y);                  // verifica se colide      if ( (x+tamanho) >= obj_x && x <= obj_x+obj_tam )      {         if ( (y+tamanho) >= obj_y && y <= obj_y+obj_tam )         {            textout_ex(buffer, font, "COLIDE", obj_x+25, obj_y+50, makecol(0, 255, 0), -1);         }      }                  // imprime o buffer na tela      vsync();      blit(buffer, screen, 0,0,0,0, SCREEN_W, SCREEN_H);            // essa função faz o allegro esperar um pouco antes de voltar para o while      rest(10);   }         allegro_exit();   return 0;}END_OF_MAIN();

Page 32: Biblioteca Allegro

FIM DE CÓDIGO...

Primeiro exemplo:

Neste caso, a imagem vermelha está fora dos limites da imagem do centro azul.

Neste caso, temos o vermelho dentro do limite X da imagem azul, porém, o vermelho continua fora do limite Y, logo, ainda não temos uma colisão.

Já temos este teste: 

 if ( (x+tamanho) >= obj_x && x <= obj_x+obj_tam )

 

Neste caso, temos o vermelho dentro do limite Y da imagem azul, porém, o vermelho continua fora do limite X, logo, ainda não temos uma colisão.

Page 33: Biblioteca Allegro

Neste caso, já temos este teste: 

 if ( (y+tamanho) >= obj_y && y <= obj_y+obj_tam )

 

E no último exemplo, temos a colisão em X e Y dentro dos limites nas imagem azul.Juntando os 2 testes, temos:

     // verifica se colide      if ( (x+tamanho) >= obj_x && x <= obj_x+obj_tam )      {         if ( (y+tamanho) >= obj_y && y <= obj_y+obj_tam )         {            textout_ex(buffer, font, "COLIDE", obj_x+25, obj_y+50, makecol(0, 255, 0), -1);         }      }

27 - Detectando colisões com uma função

Usando o capítulo 27, vamos praticar detectar colisão, usando uma função e não deixando que o objeto que está se movendo, consiga se mover enquanto estiver colidindo.

O programa não muda muito, nem a formula, a única diferença é que precisamos juntar os ifs em um único if.

 

CÓDIGO...

// Colisão em uma função #include <allegro.h>

// função que verifica colisãobool colide( int, int, int, int, int, int );

int main(){   allegro_init();   set_color_depth(16);   install_keyboard();   set_gfx_mode(GFX_AUTODETECT_FULLSCREEN, 640, 480, 0, 0);      // inicio da programação atual   int x = 0;   int y = 0;

Page 34: Biblioteca Allegro

      int x_atual = x;   int y_atual = y;      const int tamanho = 50;      const int obj_tam = 100;   const int obj_x = ( SCREEN_W - (obj_tam / 2) ) / 2;   const int obj_y = ( SCREEN_H - (obj_tam / 2) ) / 2;      bool colidindo = false;      // Criando BUFFER para double buffer   BITMAP *buffer = NULL;   buffer = create_bitmap(800,800);         // Laço principal   while( !key[KEY_ESC] )   {      x_atual = x;      y_atual = y;                  if ( key[KEY_UP] )      {         y--;      }            if ( key[KEY_DOWN] )      {         y++;      }            if ( key[KEY_LEFT] )      {         x--;      }            if ( key[KEY_RIGHT] )      {         x++;      }            colidindo = colide( x, y, tamanho, obj_x, obj_y, obj_tam );            if ( colidindo == false )      {         x_atual = x;         y_atual = y;      }      else      {         // volta para os valores antigos         x = x_atual;         y = y_atual;      }            // limpa o nosso novo buffer      clear( buffer );            // escreve o quadrado no buffer      rectfill( buffer, x_atual, y_atual, x_atual+tamanho, y_atual+tamanho, makecol(255, 0, 0) );      

Page 35: Biblioteca Allegro

      rectfill( buffer, obj_x, obj_y, obj_x+obj_tam, obj_y+obj_tam, makecol(0, 0, 255) );            // imprimimos as coordenadas x e y na tela para o usuário ver no buffer      textprintf_ex( buffer, font, 10, 10, makecol(255,0,0), -1, "Variavel X: %d", x);      textprintf_ex( buffer, font, 10, 20, makecol(255,0,0), -1, "Variavel Y: %d", y);            textprintf_ex( buffer, font, 10, 30, makecol(255,0,0), -1, "obj X: %d", obj_x);      textprintf_ex( buffer, font, 10, 40, makecol(255,0,0), -1, "obj Y: %d", obj_y);      textprintf_ex( buffer, font, 10, 50, makecol(255,0,0), -1, "colide: %d", colidindo);                  // imprime o buffer na tela      vsync();      blit(buffer, screen, 0,0,0,0, SCREEN_W, SCREEN_H);            // essa função faz o allegro esperar um pouco antes de voltar para o while      rest(10);   }         allegro_exit();   return 0;}END_OF_MAIN();   // função que verifica colisãobool colide( int x, int y, int tamanho, int obj_x, int obj_y, int obj_tam ){   if ( x+tamanho > obj_x && x < obj_x+obj_tam && y+tamanho > obj_y && y < obj_y+obj_tam )   {      return true;   }      return false;}

Exercício pratíco

- Utilizando o programa do capítulo 27, crie uma classe para imprimir vários quadrados "objetos" na tela.O quadrado que se move deve detectar colisão nos outros objetos inseridos.Imprima 5 quadrados na tela de tamanho diferentes e verifique a colisão com todos eles.

- Faça com que uma bola ande de um lado para outro na tela, de forma que ele se colida com o final da tela e volte para o inicio da tela.

- Faça com que uma bola ande por toda tela, colidindo com as paredes.Quando a bola colidir, ela deve alterar para aonde ela vai colidir da próxima vez, de forma que ela fique andando por toda a tela.

 

Os exercícios acima são bastante interessantes e também difíceis. Você pode olhar as respostas caso não conseguir fazer, mais tentar resolver os exercícios vai aumentar seu raciocínio lógico.

Para resolver o primeiro exercício:

http://www.bdjogos.com/adrw/c/classes.htmhttp://www.bdjogos.com/adrw/c/classes_implementacao.htmhttp://www.bdjogos.com/adrw/c/construtores_destruidores.htm

Page 36: Biblioteca Allegro

Respostas dos Exercícios

- Utilizando o programa do capítulo 27, crie uma classe para imprimir vários quadrados "objetos" na tela.O quadrado que se move deve detectar colisão nos outros objetos inseridos.Imprima 5 quadrados na tela de tamanho diferentes e verifique a colisão com todos eles.

 

Preste atenção na forma que a classe foi criada.Quanto mais prática a classe ficar, melhor para seu jogo. Você poderá usar ela muito mais fácil.Evite complicações.

CÓDIGO...

#ifndef PAREDE_H#define PAREDE_H

// ARQUIVO: parede.h// Data: 01/08/2007

class Parede {public:

Parede( int, int, int, int );   void imprimir( BITMAP * );   bool colide( int, int, int, int );

private:

   int x;   int y;   int tamanho_x;   int tamanho_y;};

#endif

FIM DE CÓDIGO...

CÓDIGO...

#include <allegro.h>#include "parede.h"

// ARQUIVO: parede.cpp// Data: 01/08/2007

// Este é o construtor do objetoParede::Parede( int x, int y, int tam_x, int tam_y ){   this->x = x;   this->y = y;   this->tamanho_x = tam_x;   this->tamanho_y = tam_y;}

// Essa função imprime o objeto na tela,// passamos o local aonde queremos imprimirvoid Parede::imprimir( BITMAP *screen ){   rectfill( screen, x, y, x+tamanho_x, y+tamanho_y, makecol(255, 255, 0) );}

// Testa a colisão com o objeto atual ( isso quer dizer 1 de cada vez )bool Parede::colide( int x, int y, int tamanho_x, int tamanho_y )

Page 37: Biblioteca Allegro

{   if (      ( x+tamanho_x > this->x ) &&      ( x < this->x+this->tamanho_x ) &&      ( y+tamanho_y > this->y ) &&      ( y < this->y+this->tamanho_y )   )   {      return true;   }      return false;}

FIM DE CÓDIGO...

CÓDIGO...

// Colisão com vários objetos #include <allegro.h>#include "parede.h"

// ARQUIVO: main.cpp// Data: 01/08/2007

int main(){   allegro_init();   set_color_depth(16);   install_keyboard();   set_gfx_mode(GFX_AUTODETECT_FULLSCREEN, 640, 480, 0, 0);      // inicio da programação atual   int x = 0;   int y = 0;      int x_atual = x;   int y_atual = y;      const int tamanho_x = 20;   const int tamanho_y = 10;      const int total_paredes = 5;      bool colidindo = false;      // forma de iniciar vários objetos com construtor   Parede aparede[total_paredes] = {   Parede( 300, 50, 10, 30 ),   Parede( 150, 150, 50, 30 ),   Parede( 300, 150, 150, 50 ),   Parede( 250, 250, 50, 150 ),   Parede( 500, 250, 80, 80 )   } ;      int i = 0;      // Criando BUFFER para double buffer   BITMAP *buffer = NULL;   buffer = create_bitmap(800,800);         // Laço principal   while( !key[KEY_ESC] )   {      x_atual = x;      y_atual = y;            if ( key[KEY_UP] )

Page 38: Biblioteca Allegro

      {         y -= 5;      }            if ( key[KEY_DOWN] )      {         y += 5;      }            if ( key[KEY_LEFT] )      {         x -= 5;      }            if ( key[KEY_RIGHT] )      {         x += 5;      }                  // Aqui vem o complicado      // Nós olhamos objeto por objeto, se colidir com um deles      // paramos o for e não deixamos nosso quadrado andar      for ( i=0; i<total_paredes; i++ )      {         colidindo = aparede[i].colide( x, y, tamanho_x, tamanho_y );                  if ( colidindo == true )         {            i = total_paredes;         }      }               if ( colidindo == false )      {         x_atual = x;         y_atual = y;      }      else      {         // volta para os valores antigos         x = x_atual;         y = y_atual;      }                  // limpa o nosso novo buffer      clear( buffer );            // escreve o quadrado no buffer      rectfill( buffer, x_atual, y_atual, x_atual+tamanho_x, y_atual+tamanho_y, makecol(255, 0, 0) );                  // imprimimos as coordenadas x e y na tela para o usuário ver no buffer      textprintf_ex( buffer, font, 10, 10, makecol(255,0,0), -1, "Variavel X: %d", x);      textprintf_ex( buffer, font, 10, 20, makecol(255,0,0), -1, "Variavel Y: %d", y);            textprintf_ex( buffer, font, 10, 50, makecol(255,0,0), -1, "colide: %d", colidindo);            // imprime todos os quadrados no destino ( buffer )      for ( i=0; i<total_paredes; i++ )      {         aparede[i].imprimir( buffer );      }            // imprime o buffer na tela      vsync();      blit(buffer, screen, 0,0,0,0, SCREEN_W, SCREEN_H);            // essa função faz o allegro esperar um pouco antes de voltar para o while      rest(10);   }   

Page 39: Biblioteca Allegro

      allegro_exit();   return 0;}END_OF_MAIN();   

FIM DE CÓDIGO...

Preste muita atenção a forma que as paredes foram criadas.Também preste atenção em como fazemos a colisão, olhando um objeto de cada vez.

Será que em um jogo grande isso seria válido?? Imagine testar a colisão desta forma em toda uma fase do Super Mario Bros ?Quantos testes seriam necessários?

Por enquanto não se preocupe com otimização de código, poderiam ter 5 mil objetos na tela que o C++ iriadar conta do recado. Utilize sua imaginação sem pensar que seu jogo irá ficar lento.

 

- Faça com que uma bola ande de um lado para outro na tela, de forma que ele se colida com o final da tela e volte para o inicio da tela.

CÓDIGO...

// Bolinha andadora #include <allegro.h>

int main(){   allegro_init();   set_color_depth(16);   install_keyboard();   set_gfx_mode(GFX_AUTODETECT_FULLSCREEN, 640, 480, 0, 0);      // inicio da programação atual   int x = 0;   int y = SCREEN_H/2;   int tamanho = 20;   bool colide = false;      int variavel = 1;      // Criando BUFFER para double buffer   BITMAP *buffer = NULL;   buffer = create_bitmap(800,800);         // Laço principal   while( !key[KEY_ESC] )   {            if ( variavel == 1 )      {         x += 5;      }      else      {         x -= 5;      }                  // limpa o nosso novo buffer      clear( buffer );      

Page 40: Biblioteca Allegro

      // escreve o quadrado no buffer      ellipsefill(buffer, x, y, tamanho, tamanho, makecol(255,255,0) );                        // imprimimos as coordenadas x e y na tela para o usuário ver no buffer      textprintf_ex( buffer, font, 10, 10, makecol(255,0,0), -1, "Variavel X: %d", x);      textprintf_ex( buffer, font, 10, 20, makecol(255,0,0), -1, "Variavel Y: %d", y);                  // limitando o quadrado dentro da tela      if ( x < 0 )      {         variavel = variavel * (-1);      }            if ( (x+tamanho) > SCREEN_W )      {         variavel = variavel * (-1);      }                  // imprime o buffer na tela      vsync();      blit(buffer, screen, 0,0,0,0, SCREEN_W, SCREEN_H);            // essa função faz o allegro esperar um pouco antes de voltar para o while      rest(10);   }         allegro_exit();   return 0;}END_OF_MAIN();

FIM DE CÓDIGO...

- Faça com que uma bola ande por toda tela, colidindo com as paredes.Quando a bola colidir, ela deve alterar para aonde ela vai colidir da próxima vez, de forma que ela fique andando por toda a tela.

CÓDIGO...

// Bolinha colisão #include <allegro.h>

int main(){   allegro_init();   set_color_depth(16);   install_keyboard();   set_gfx_mode(GFX_AUTODETECT_FULLSCREEN, 640, 480, 0, 0);      // inicio da programação atual   int x = 0;   int y = SCREEN_H/2;   int tamanho = 20;   bool colide = false;      int variavel_x = 10;   int variavel_y = 10;      // Criando BUFFER para double buffer   BITMAP *buffer = NULL;   buffer = create_bitmap(800,800);

Page 41: Biblioteca Allegro

         // Laço principal   while( !key[KEY_ESC] )   {         if ( variavel_x == 1 )      {         x += variavel_x;      }      else      {         x -= variavel_x;      }            if ( variavel_y == 1 )      {         y += variavel_y;      }      else      {         y -= variavel_y;      }                  // limpa o nosso novo buffer      clear( buffer );            // escreve o quadrado no buffer      ellipsefill(buffer, x, y, tamanho, tamanho, makecol(255,255,0) );                        // imprimimos as coordenadas x e y na tela para o usuário ver no buffer      textprintf_ex( buffer, font, 10, 10, makecol(255,0,0), -1, "Variavel X: %d", x);      textprintf_ex( buffer, font, 10, 20, makecol(255,0,0), -1, "Variavel Y: %d", y);                  // limitando o quadrado dentro da tela      if ( x < 0 )      {         variavel_x = variavel_x * (-1);      }            if ( (x+tamanho) > SCREEN_W )      {         variavel_x = variavel_x * (-1);      }            if ( y < 0 )      {         variavel_y = variavel_y * (-1);      }            if ( (y+tamanho) > SCREEN_H )      {         variavel_y = variavel_y * (-1);      }                  // imprime o buffer na tela      vsync();      blit(buffer, screen, 0,0,0,0, SCREEN_W, SCREEN_H);            // essa função faz o allegro esperar um pouco antes de voltar para o while      rest(10);   }         allegro_exit();   return 0;}END_OF_MAIN();

Page 42: Biblioteca Allegro

28 - FPS - Quadros por segundo, Frames per second ou Frame Rate

Todo jogo possui animação, isto é fato. Animações são geralmente quadros de desenhos que vão se mudando ( sobrepondo a antiga ) gerando assim uma animação.

Este é o exemplo de uma animação e alguns de seus quadros, que juntos formam a animação.

=

Esta animação originalmente tem 12 quadros e ela é executada a 100 milisegundos.Se aumentarmos a taxa de milisegundos de cada quadro em separado, a animação vai ficar ainda mais lenta. Se diminuirmos a taxa de 100 milisegundos de cada quadro, a animação vai ficar bem mais rápida.

Essa mudança de tempo nos quadros da animação influencia muito os jogos feitos para computadores.Geralmente, nosso programa roda na velocidade de clock tick do nosso processador. Isto quer dizer que o programa pode rodar mais rápido em algumas máquinas e mais lento em outras máquinas.

Isso influência não só as animações como também a jogabilidade.

Para que o jogo rode igual em qualquer máquina, precisamos criar um controle de Frame Rate ( ou controle de FPS ).Para isto, primeiro precisamos descobrir como contar os FPS de nosso programa.

 

DICA...

Esta técnica de contar FPS e fazer o programa rodar em um FPS igual para todas as máquinas pode e deve ser aplicado em qualquer tipo de linguagem de programação para jogos ou animações.

Cada linguagem tem uma forma diferente de se chegar nesse resultado, mais a tecnica é a mesma.Vamos aprender em Allegro um código completo.

Em DirectX podemos fazer a mesma coisa usando a função do windows GetTickCount(); que pega o total de ticks.Em OpenGL podemos fazer a mesma coisa usando as funções do GLUT .

 

VOCÊ PRECISA SABER...

É importante que você tenha lido nosso artigo sobre como instalar timers no Allegro. 

- Controle de tempo  

Page 43: Biblioteca Allegro

CÓDIGO...

#include <allegro.h>

// Exemplo da bolinha com contador de Frames por segundos

// globalint fps = 0;int fps_antigo = 0;

// prototipo do contador de framesvoid frame_rate();

int main(){   allegro_init();   set_color_depth(16);   install_keyboard();   set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0);         BITMAP *buffer = NULL;      int x = 0;   int y = SCREEN_H/2;   int tamanho = 20;      install_timer();      // a cada 1 segundo mostra quantas vezes a imagem foi impressa na tela   install_int( frame_rate, 1000 );            bool colide = false;      int variavel_x = 10;   int variavel_y = 10;      // Criando BUFFER para double buffer   buffer = create_bitmap(800,800);            // Laço principal   while( !key[KEY_ESC] )   {            if ( variavel_x == 1 )      {         x += variavel_x;      }      else      {         x -= variavel_x;      }            if ( variavel_y == 1 )      {         y += variavel_y;      }      else      {         y -= variavel_y;      }            // limitando o quadrado dentro da tela      if ( x < 0 )      {         variavel_x = variavel_x * (-1);      }      

Page 44: Biblioteca Allegro

      if ( (x+tamanho) > SCREEN_W )      {         variavel_x = variavel_x * (-1);      }            if ( y < 0 )      {         variavel_y = variavel_y * (-1);      }            if ( (y+tamanho) > SCREEN_H )      {         variavel_y = variavel_y * (-1);      }            // limpa o nosso novo buffer      clear( buffer );            // escreve a bola no buffer      ellipsefill(buffer, x, y, tamanho, tamanho, makecol(255,255,0) );            textprintf_ex( buffer, font, 10, 10, makecol(255,0,0), -1, "FPS: %d", ::fps_antigo );                              blit(buffer, screen, 0,0,0,0, SCREEN_W, SCREEN_H);            vsync();      ::fps++;         }          destroy_bitmap( buffer );         allegro_exit();   return 0;}END_OF_MAIN();      void frame_rate(){   ::fps_antigo = ::fps;   ::fps = 0;}

FIM DE CÓDIGO

No exemplo acima, criamos uma variável global fps que é incrementada sempre após que a imagem é imprimida na tela.Usando um timer do Allegro, sempre que passar 1 segundo, nós mostramos o total de FPS que o Allegro conseguiu imprimir, tendo assim o total de frames impressos em 1 segundo.

Legal.. já sabemos a quantos quadros nosso programa roda, agora precisamos controlar a taxa de quadros, sem influênciar na jogabilidade.Nos exemplos anteriores do site, estavamos usando o comando rest() que fazia o compilador esperar.Esta função não deve ser usanda, por que durante a execução do comando rest() as teclas do teclado não são detectadas.

Agora precisamos controlar a taxa de frames de nosso programa.A descisão da quantidade de frames por segundo o jogo vai rodar é sua, você pode usar 30 FPS que é o limite mínimo para que o olho detecte transação da animação, ou 60 que é o valor mostrado no manual da Allegro.

CÓDIGO...

Page 45: Biblioteca Allegro

#include <allegro.h>

// Exemplo da bolinha com limitador de FPS

// globalint fps = 0;

int fps_antigo = 0;int fps_speed = 0;

// prototipo do contador de framesvoid frame_rate();

// prototipo do contador de velocidade void incrementa_speed();

int main(){   allegro_init();   set_color_depth(16);   install_keyboard();   set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0);         BITMAP *buffer = NULL;      int x = 0;   int y = SCREEN_H/2;   int tamanho = 20;      install_timer();      // a cada 1 segundo mostra quantas vezes a imagem foi impressa na tela   install_int( frame_rate, 1000 );      install_int_ex( incrementa_speed, BPS_TO_TIMER(60) );         bool colide = false;      int variavel_x = 10;   int variavel_y = 10;      // Criando BUFFER para double buffer   buffer = create_bitmap(800,800);            // Laço principal   while( !key[KEY_ESC] )   {      while ( ::fps_speed > 0 )       {          if ( variavel_x == 1 )         {            x += variavel_x;         }         else         {            x -= variavel_x;         }                  if ( variavel_y == 1 )         {            y += variavel_y;         }         else         {            y -= variavel_y;         }                  // limitando o quadrado dentro da tela         if ( x < 0 )         {

Page 46: Biblioteca Allegro

            variavel_x = variavel_x * (-1);         }                  if ( (x+tamanho) > SCREEN_W )         {            variavel_x = variavel_x * (-1);         }                  if ( y < 0 )         {            variavel_y = variavel_y * (-1);         }                  if ( (y+tamanho) > SCREEN_H )         {            variavel_y = variavel_y * (-1);         }                  // limpa o nosso novo buffer         clear( buffer );                  // escreve o quadrado no buffer         ellipsefill(buffer, x, y, tamanho, tamanho, makecol(255,255,0) );                  textprintf_ex( buffer, font, 10, 10, makecol(255,0,0), -1, "FPS: %d", ::fps_antigo );

         ::fps_speed--;         ::fps++;      }             blit(buffer, screen, 0,0,0,0, SCREEN_W, SCREEN_H);                  vsync();         }       destroy_bitmap( buffer );      allegro_exit();   return 0;}END_OF_MAIN();   void frame_rate(){   ::fps_antigo = ::fps;   ::fps = 0;}   void incrementa_speed(){   ::fps_speed++; }

FIM DE CÓDIGO

O programa acima pode parecer confuso.Nós criamos um timer para que, a cada 60 clock tick ( usando a macro BPS_TO_TIMER ) o contador de speed seja incrementado.

Quando acontecer os primeiros 60 clocks, o compilador vai entrar no while que atualiza a lógica do jogo.Enquanto o fps_speed não for menor que 0 ( o while vai fazer ele chegar bem rápido ) ele vai atualizando a lógica do jogo, mais não a tela.

Esta lógica faz com que o contador de FPS fique constante. Execute o código acima e faça testes alterando o valor que está na macro BPS_TO_TIMER para conferir.

Page 47: Biblioteca Allegro

Para fazer com que o teclado obedeça a velocidade em FPS é preciso utilizar um Buffer de teclado, que pode ser lido aqui:http://www.bdjogos.com/biblioteca_conteudo.php?id=17

Com este tutorial suprimos uma das dúvidas mais cabulosas na criação de jogos, a partir deste capítulo iremos demonstrar outras técnicas importantes na programação de jogos. Qualquer dúvida entre em contato pelo forum.

29 - Movimentação de Sprites

Sprites é um conjunto de imagens que organizadas em uma determinada seqüência simulam o movimento de algum objeto ou personagem. Praticamente todos os jogos 2D possuem algum tipo de sprites.

Podemos definir como sprites a movimentação do jogador principal, as folhas caindo no chão, os inimigos em movimento e principalmente os efeitos entre os objetos do cenário.

A seguir iremos selecionar alguns sprites prontos de jogos e colocá-los em movimento.Se você proferir escolher algum outro sprite segue uma seqüência de sites abaixo com uma infinidade de sprites de jogos.

Links para Sprites

http://tsgk.captainn.net/http://www.panelmonkey.org/category.php?id=1&theme=1http://www.molotov.nu/?page=graphics

dragao.bmp

Especificações

Qtde de Desenhos em x[0]:Qtde de Desenhos em y[0]:Largura total:Largura individual:Altura:

6 0 360 pixels 60 pixels 55 pixels

robocop.bmp

Especificações

Qtde de Desenhos em x[0]:Qtde de Desenhos em y[0]:Largura total:Largura individual:Altura:

6 0 415 pixels 69 pixels 80 pixels

 

Não se esqueça de converter as imagens acima para bitmap. 

O código que iremos usar para movimentar o Robocop e o dragão é o mesmo. Porém, antes de executar cada um é necessário fazer algumas configurações com relação às especificações citadas acima. 

Page 48: Biblioteca Allegro

CÓDIGO...

#include <allegro.h>

int main(){   allegro_init();   install_keyboard();   set_color_depth(16);   set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0);

   BITMAP *imgSprites = NULL;   imgSprites = load_bitmap("robocop.bmp",NULL);

   BITMAP *Buffer = NULL;   Buffer = create_bitmap(SCREEN_W,SCREEN_H);

   const int iSprite_Largura = 69;   // Largura de um sprite   const int iSprite_Altura = 80;     // Altura de um sprite   const int Qtde_Spritesx = 6;      // Qtde de Desenhos em x[0] - Horizontal   const int Qtde_Spritesy = 0;      // Qtde de Desenhos em y[0] - Vertical

   const int iCiclo = 30; // Ciclo de troca de sprite

   int iPosx = 0; // Posição x do corte.   int iPosy = 0; // Posição y do corte. Como é apenas uma fileira então y sempre vai ser 0

   int buffer_animacao = 0; // Buffer para controle do ciclo

   while (!key[KEY_ESC])   {      clear_bitmap( Buffer );      textprintf_ex(Buffer, font, 0, 0, makecol(0, 255, 255),-1,"ANIMACAO SPRITES");

      // Controle para mudar o sprite       if(buffer_animacao == 0)      {            if (iPosx > 0 && iPosx == Qtde_Spritesx-1)            {               iPosx = 0;            }

            if (Qtde_Spritesx > 0)             {               iPosx++;            }

            buffer_animacao = iCiclo;

      }       else       {         buffer_animacao--;      }

      // Corta e desenha o sprite      masked_blit(imgSprites,Buffer,iPosx*iSprite_Largura,iPosy,SCREEN_W/2, SCREEN_H/2,iSprite_Largura,iSprite_Altura);

      blit(Buffer,screen,0,0,0,0,SCREEN_W,SCREEN_H);      vsync();

   }

   destroy_bitmap(imgSprites);    destroy_bitmap(Buffer);   

   allegro_exit();

   return 0;

Page 49: Biblioteca Allegro

}END_OF_MAIN();

FIM DO CÓDIGO

Logo acima temos um código genérico para a movimentação dos sprites na horizontal. No processo para a movimentação dos sprites na vertical a única coisa que muda é o eixo x para y.

Basicamente, o código possui a informação da largura, altura e quantidade de sprites na horizontal. Com essas informações é possível fazer um corte nas imagens com a função blit vista em outro artigo.

Para que as imagens não fossem trocadas na velocidade de processamento de cada computador adicionamos um buffer de animação iCiclo. Dessa forma nossa animação terá um movimento mais pausado e uma impressão de movimento muito melhor.

Caro leitor chegamos ao fim do entendimento da movimentação de sprites. Se houver alguma dúvida ou dificuldade favor entrar em contato com o pessoal pelo Forum.

Trocar de direção usando Sprite

Nesse artigo vou ilustrar o código para movimentação do pacman em todas as direções utilizando sprites.

Antes de iniciar o código será necessário entender como estão organizados os sprites no arquivo. A tabela abaixo mostra que na vertical estão definidas as direções dos desenhos, Direita, Esquerda, Cima e Baixo. 

Já na horizontal estão definidas as seqüências dos sprites que irão definir o movimento do pacman.

Posições dos SpritesAnimação (x), Direção (y).

 Sprite1

Sprite2

Sprite3

Sprite4

Sprite5

Direita 0,0 25,0 50,0 75,0 100,0

Esquerda 0,25 25,25 50,25 75,25 100,25

Cima 0,50 25,50 50,50 75,50 100,50

Baixo 0,75 25,75 50,75 75,75 100,75

Ao selecionar a posição 0,0 você deverá desenhar na tela o pacman virado para a direita e de boca fechada. 

Já na posição 100,0 o pacman está virado para a direita e de boca aberta. Para fazer a simulação de movimento e de troca de direção é necessário juntar a tabela acima com os direcionais do teclado.

Abaixo segue as especificações dos sprites.

pacpac.bmp

Especificações

Page 50: Biblioteca Allegro

Qtde de Desenhos em x[0]:Qtde de Desenhos em y[0]:Largura total:Largura individual:Altura:

5 4 125 pixels 25 pixels 25 pixels

OBS: Salvar a imagem acima como .BMP. O allegro não aceita o tipo jpg.

O código que iremos usar para movimentar o Robocop e o dragão é o mesmo.

Porém, antes de executar cada um é necessário fazer algumas configurações com relação às especificações citadas acima. 

O código será dividido em 3 arquivos. São eles:

main.cpp Jogador.h Jogador.cpp

CÓDIGO...

#include <allegro.h>#include "cjogador.h"

// Arquivo: main.cpp 

int main(){   allegro_init();   set_color_depth(32);   install_keyboard();   set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0);

   CJogador Jogador; // Instacia o Pacman

   BITMAP *Buffer = NULL;   Buffer = create_bitmap(SCREEN_W,SCREEN_H);

   while (!key[KEY_ESC])   {      clear_bitmap(Buffer);      textprintf_ex(Buffer, font, 0, 0, makecol(0, 255, 255),-1,"SPRITE EM MOVIMENTO");

      //Atualiza o Pacman na tela      Jogador.Atualiza(Buffer);

      //Controle para Movimentação do Pacman na tela      Jogador.Controle(25);

      blit(Buffer,screen,0,0,0,0,SCREEN_W,SCREEN_H);      vsync();   }

   destroy_bitmap(Buffer);   delete Buffer;   allegro_exit();   return 0;}END_OF_MAIN();

FIM DO CÓDIGO

Page 51: Biblioteca Allegro

main.cpp

Dentro desse arquivo é possível encontrar:

Referência aos arquivos de recursos. Inicialização da biblioteca allegro. Referência a classe Jogador (Pacman) Loop principal do jogo. Liberação dos ponteiros. Encerramento do programa.

CÓDIGO...

#ifndef CJOGADOR_H #define CJOGADOR_H

#include <allegro.h>

// Arquivo: cjogador.h 

class CJogador{

private:   BITMAP *imgJogador;   int iPosx;   int iPosy;   int iSpritex;   int iSpritey;

public:   int iLargura;   int iAltura;

   CJogador();   ~CJogador();   void Setax(int);   void Setay(int);   void Atualiza(BITMAP *buffer);   void Controle(int);};

#endif

FIM DO CÓDIGO

Jocador.h

Arquivo de recurso responsável por armazenar apenas a definição da classe Jogador. Todas as características que o pacman vai possuir estão definidas nesse arquivo. São elas:

Variável

Descrição

BITMAP *imgJogador Guarda imagem do Pacmam

int iPosx Posição x do pacman na tela

int iPosy Posição y do pacman na tela

int iSpritex Posição da animação do sprite

Page 52: Biblioteca Allegro

int iSpritey Posição da direção do sprite

int iLargura Armazena a largura de um sprite

int iAltura Armazena a altura de um sprite

Variável

Descrição

CJogador(); Construtor para iniciar as variáveis da classe.

~CJogador(); Destrutor responsável por liberar a imagem do pacman da memória.

void Setax(int); Seta a posição x do pacman a partir de uma velocidade passada por argumento.

void Setay(int); Seta a posição y do pacman a partir de uma velocidade passada por argumento.

void Atualiza(BITMAP *buffer); Renderiza o pacman na tela.

void Controle(int); Controla a direção do pacman através das setas do teclado.

CÓDIGO...

#include "cjogador.h"

// Arquivo: cjogador.cpp

static int iVelSprite = 4;

CJogador::CJogador(){   this->imgJogador = load_bitmap("pacpac.bmp",NULL); // Sprites do PacPac   this->iPosx = 0; // Posição x do pacman na tela   this->iPosy = 0; // Posição y do pacman na tela   this->iLargura = 25; // Define a largura do sprite   this->iAltura = 25; // Define a altura do sprite   this->iSpritex = 0; // Inicia animação na posição zero.   this->iSpritey = 0; // Inicia virado para a direita}

CJogador::~CJogador(){   delete this->imgJogador;}

void CJogador::Setax(int vx){   this->iPosx += vx;

   if (vx<0) this->iSpritey = 25; // Vira Sprite para esquerda   if (vx>0) this->iSpritey = 0; // Vira o Sprite para a direita}

void CJogador::Setay(int vy){   this->iPosy += vy;

   if (vy<0) this->iSpritey = 50; // Vira o Sprite para Cima   if (vy>0) this->iSpritey = 75; // Vira o Sprite para Baixo}

void CJogador::Atualiza(BITMAP *buffer){   if (iSpritex >= 100) iSpritex = 0;

Page 53: Biblioteca Allegro

   masked_blit(imgJogador,buffer,this->iSpritex,this->iSpritey,this->iPosx,this->iPosy,this->iLargura,this->iAltura);   iVelSprite--;

   if (iVelSprite<=0)   {      this->iSpritex += 25;      iVelSprite = 4;   }}

void CJogador::Controle(int Vel){   static int Buffer_Teclado =0;   // A cada movimento reinicia o buffer do teclado.

   if (Buffer_Teclado == 0)   {      if (key[KEY_UP])      {         this->Setay(-Vel);         Buffer_Teclado = 10;      }      else if (key[KEY_DOWN])      {         this->Setay(Vel);         Buffer_Teclado = 10;      }      else if (key[KEY_LEFT])      {         this->Setax(-Vel);         Buffer_Teclado = 10;      }      else if (key[KEY_RIGHT])      {         this->Setax(Vel);         Buffer_Teclado = 10;      }

   }    else    {      Buffer_Teclado--;   }}

FIM DO CÓDIGO

Jogador.cpp

Esse arquivo faz referencia ao arquivo de recursos Jogador.h. No entanto, ele guarda apenas o código responsável pela movimentação do pacman na tela.

Caro leitor chegamos ao fim do entendimento da troca de sprites durante a animação. Espero ter escrito um código auto explicativo.

Se houver alguma dúvida ou dificuldade favor entrar em contato com o pessoal pelo Forum.

30 - Rolagem de Cenário ( Scroll ou Scrolling )

Existem várias formas de se fazer um efeito de Scrolling.Vamos aprender a fazer 3 tipos de Efeitos.

Page 54: Biblioteca Allegro

Scrolling falso

Um exemplo de scrolling falso é o exemplo do espaço aonde as estrelas vão passando, umas bem lentas e outras rápidamente dando o efeito de que o cenário está andando.

Um outro exemplo é de um cenário que tem a mesma imagem de fundo, quando o personagem vai andando a imagem vai andando também, mais ela acaba sempre se repetindo.

Vamos trabalhar com os 2 exemplos, primeiro, mostraremos nosso programa base que mostra e limita os FPS.

CÓDIGO...

#include <allegro.h>

// PROGRAMA BASE 

// variáveis globaisint fps = 0;int fps_antigo = 0;int fps_speed = 0;

// prototipo do contador de framesvoid frame_rate();

// prototipo do contador de velocidadevoid incrementa_speed();

int main(){   allegro_init();   set_color_depth(16);   install_keyboard();   set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0);      install_timer();   install_int( frame_rate, 1000 );   install_int_ex( incrementa_speed, BPS_TO_TIMER(60) );         BITMAP *buffer = NULL;         // Criando BUFFER para double buffer   buffer = create_bitmap(800,800);      // Laço principal   while( !key[KEY_ESC] )   {      while ( ::fps_speed > 0 )      {         clear( buffer );                  textprintf_ex( buffer, font, 10, 10, makecol(255,0,0), -1, "FPS: %d", ::fps_antigo );                  ::fps_speed--;         ::fps++;      }                        blit(buffer, screen, 0,0,0,0, SCREEN_W, SCREEN_H);      vsync();            }         destroy_bitmap( buffer );      allegro_exit();   return 0;}

Page 55: Biblioteca Allegro

END_OF_MAIN();   void frame_rate(){   ::fps_antigo = ::fps;   ::fps = 0;}   void incrementa_speed(){   ::fps_speed++;}

FIM DE CÓDIGO...

Exemplo do espaço estrelado.

O exemplo não tem muita complicação. O resultado se parece muito com os créditos de vários jogos ( Mega Man ).

Criamos uma classe que sempre fica verificando a posição do objeto, se o objeto está fora da tela, ele reposiciona o objeto para o começo da tela com uma nova velocidade.

Preste atenção na forma como eu aloco cada objeto do array de estrelas, como eu uso eles e como a memória é desalocada.

CÓDIGO...

#ifndef STAR_H#define STAR_H

// ARQUIVO: star.h// Data: 18/08/2007

const int tamanho_total = 3;const int velocidade_total = 15;

class Star {

private:

   int velocidade;   int id;

public:

   Star( int ); // construtor   ~Star(); // destrutor      void iniciar( int ); // inicia o objeto   void verificar(); // verifica se não chegou no final da tela      int x;   int y;   int tamanho;

};

#endif

FIM DE CÓDIGO...

CÓDIGO...

Page 56: Biblioteca Allegro

#include <allegro.h>#include <iostream>#include "star.h"

// ARQUIVO: star.cpp// Data: 18/08/2007

// construtorStar::Star( int vid ){   this->id = vid;      this->velocidade = 0;   this->x = 0;   this->y = 0;   this->tamanho = 0;      this->x = 1 + rand() % SCREEN_W;   this->y = this->id + rand() % ( this->id + 10 );      this->velocidade = ( 1 + rand() % (::velocidade_total) );   this->tamanho = 1 + rand() % (::tamanho_total);}   // destrutorStar::~Star(){   this->velocidade = 0;   this->x = 0;   this->y = 0;   this->tamanho = 0;}   // inicia o objetovoid Star::iniciar( int i ){   if ( i == 0 )   {      i = 1;   }      this->y = i + rand() % ( i + 10 );      this->velocidade = ( 1 + rand() % (::velocidade_total) );   this->tamanho = 1 + rand() % (::tamanho_total);      this->x = SCREEN_W;}   // verifica se não chegou no final da telavoid Star::verificar(){   if ( this->x > 0 )   {         this->x -= this->velocidade;   }   else   {      this->iniciar( this->id );   }}

FIM DE CÓDIGO...

CÓDIGO...

Page 57: Biblioteca Allegro

#include <allegro.h>#include <iostream>#include <ctime>#include "star.h"

// ARQUIVO: main.cpp// Data: 18/08/2007

// Exemplo de scrolling falso

// variáveis globaisint fps = 0;int fps_antigo = 0;int fps_speed = 0;

// prototipo do contador de framesvoid frame_rate();

// prototipo do contador de velocidadevoid incrementa_speed();

int main(){   allegro_init();   set_color_depth(16);   install_keyboard();   set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0);      srand( time(0) ); // determina a randomização         install_timer();   install_int( frame_rate, 1000 );   install_int_ex( incrementa_speed, BPS_TO_TIMER(60) );         BITMAP *buffer = NULL;      int i = 0;   int total_estrelas = SCREEN_H;      Star *estrelas[total_estrelas];      // inicia as estrelas   for ( i=0; i< total_estrelas; i++ )   {      estrelas[ i ] = new Star( i );   }         // Criando BUFFER para double buffer   buffer = create_bitmap(800,800);      // Laço principal   while( !key[KEY_ESC] )   {      while ( ::fps_speed > 0 )      {         clear( buffer );                  // imprime as estrelas na tela e verifica se ela não chegou no final da tela         for ( i=0; i< total_estrelas; i++ )         {            putpixel(buffer, estrelas[ i ]->x, estrelas[ i ]->y, makecol(255,255,255));            estrelas[ i ]->verificar();         }                           textprintf_ex( buffer, font, 10, 10, makecol(255,0,0), -1, "FPS: %d", ::fps_antigo );         

Page 58: Biblioteca Allegro

                  ::fps_speed--;         ::fps++;      }                        blit(buffer, screen, 0,0,0,0, SCREEN_W, SCREEN_H);      vsync();            }         // apaga as estrelas da memoria   for ( i=0; i< total_estrelas; i++ )   {      delete estrelas[i];   }      destroy_bitmap( buffer );      allegro_exit();   return 0;}END_OF_MAIN();   void frame_rate(){   ::fps_antigo = ::fps;   ::fps = 0;}   void incrementa_speed(){   ::fps_speed++;}

FIM DE CÓDIGO...

Exemplo cenário com imagem de fundo repetida.

Este é um exemplo bastante utilizado em jogos de scroll vertical, como o Ninja Gaiden.Faremos uma imagem de fundo passar lentamente, e uma imagem mais perto passar bem rápido, passando a impressão de velocidade e movimentação do cenário.

Vamos usar as seguintes imagens retiradas do Ninja Gaiden 3.

Este será o céu, é uma imagem que andará bem lentamente. Agora vamos pegar a segunda paisagem que andará rapidamente, dando o efeito de scroll.

Page 59: Biblioteca Allegro

 

Novamente iremos utilizar nosso travador de FPS.A lógica do programa é fazer copias das imagens, para que o usuário não perceba que elas tem um inicio e um fim.Iremos concatenar a imagem para fazer isso.

CÓDIGO...

#include <allegro.h>

// ARQUIVO: main.cpp// Data: 20/08/2007

// Exemplo de scrolling falso

// variáveis globaisint fps = 0;int fps_antigo = 0;int fps_speed = 0;

// prototipo do contador de framesvoid frame_rate();

// prototipo do contador de velocidadevoid incrementa_speed();

int main(){   allegro_init();   set_color_depth(16);   install_keyboard();   set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0);      install_timer();   install_int( frame_rate, 1000 );   install_int_ex( incrementa_speed, BPS_TO_TIMER(60) );         // Criando BUFFER para double buffer   BITMAP *buffer = NULL;   buffer = create_bitmap(800,800);      // Load das imagens .bmp ( converter de jpg para bmp )   BITMAP *img_fundo1 = NULL;   BITMAP *img_fundo2 = NULL;      img_fundo1 = load_bitmap("ceu1.bmp", NULL );   img_fundo2 = load_bitmap("cenario2.bmp", NULL );      // essas variáveis são variáveis auxiliares que ajudarão muito   BITMAP *fundo1 = NULL;   BITMAP *fundo2 = NULL;      fundo1 = create_bitmap( 1022, 180 );

Page 60: Biblioteca Allegro

   fundo2 = create_bitmap( 780, 149 );      // estou criando o fundo em uma variavel auxiliar   blit(img_fundo1, fundo1, 0, 0, 0, 0, 511, 180 );   blit(img_fundo1, fundo1, 0, 0, 511, 0, 511, 180 );      // estou usando masked para ficar incolor a area rosa   masked_blit(img_fundo2, fundo2, 0, 0, 0, 0, 260, 149 );   masked_blit(img_fundo2, fundo2, 0, 0, 260, 0, 260, 149 );   masked_blit(img_fundo2, fundo2, 0, 0, 420, 0, 260, 149 );         // Laço principal   while( !key[KEY_ESC] )   {      while ( ::fps_speed > 0 )      {         clear( buffer );                  masked_blit( fundo1, buffer, 0, 0, 0, 0, 1022, 180 );         masked_blit( fundo2, buffer, 0, 0, 0, 130, 780, 149 );                  textprintf_ex( buffer, font, 10, 10, makecol(255,0,0), -1, "FPS: %d", ::fps_antigo );                           ::fps_speed--;         ::fps++;      }                  blit(buffer, screen, 0,0,0,0, SCREEN_W, SCREEN_H);      vsync();         }         destroy_bitmap( buffer );      destroy_bitmap( img_fundo1 );   destroy_bitmap( img_fundo2 );      destroy_bitmap( fundo1 );   destroy_bitmap( fundo2 );      allegro_exit();   return 0;}END_OF_MAIN();   void frame_rate(){   ::fps_antigo = ::fps;   ::fps = 0;}   void incrementa_speed(){   ::fps_speed++;}

FIM DE CÓDIGO...

Agora vamos implementar no código acima, a rolagem do cenário. Observe no código abaixo, que colamos 2 imagens iguais de fundo no buffer.. uma ao lado da outra.A lógica funciona da seguinte maneira.

Enquanto nosso contador for menor que o tamanho total de 1 imagem ( a mesma imagem montada uma ao lado da outra ), a gente vai incrementando ela e mostrando na tela a imagem na posição do contador. Desta forma a imagem

Page 61: Biblioteca Allegro

vai andando para o lado.

Para que a imagem seja repetida corretamente, a segunda imagem é colada na posição(total_fundo1*(-1))+x_fundo1)

Que é igual a, -1022 + x_fundo1, isso vai fazer com que nossa segunda imagem fique exatamente no final da primeira imagem, e isso também vai previnir de que o cenário seja quebrado. 

CÓDIGO...

#include <allegro.h>

// ARQUIVO: main.cpp// Data: 20/08/2007

// Exemplo de scrolling falso

// variáveis globaisint fps = 0;int fps_antigo = 0;int fps_speed = 0;

// prototipo do contador de framesvoid frame_rate();

// prototipo do contador de velocidadevoid incrementa_speed();

int main(){   allegro_init();   set_color_depth(16);   install_keyboard();   set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0);      install_timer();   install_int( frame_rate, 1000 );   install_int_ex( incrementa_speed, BPS_TO_TIMER(30) );         // Criando BUFFER para double buffer   BITMAP *buffer = NULL;   buffer = create_bitmap(800,800);      // Load das imagens .bmp ( converter de jpg para bmp )   BITMAP *img_fundo1 = NULL;   BITMAP *img_fundo2 = NULL;      img_fundo1 = load_bitmap("ceu1.bmp", NULL );   img_fundo2 = load_bitmap("cenario2.bmp", NULL );      // essas variáveis são variáveis auxiliares que ajudarão muito   BITMAP *fundo1 = NULL;   BITMAP *fundo2 = NULL;      fundo1 = create_bitmap( 1022, 180 );   fundo2 = create_bitmap( 780, 149 );      // estou criando o fundo em uma variavel auxiliar   blit(img_fundo1, fundo1, 0, 0, 0, 0, 511, 180 );   blit(img_fundo1, fundo1, 0, 0, 511, 0, 511, 180 );      // estou usando masked para ficar incolor a area rosa   masked_blit(img_fundo2, fundo2, 0, 0, 0, 0, 260, 149 );   masked_blit(img_fundo2, fundo2, 0, 0, 260, 0, 260, 149 );   masked_blit(img_fundo2, fundo2, 0, 0, 520, 0, 260, 149 );   //masked_blit(img_fundo2, fundo2, 0, 0, 420, 0, 260, 149 );         // Inicio programação da rolagem

Page 62: Biblioteca Allegro

   int velocidade_fundo1 = 1;   int velocidade_fundo2 = 6;      int total_fundo1 = 1022;   int total_fundo2 = 780;      int x_fundo1 = 0;   int x_fundo2 = 0;      // Laço principal   while( !key[KEY_ESC] )   {      while ( ::fps_speed > 0 )      {         clear( buffer );                  // faz com que, antes que o segundo fundo termine, começe a imprimir o primeiro fundo         if ( x_fundo1 < ( total_fundo1 ) )         {            x_fundo1 = x_fundo1 + velocidade_fundo1;         }         else         {            x_fundo1 = 0;         }                  // vai passando o primeiro fundo         masked_blit( fundo1, buffer, x_fundo1, 0, 0, 0, 1022, 180 );                  // imprime no final do primeiro fundo, e vai passando         masked_blit( fundo1, buffer, ((total_fundo1*(-1))+x_fundo1), 0, 0, 0, 1022, 180 );                  if ( x_fundo2 < ( total_fundo2 ) )         {            x_fundo2 = x_fundo2 + velocidade_fundo2;         }         else         {            x_fundo2 = 0;         }                  masked_blit( fundo2, buffer, x_fundo2, 0, 0, 130, 780, 149 );         masked_blit( fundo2, buffer, ((total_fundo2*(-1))+x_fundo2), 0, 0, 130, 780, 149 );                  textprintf_ex( buffer, font, 10, 10, makecol(255,0,0), -1, "FPS: %d", ::fps_antigo );                  ::fps_speed--;         ::fps++;      }                        blit(buffer, screen, 0,0,0,0, SCREEN_W, SCREEN_H);      vsync();            }                     destroy_bitmap( buffer );      destroy_bitmap( img_fundo1 );   destroy_bitmap( img_fundo2 );      destroy_bitmap( fundo1 );   destroy_bitmap( fundo2 );      allegro_exit();   return 0;}END_OF_MAIN();

void frame_rate(){   ::fps_antigo = ::fps;

Page 63: Biblioteca Allegro

   ::fps = 0;}

void incrementa_speed(){   ::fps_speed++;}

FIM DE CÓDIGO...

Exemplo de scrolling verdadeiro

Imagine uma fase do Super Mario, quando o personagem chega no limite da tela, a tela começa a andar, e os sprites de fundo começam a se mover.

É esse tipo de simulação que iremos mostrar agora.O código abaixo é controlado pelo teclado. Utilize as setas para fazer o cenário andar. 

Para escrever o código, primeiro precisamos saber que o personagem é o centro da tela da nossa rolagem ( ele não precisa estar no centro da tela necessariamente ).

Quando o personagem se mover, devemos mover todos os tiles do fundo na posição oposta. Para isso, precisamos também ter o tamanho total da tela, no caso, a fase do nosso jogo.

Nossa tela medirá 500 px.Nós vamos especificar o tamanho do cenario em 250 tiles.Cada tile terá 50 px de tamanho total;

Iremos usar também a parte central da tela, desta forma, podemos ver o efeito de scrolling.

Você vai perceber que nossos códigos estão começando a aumentar hehehe.. Isto é normal.A técnica utilizada abaixo pode ser usada com um construtor de cenários, basta entender o algoritmo de rolagem que é bem simples.

CÓDIGO...

#include <allegro.h>

// ARQUIVO: main.cpp// Data: 22/08/2007

// Exemplo de scrolling verdadeiro

// variáveis globaisint fps = 0;int fps_antigo = 0;int fps_speed = 0;

// prototipo do contador de framesvoid frame_rate();

// prototipo do contador de velocidadevoid incrementa_speed();

int main(){   allegro_init();   set_color_depth(16);   install_keyboard();   set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0);      install_timer();   install_int( frame_rate, 1000 );

Page 64: Biblioteca Allegro

   install_int_ex( incrementa_speed, BPS_TO_TIMER(30) );      // Criando BUFFER para double buffer   BITMAP *buffer = NULL;   buffer = create_bitmap(800,800);      // variáveis comentadas no escopo do programa   const int tamanho_tela = 500;   const int inicial_tela = ( (SCREEN_W/2)-(tamanho_tela/2) );   const int cenario = 250; // tiles   const int tamanho_tile = 50;      // posição de nosso personagem   int personagem_pos = inicial_tela + 10;   int x = personagem_pos;      // auxiliares   int i = 0;   int total_for = 0;   int posicao = 0;      int posicao_cenario_inicial = inicial_tela;   int posicao_cenario = 0;            // Laço principal   while( !key[KEY_ESC] )   {      while ( ::fps_speed > 0 )      {               clear( buffer );                  // este for faz com que seja mostrado apenas a parte do cenario visivel para o usuário         // para isso ele só vai mostrar         // os tiles que cabem na tela ( tamanho_tela / tamanho_tile )         // mais a posição atual do cenario ( que é incrementado quando se aperta uma tecla )         // usamos o -1 para trocar o sinal da operação         total_for = ( tamanho_tela / tamanho_tile )+( posicao_cenario*(-1) );         for ( i=posicao_cenario; i<=total_for; i++ )         {            // aqui determinamos a posição correta de cada tile            // usamos a posicao_cenario_inicial para somar aonde começa o cenario            posicao = ( posicao_cenario + i ) * tamanho_tile;            posicao += posicao_cenario_inicial;                        // impressão dos tiles nas posições corretas            rectfill(buffer, posicao, 230, posicao+tamanho_tile, 250, makecol(0,0,255) );            rect(buffer, posicao, 230, posicao+tamanho_tile, 250, makecol(255,0,0) );            textprintf_ex( buffer, font, posicao+1, 235, makecol(255,255,0), -1, "%d", i );         }                           // esse será nosso limite de tela         rect(buffer, inicial_tela, 10, inicial_tela+tamanho_tela, 250, makecol(0,255,0) );                  // esse é nosso personagem         personagem_pos = x;         rectfill(buffer, personagem_pos, 210, personagem_pos+10, 230, makecol(255,255,0) );                           textprintf_ex( buffer, font, 10, 10, makecol(255,0,0), -1, "FPS: %d", ::fps_antigo );                  ::fps_speed--;         ::fps++;      }                        if ( key[KEY_RIGHT] )      {         // atualiza a posição do nosso personagem

Page 65: Biblioteca Allegro

         if ( x < ( tamanho_tela + inicial_tela ) / 2 )         {            x++;         }         else         {            // do cenario            // nunca vai poder passar o limite do cenario            if ( (posicao_cenario ) > - ( ( cenario ) - (tamanho_tela / tamanho_tile) ) )            {               posicao_cenario--;            }            else            {               // atualiza a posição do nosso personagem               if ( x < (inicial_tela+tamanho_tela)-10 )               {                  x++;               }            }         }      }                  if ( key[KEY_LEFT] )      {         // atualiza a posição do nosso personagem         if ( x > ( tamanho_tela + inicial_tela ) / 2 )         {            x--;         }         else         {            // do cenario            // nunca vai poder passar o limite do cenario            if ( posicao_cenario < cenario && posicao_cenario < 0 )            {               posicao_cenario++;            }            else            {               // atualiza a posição do nosso personagem               if ( x > ( inicial_tela ) )               {                  x--;               }            }         }                  }                  blit(buffer, screen, 0,0,0,0, SCREEN_W, SCREEN_H);      vsync();               }                           destroy_bitmap( buffer );                                    allegro_exit();   return 0;}END_OF_MAIN();            void frame_rate(){   ::fps_antigo = ::fps;   ::fps = 0;}            void incrementa_speed(){   ::fps_speed++;

Page 66: Biblioteca Allegro

}

30 - Scrolling de Tiles

No tutorial 26 "Utilizando Tiles" já foi explicado conceitos básicos sobre tiles e sua aplicação. Nesse tutorial, estarei mostrando uma técnica chamada SCROLLING de TILES (Não é importante que você tenha lido o tutorial 26 antes de continuar neste tutorial, pois estarei mostrando uma abordagem diferente sobre o assunto). Para imprimir tiles na tela, é usado um mapa para representar a posição de cada tile. Para preenchermos uma tela de 640x480, e usando tiles de 32x32, precisaríamos de 20 tiles na horizontal e 15 na vertical. Com SCROLLING DE TILES, podemos ter o mapa do tamanho que quisermos (não preciso nem dizer que o mapa é representado por uma matriz multidimensional). SCROLING DE TILES é amplamente usado em jogos 2d, como Mario ou Metroid.

Portanto, poderemos ter um mapa de tamanho qualquer, não importa a altura nem a largura, nosso algoritmo deve imprimir os tiles na tela perfeitamente. Você pode estar pensando: "é só imprimir todos os tiles na tela". Nada impede que você faça isso, mas seu jogo ficará extremamente lento. O correto é imprimir somente a área correspondente ao tamanho da tela.

Básico

O tutorial 26 mostrou um jeito fácil de imprimir tiles na tela. Nessa parte básica, vou mostrar outra forma de obter o mesmo resultado, ou seja, imprimir tiles carregando o mapa a partir de um arquivo. Primeiro, precisamos declarar algumas constantes e estruturas básicas:

CÓDIGO...

/* Tamanho do tile */

#define TILEWH 32

typedef struct{    BITMAP * bitmap;

} SPRITE;

typedef struct{    SPRITE * sprite;

} TILE;

typedef struct{    int w;    int h;

    TILE ** tiles;

} MAP;

typedef struct{    int w;    int h;

} MAPINFO;

Page 67: Biblioteca Allegro

FIM DE CÓDIGO...

A primeira estrutura, SPRITE, armazenará informações do tile atual, que no nosso caso vai ser somente estático (imagem fixa). A segunda estrutura, TILE, armazenará informações sobre que tipo de SPRITE iremos imprimir na tela. A estrutura MAP armazenará o mapa, contendo a posição x e y da cada tile. 

MAPINFO é um estrutura adicional e será explicada mais à frente.

Os Tiles

Usarei tiles de 32x32px de tamanho neste tutorial. Portanto, o arquivo Bitmap deve ser múltiplo de 32. Poderíamos carregar os tiles manualmente, mas um processo automatizado é sempre melhor, então, criaremos uma função para carregar os tiles a partir de um arquivo Bitmap. A função irá retornar um vetor de SPRITE.

CÓDIGO...

/* Função para carga de tiles */

SPRITE * load_tiles(const char * filename){     /* Armazenando o bitmap em uma variável temporária */

    BITMAP * tmp = load_bitmap(filename, NULL);

    /* Verificando se o bitmap foi carregado */

    if (tmp == NULL)        return NULL;

     /* Pegando o número de tiles na horizontal e na vertical */

    int xtiles = tmp->w / TILEWH;    int ytiles = tmp->h / TILEWH;

    /* Total de tiles no bitmap */

    int total = xtiles * ytiles;

    /* Criando o vetor de SPRITE */

    SPRITE * spr = (SPRITE *) malloc(sizeof(SPRITE) * total);

    int x;    int y;

    /* Índice para o vetor de SPRITE */

    int i = 0;

    /* Faça enquanto houver tiles na horizontal e na vertical */

    for (y = 0; y < ytiles; y++)    {        for (x = 0; x < xtiles; x++)

Page 68: Biblioteca Allegro

        {             /* Criando o BITMAP que vai armazenar o tile */

            spr[i].bitmap = create_bitmap(TILEWH, TILEWH);

             /* Copiando o pedaço da imagem para o SPRITE */

            blit(tmp, spr[i].bitmap, x * TILE, y * TILE, 0, 0, TILE, TILE);

             /* Próximo índice */

            i++;        }    }

     /* Libere a memória alocada para o bitmap */

    destroy_bitmap(tmp);

    /* Retorne o vetor de SPRITE */

    return spr;}

FIM DE CÓDIGO...

Explicando

No código acima, criamos uma rotina de carga de tiles. Um arquivo Bitmap é requerido. Para obtermos o número de tiles na horizontal e na vertical, basta pegar a altura e largura do Bitmap e dividir pelo tamanho do tile.

int xtiles = tmp->w / TILEWH;int ytiles = tmp->h / TILEWH;

Calculamos o total de tiles no bitmap

int total = xtiles * ytiles;

Já temos condição de criar o vetor de SPRITE, que armazenará os tiles:

SPRITE * spr = (SPRITE *) malloc(sizeof(SPRITE) * total);

E o final: carregar os tiles do bitmap "tmp" no vetor.

int x;int y;

int i = 0;

for (y = 0; y < ytiles; y++){    for (x = 0; x < xtiles; x++)    {        spr[i]->bitmap = create_bitmap(TILE, TILE);

        blit(tmp, spr[i]->bitmap, x * TILE, y * TILE, 0, 0, TILE, TILE);

Page 69: Biblioteca Allegro

        i++;    }}

Primeiro, declaramos uma variável i, que armazenará o índice no vetor de SPRITE (nosso vetor de SPRITE é linear). Os dois laços de repetição FOR são usados para percorrer o bitmap "tmp" de cima para baixo e da esquerda para a direita. 

Para cada vez que o laço y for executado, o laço x será executado N vezes. 

Na prática ficaria:

+-------+-------+-------+|       |       |       ||   1   |   2   |   3   |+-------+-------+-------+

Isso se tivéssemos um BITMAP de 96x32. Se tivéssemos um BITMAP de 96x96:

+-------+-------+-------+|       |       |       ||   1   |   2   |   3   |+-------+-------+-------+|       |       |       ||   4   |   5   |   6   |+-------+-------+-------+|       |       |       ||   7   |   8   |   9   |+-------+-------+-------+

Os tiles ficariam organizados nessa ordem no vetor. Para finalizar, criamos realmente o BITMAP da estrutura SPRITE e "blitamos" o tile nele:

spr[i]->bitmap = create_bitmap(TILEWH, TILEWH);

blit(tmp, spr[i]->bitmap, x * TILEWH, y * TILEWH, 0, 0, TILEWH, TILEWH);

Lembrando que a posição do tile é definida pelos parâmetros x e y dos laços. Como nossos TILES tem 32x32 de tamanho, fica fácil encontrar sua posição no BITMAP tmp, pois é só multiplicar por 32.

O Mapa

Normalmente, tudo em um jogo é colocado dentro de arquivos, no caso de mapas, é mais simples e você ainda tem a possibilidade de criar outros mapas para seu jogo. Pode-se usar programas como o Mappy ou Tile Studio, mas a verdade é que VOCÊ deve fazer seu próprio editor de mapas. O mapa pode ser armazenado em arquivos texto ou binário.

Para usarmos arquivos texto, precisaríamos criar um ANALISADOR LÉXICO para ler os tokens. Escrever um analisador léxico (descente) não é fácil, e outra coisa, o mapa ficaria exposto (com qualquer editor de texto poderíamos editar o mapa). Com arquivos binários, é mais simples e para alterar o mapa será necessário um editor hexadecimal.

Usaremos o Mappy para criar os mapas, mas não usaremos sua API para imprimi-lo na tela. Baixe o Mappy no endereço:

http://www.tilemap.co.uk/mappy.php

Basta descompactá-lo e executar normalmente. Abra-o e uma tela como essa será apresentada:

Page 70: Biblioteca Allegro

Para criar um mapa, basta ir ao menu "File / New Map". Altere o tamanho do tile se quiser, mas a configuração padrão é a de tiles de 32x32. Mais abaixo, coloque o tamanho na horizontal (tiles wide) e na vertical (tiles high), lembrando que isso significa o tamanho da tela em tiles, não em pixels. Crie um mapa com tiles de 32x32 e a tela com 40 tiles na horizontal e 15 na vertical.

Depois de criar o mapa, é só importar o tiles. Baixe os seguintes tiles (clique em cima da imagem para fazer o download):

Agora, para importar, vá no menu "File / Import", e escolha um arquivo .bmp contendo os tiles que você baixou ou os de sua escolha. Selecione o tile na janela à direita e depois pinte o mapa. Faça um mapa parecido com este:

Depois que o mapa estiver pronto, precisaremos gerar um arquivo. Como não usaremos a API do Mappy, precisaremos exportar para um outro formato de arquivo que seja fácil

Page 71: Biblioteca Allegro

de abrirmos. 

Vá no menu "Custom / Export binary file". 

Uma janela abrirá, basta clicar em OK e outra janela abrirá. 

Nela diz se você quer ajustar os valores dos tiles (pode-se ajustar tanto para cima quanto para baixo). O valor "-1" é o padrão, mas devemos alterar para "0" pelo seguinte fato: os tiles vazios são representados pelo número "0", e o nosso primeiro tile é representado pelo número "1", se colocarmos o valor de ajuste como "-1", nosso primeiro tile será "0", ou seja, ele será um tile vazio!

Ao final, um arquivo .map será criado. Segue-se a estrutura desse tipo de arquivo:

+----------------------+| Cabeçalho            |+----------------------+| Dados sobre a fase   ||                      ||                      ||                      |+----------------------+

As primeiras informações no arquivo referem-se ao tamanho do mapa (em tiles) na horizontal e na vertical. Usaremos a estrutura MAPINFO para obter essas informações para depois ler o mapa. Segue a rotina de carga de mapa:

CÓDIGO...

/* Rotina para carga de mapa */

MAP * load_map(const char * filename, SPRITE * spr){    /* Abrindo o arquivo de mapa */

    FILE * file = fopen(filename, "rb");

    /* Verificando se ele existe */

    if (!file)        return NULL;

     /* Alocando memória para o mapa */

    MAP * map = (MAP *) malloc(sizeof(MAP));

    /* Pegando informaçõe sobre o mapa */

    MAPINFO info;

    fread(& info, sizeof(MAPINFO), 1, file);

    /* Altura e largura do mapa */

    map->w = info.w;    map->h = info.h;

    /* Crie a matriz na vertical */

    map->tiles = (TILE **) malloc(sizeof(TILE) * map->h);

Page 72: Biblioteca Allegro

    int x;    int y;

    int value;

    for (y = 0; y < map->h; y++)    {        /* Crie a matriz na horizontal */

        map->tiles[y] = (TILE *) malloc(sizeof(TILE) * map->w);

        for (x = 0; x < map->w; x++)        {            /* Lendo o tipo de tile */

            fread(& value, sizeof(int), 1, file);

             /* Se o valor do tile for diferente de 0, atribua o SPRITE, senão, NULL */

            map->tiles[y][x].sprite = value != 0 ? & spr[value - 1] : NULL;        }    }

    /* Feche o handle de arquivo */

    fclose(file);

    /* Retorne o mapa */

    return map;}

FIM DE CÓDIGO...

Explicando

No código acima, um arquivo de mapa é requerido e o vetor de SPRITE contendo os tiles. 

Primeiramente, pegamos as informações referentes ao mapa (altura e largura em tiles). A partir daí, é só carregar o mapa. A leitura do mapa é feita sequencialmente, quem define a posição de cada tile são os laços de repetição FOR. 

Lemos um inteiro de cada vez e verificamos se o seu valor não é 0. Se for 0, o valor NULL é atribuído, senão, atribua o ponteiro para o vetor de SPRITE (lembrando que na rotina de carga de tiles, os SPRITEs são carregados linearmente, começando pelo índice 0, que é o primeiro tile).

Obs.: No Mappy, o primeiro tile representa o índice 1, por isso a subtração por 1.

Imprimindo o mapa

A rotina de impressão de mapa deste tutorial difere um pouco do tutorial 26. Se você reparar bem, o código mostrado até agora parece ser uma extensão do allegro. Eis a rotina de impressão de mapa:

CÓDIGO...

/* Rotina de impressão de mapa */

Page 73: Biblioteca Allegro

void draw_map(BITMAP * bitmap, MAP * map, int xpos, int ypos){     /* Posição inicial */

    int xbegin = abs(xpos / TILEWH);    int ybegin = abs(ypos / TILEWH);

     /* Verificando se não ultrapassa o mapa */

    if (xpos > 0)        xbegin = 0;

    if (ypos > 0)        ybegin = 0;

     /* Posição final */

    int xend = SCREEN_W / TILEWH + xbegin + 1;    int yend = SCREEN_H / TILEWH + ybegin + 1;

     /* Verificando se não ultrapassa o mapa */

    if (xend > map->w)        xend = map->w;

    if (yend > map->h)        yend = map->h;

    int x;    int y;

    /* Imprima o mapa! */

    for (y = ybegin; y < yend; y++)        for (x = xbegin; x < xend; x++)            if (map->tiles[y][x].sprite)                draw_sprite(bitmap, map->tiles[y][x].sprite->bitmap, x * TILEWH + xpos, y * TILEWH + ypos);}

FIM DE CÓDIGO...

Explicando

A rotina acima se propõe a imprimir um MAPA no "BITMAP bitmap". As posições x e y são também requeridas. Primeiro, pegamos as posições iniciais x e y de pintura da tela. Como dito anteriormente, a tela fica sendo dividida em tiles, então, devemos pegar a posição do tile e não a posição na tela, portanto, devemos dividir as posições x e y pelo tamanho do tile que estamos usando (32x32).

Se xpos for 16, dividido por 32, fica sendo 0,5. Arredondando dá 0. Então essa é a posição x inicial. O mesmo para ypos (abs() foi usando pois não adianta imprimir antes da posição 0,0 da tela). os dois IF's verificam se as posições xpos e ypos são maiores que 0, então, a posição inicial x e y ficam sendo 0.

Já encontramos as posições iniciais x e y, agora falta encontrar as posições finais. As posições finais são fáceis de encontrar: basta pegar o tamanho da tela em tiles (tanto na horizontal quanto na vertical) e somar com o x e y inicial. O "+ 1" é para que a rotina de impressão imprima até para fora da tela (somente 1 tile a mais). Sem isso, os tiles não serão impressos até que preencham a tela toda.

Sabendo as posições iniciais e finais, basta imprimir na tela. Antes de imprimir, verificamos se o SPRITE é nulo (NULL), e se não for, imprimimos ele na tela. Encontramos a posição correta de cada tile multiplicando pelo tamanho do mesmo, ou seja:

Page 74: Biblioteca Allegro

Tile: 0,0 - Posição: 0, 0Tile: 0,1 - Posição: 0, 32...Tile: 1,1 - Posição: 32, 32...

Acrecentamos também xpos e ypos à posição do tile, para não dar um efeito de MOSAICO na tela.

O Código Completo

Para executar o código abaixo, baixe o seguinte arquivo : mapa.map

CÓDIGO...

#include <allegro.h>#include <stdio.h>#include <stdlib.h>#include <math.h>

#define TILEWH 32

#define SPEED 1

typedef struct{    BITMAP * bitmap;

} SPRITE;

typedef struct{    SPRITE * sprite;

} TILE;

typedef struct{    TILE ** tiles;

    int w;    int h;

} MAP;

typedef struct{    int w;    int h;

} MAPINFO;

BITMAP * buffer;

SPRITE * load_tiles(const char *);

MAP * load_map(const char *, SPRITE *);void draw_map(BITMAP *, MAP *, int, int);

Page 75: Biblioteca Allegro

int main(int argc, char ** argv){    allegro_init();

    install_keyboard();

    set_color_depth(16);    set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0);

    buffer = create_bitmap(SCREEN_W, SCREEN_H);

    SPRITE * spr = load_tiles("tiles.bmp");

    if (!spr)    {        allegro_message("Falha ao carregar SPRITES!");

        return 1;    }

    MAP * map = load_map("mapa.map", spr);

    if (!map)    {        allegro_message("Falha ao carregar MAPA!");

        return 1;    }

    int xmap = 0;    int ymap = 0;

    int x = 0;    int y = 0;

    while (!key[KEY_ESC])    {        if (key[KEY_LEFT])            xmap += SPEED;

        if (key[KEY_RIGHT])            xmap -= SPEED;

        clear(buffer);

        draw_map(buffer, map, xmap, 0);

        blit(buffer, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);    }

    destroy_bitmap(buffer);

    allegro_exit();

    return 0;}END_OF_MAIN();

SPRITE * load_tiles(const char * filename){    BITMAP * tmp = load_bitmap(filename, NULL);

Page 76: Biblioteca Allegro

    if (!tmp)        return NULL;

    int xtiles = tmp->w / TILEWH;    int ytiles = tmp->h / TILEWH;

    int total = xtiles * ytiles;

    SPRITE * spr = (SPRITE *) malloc(sizeof(SPRITE *) * total);

    int x;    int y;

    int i = 0;

    for (y = 0; y < ytiles; y++)    {        for (x = 0; x < xtiles; x++)        {            spr[i].bitmap = create_bitmap(TILEWH, TILEWH);

            blit(tmp, spr[i].bitmap, x * TILEWH, y * TILEWH, 0, 0, TILEWH, TILEWH);

            i++;        }    }

    destroy_bitmap(tmp);

    return spr;}

MAP * load_map(const char * filename, SPRITE * spr){    FILE * file = fopen(filename, "rb");

    if (!file)        return NULL;

    MAP * map = (MAP *) malloc(sizeof(MAP));

    MAPINFO info;

    fread(& info, sizeof(MAPINFO), 1, file);

    map->w = info.w;    map->h = info.h;

    map->tiles = (TILE **) malloc(sizeof(TILE **) * map->h);

    int x;    int y;

    int value;

    for (y = 0; y < map->h; y++)    {        map->tiles[y] = (TILE *) malloc(sizeof(TILE *) * map->w);

        for (x = 0; x < map->w; x++)        {            fread(& value, sizeof(int), 1, file);

Page 77: Biblioteca Allegro

            map->tiles[y][x].sprite = value != 0 ? & spr[value - 1] : NULL;        }    }

    fclose(file);

    return map;}

void draw_map(BITMAP * bitmap, MAP * map, int xpos, int ypos){    int xbegin = abs(xpos / TILEWH);    int ybegin = abs(ypos / TILEWH);

    if (xpos > 0)        xbegin = 0;

    if (ypos > 0)        ybegin = 0;

    int xend = SCREEN_W / TILEWH + xbegin + 1;    int yend = SCREEN_H / TILEWH + ybegin + 1;

    if (xend > map->w)        xend = map->w;

    if (yend > map->h)        yend = map->h;

    int x;    int y;

    for (y = ybegin; y < yend; y++)        for (x = xbegin; x < xend; x++)            if (map->tiles[y][x].sprite)                draw_sprite(bitmap, map->tiles[y][x].sprite->bitmap, x * TILEWH + xpos, y * TILEWH + ypos);}

Fim

Chegamos ao final deste EXTENSO tutorial. Se houver alguma dúvida, crítica ou sugestão sobre o tutorial é só ir no fórum, ficarei muito grato. Num próximo tutorial, estarei escrevendo sobre colisão com o cenário (um dos assuntos mais comentados sobre desenvolvimento de jogos).

Mais uma coisa: se você quer ver a rotina de impressão de mapa trabalhar, altere o código da seguinte forma:

for (y = ybegin + 1; y < yend - 1; y++)    for (x = xbegin + 1; x < xend - 1; x++)        if (map->tiles[y][x].sprite)            draw_sprite(bitmap, map->tiles[y][x].sprite->bitmap, x * TILEWH + xpos, y * TILEWH + ypos);

Você pode incrementar o cenário, imprimindo uma imagem de fundo atrás do mapa, como um céu ou uma caverna.

Espero que tenham gostado do tutorial e até mais...

31 - Pulando e Subindo Morros

Page 78: Biblioteca Allegro

Chegamos em um dos capítulos mais interessantes e também muito importante para nossa linha de aprendizado.Iremos aprender como fazer nosso personagem pular e subir morros.

Este capítulo é importante por que iremos introduzir a partir de agora um dos assuntos mais complicados na programação de jogos: Matemática e Física.

Nosso objetivo é sempre deixar tudo muito claro, e por causa disso não iremos dar aulas de física avançada aqui.A idéia é deixar claro como certas coisas em jogos são feitas.

Muito jogos utilizam física, mais certamente, todos utilizam matemática. Nenhum dos dois assuntos é difícil, basta que ele seja bem explicado e bem compreendido por quem lê.

O Algoritmo para fazer um personagem pular é bem simples e ao mesmo tempo sensacional.Sensacional por que nele vemos como a física é utilizada claramento nos jogos.

Para fazer um personagem pular, primeiro precisamos pensar como no mundo real.O que faz com que a gente não saia voando por ai ??

A gravidade.

E quando tentamos pular, ou correr, ou se movimentar, o que nosso corpo precisa fazer para sair do lugar?

Aplicar Força.

Desta forma, temos que a gravidade sempre vai agir sobre nosso personagem ( assim como no mundo real ),e para que ele pule, basta aplicar uma força X que irá se perder aos poucos e fazer o objeto cair.

Veja o código:

Para entender o código, é importante que você já tenha lido os seguintes tutoriais:

23 - Conceito X e Y   24 - Movendo um objeto na tela28 - FPS - Quadros por segundo ou Frame Rate

 

Algoritmo de Pulo

CÓDIGO...

#include <allegro.h>

// Programa que simula pulos de um personagem 

// variáveis globaisint fps = 0;int fps_antigo = 0;int fps_speed = 0;

// prototipo do contador de framesvoid frame_rate();

// prototipo do contador de velocidadevoid incrementa_speed();

int main(){   allegro_init();   set_color_depth(16);   install_keyboard();   set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0);   

Page 79: Biblioteca Allegro

   install_timer();   install_int( frame_rate, 1000 );   install_int_ex( incrementa_speed, BPS_TO_TIMER(30) );       BITMAP *buffer = NULL;   buffer = create_bitmap(800,800);      // Inicio do programa atual       // Esse será nosso chão   const int limite_cenario = 300;      // posição do personagem   // nosso personagem terá 10 pixels   int x = 10;   int y = limite_cenario - 10;      // valida se o personagem já está no pulo   bool pulou = false;      // Gravidade: quanto maior, mais rápida será a queda   const int gravidade = 2;      // Força do pulo   int forca = 0;      // Laço principal   while( !key[KEY_ESC] )   {      while ( ::fps_speed > 0 )      {         clear( buffer );                  // Caso o personagem nao esteja pulando          // e apertou a tecla espaço         if ( key[KEY_SPACE] && pulou == false )         {            pulou = true;                         // força do pulo             forca = 30;         }                  // movimentação do personagem         if ( key[KEY_RIGHT] )         {            x+=5;         }                  // movimentação do personagem         if ( key[KEY_LEFT] )         {            x-=5;         }               // Isto vai fazer com que o personagem vá voltado para o chão         forca = forca - gravidade;         y = y - forca;                  // Esse if vai barrar nosso personagem de passar pelo chão ( limite )         if (y > limite_cenario-10)          {            y = limite_cenario-10;                        pulou = false;             forca = 0;         }               textprintf_ex( buffer, font, 10, 10, makecol(255,0,0), -1, "FPS: %d", ::fps_antigo );         textprintf_ex( buffer, font, 10, 30, makecol(255,0,0), -1, "Forca: %d", forca );         

Page 80: Biblioteca Allegro

         rectfill( buffer, x, y, x+10, y+10, makecol(255,255,0) );         rectfill( buffer, 0, 300, SCREEN_W, 310, makecol(255,0,0) );                  ::fps_speed--;         ::fps++;      }         blit(buffer, screen, 0,0,0,0, SCREEN_W, SCREEN_H);      vsync();         }      destroy_bitmap( buffer );      allegro_exit();   return 0;}END_OF_MAIN();   void frame_rate(){   ::fps_antigo = ::fps;   ::fps = 0;}   void incrementa_speed(){   ::fps_speed++;}

FIM DE CÓDIGO...

Observe que, quanto mais força você der para o personagem, mais alto ele irá pular.Quanto mais gravidade você colocar, menor será o tempo de pulo.

O código é alto explicativo, a gravidade sempre faz com que o personagem vai para baixo ( ela exerce na força ).Quando o personagem quer pular, adicionamos força, o que faz com que o personagem vá para cima.

 

 

Subindo Morros

Para fazer um personagem subir um morro, vamos utilizar o exemplo acima.

Aqui a idéia já começa a complicar um pouco mais.Quando o personagem vai subir um morro, primeiro temos que pensar em como vamos fazer o morro.Qual vai ser a colisão com o cenário e de que forma o personagem vai aparecer ao subir o morro.

Nós temos que responder cada uma dessas perguntas para resolver o problema.

Primeiro, no algoritmo de pulo, definimos um limite de cenário, que no caso é o chão.Neste exemplo não poderemos mais fazer isto, simplesmente por que agora não teremos mais um limite, já que agora o chão pode ser um morro de limite um pouco maior ( para cima ou para baixo ).

Para isto precisamos fazer um jogo usando objetos para o chão que irão colidir com o personagem e mante-lo na posição acima.

Vamos analisar uma solução, observe o gráfico:

Page 81: Biblioteca Allegro

Este gráfico representa um plano cartesiano, nós temos um morro ( ou subida ) desenhada nele.Nosso morro começa no ponto X( 0 ) e vai até o ponto X( 12 ).Nosso morro também começa no ponto Y( 12 ) e vai até o ponto Y( 0 ).

Observe que o plano está como se fosse nosso monitor, quando maior o Y, mais para baixo.

Agora imagine que nosso personagem andou até o ponto X(8).A formula para a gente posicionar o personagem no ponto correto da subida é:

( Nova posição de Y ) = (Tamanho total do morro ( no caso 12, o ponto incial Y ) ) - ( ponto atual do personagem, no caso X(8) )

 

Observe que essa formula só funciona com morros de pontos exatos, no caso, de 12 para 12, de 50 para 50 ou de 45 para 45.Agora, para escrever nosso programa, pasta posicionar nosso y descontando o chão e o tamanho do personagem.

Para fazer uma descida, basta inverter o sinal.

Veja o exemplo:

CÓDIGO...

#ifndef CHAO_H#define CHAO_H

// Arquivo: chao.h

Page 82: Biblioteca Allegro

// Data: 29/08/2007 

class Chao {

public:   Chao();   ~Chao();      // verifica se colide com as coordenadas passadas   bool colide( int, int, int );      void setx( int ); // seta a posicao x   void sety( int ); // seta a posicao y   void set_tamanho( int ); // seta o tamanho do objeto      int getx(); // retorna posicao x   int gety(); // retorno posicao y   int get_tamanho(); // retorna tamanho do objeto

private:

   int x;   int y;   int tamanho;    

};

#endif

FIM DE CÓDIGO...

 

CÓDIGO...

#include "chao.h"

// Arquivo: chao.cpp// Data: 29/08/2007 

Chao::Chao(){   setx(0);    sety(0);   set_tamanho(0);}

Chao::~Chao(){   setx(0);    sety(0);   set_tamanho(0);}

// verifica se colide com as coordenadas passadasbool Chao::colide( int x, int y, int tamanho ){   if (    this->x + this->tamanho > x &&    this->x < x + tamanho &&    this->y + this->tamanho > y &&    this->y < y + tamanho   )   {      return true;   }

Page 83: Biblioteca Allegro

      return false;}

// seta a posicao xvoid Chao::setx( int vx ){   this->x = vx;}

// seta a posicao yvoid Chao::sety( int vy ){   this->y = vy;}

// seta o tamanho do objetovoid Chao::set_tamanho( int vtamanho ){   this->tamanho = vtamanho;} 

// retorna posicao xint Chao::getx(){   return this->x;} 

// retorno posicao yint Chao::gety(){   return this->y;} 

// retorna tamanho do objetoint Chao::get_tamanho(){   return this->tamanho;} 

FIM DE CÓDIGO...

 

CÓDIGO...

#include <allegro.h>#include "chao.h"

// PROGRAMA que simula subida e pulo na subida.// Adriano Waltrick// Arquivo: main.cpp// Data: 06/09/2007 

// variáveis globaisint fps = 0;int fps_antigo = 0;int fps_speed = 0;

// prototipo do contador de framesvoid frame_rate();

// prototipo do contador de velocidadevoid incrementa_speed();

Page 84: Biblioteca Allegro

int main(){   allegro_init();   set_color_depth(16);   install_keyboard();   set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0);      install_timer();   install_int( frame_rate, 1000 );   install_int_ex( incrementa_speed, BPS_TO_TIMER(30) );       BITMAP *buffer = NULL;   buffer = create_bitmap(800,800);      // Inicio do programa atual       // Esse será nosso chão   const int limite_cenario = 300;      // posição do personagem   // nosso personagem terá 10 pixels   const int tamanho_personagem = 10;   int x = 10;   int y = limite_cenario - 100;      // novo y para quando estiver no morro   int novo_y = 0;      // valida se o personagem já está no pulo   bool pulou = false;      // Gravidade: quanto maior, mais rápida será a queda   const int gravidade = 2;         // Força do pulo   int forca = 0;   const int forca_maxima = 30;      // Variáveis do chão   const int total_chao1   = 50;   const int total_chao2   = 5;   const int tamanho_chao  = 30;   Chao *chao[total_chao1];   Chao *chao2[total_chao2];      // Criando as variaveis do morro de SUBIDA   const int morro1_altura  = 100;   const int morro1_largura = 100;      const int morro_x  = 200;   const int morro_x2 = 300;   const int morro_y  = limite_cenario - tamanho_chao;   const int morro_y2 = ( limite_cenario - tamanho_chao ) - morro1_altura;      // criando um morro de descida   const int morro2_altura  = 100;   const int morro2_largura = 100;      const int morro2_x  = 550;   const int morro2_x2 = 450;   const int morro2_y  = limite_cenario - tamanho_chao;   const int morro2_y2 = ( limite_cenario- tamanho_chao ) - morro2_altura;         // inicia o chao   int i = 0;   for ( i=0; i< total_chao1; i++ )   {      chao[ i ] = new Chao();      chao[i]->sety( limite_cenario - tamanho_chao );      chao[i]->setx( (i * tamanho_chao) + tamanho_chao );      chao[i]->set_tamanho( tamanho_chao );

Page 85: Biblioteca Allegro

   }      // criando o chão em cima do morro   for ( i=0; i< total_chao2; i++ )   {      chao2[ i ] = new Chao();      chao2[i]->sety( (limite_cenario - tamanho_chao) - morro2_altura );      chao2[i]->setx( (i * tamanho_chao) + tamanho_chao + 270 );      chao2[i]->set_tamanho( tamanho_chao );   }                  // Laço principal   while( !key[KEY_ESC] )   {            while ( ::fps_speed > 0 )      {                  clear( buffer );                  // Caso o personagem nao esteja pulando          // e apertou a tecla espaço         if ( key[KEY_SPACE] && pulou == false )         {            pulou = true;                        // força do pulo inicial            forca = forca_maxima;         }               // movimentação do personagem         if ( key[KEY_RIGHT] )         {            x+=5;         }                  // movimentação do personagem         if ( key[KEY_LEFT] )         {            x-=5;         }               textprintf_ex( buffer, font, 10, 10, makecol(255,0,0), -1, "FPS: %d", ::fps_antigo );         textprintf_ex( buffer, font, 10, 30, makecol(255,0,0), -1, "Forca: %d", forca );         textprintf_ex( buffer, font, 10, 40, makecol(255,0,0), -1, "x: %d y: %d", x, y );                                       // Isto vai fazer com que o personagem vá voltado para o chão         forca = forca - gravidade;         y = y - forca;               // verifica se está no morro - SUBIDA          if ( x+tamanho_personagem >= morro_x && x+tamanho_personagem <= morro_x2 )         {            novo_y = ( morro_y2 + (morro2_altura - tamanho_chao) ) - ( ( x-tamanho_personagem ) - morro_x);                        if ( y > novo_y )            {               y = novo_y;                              pulou = false;               forca = 0;            }         }   

Page 86: Biblioteca Allegro

         // verifica se está no morro - DESCIDA          if ( x >= morro2_x2 && x <= morro2_x )         {            novo_y = (morro2_y2) + ( (x-tamanho_personagem) - morro2_x2 );                        if ( y > novo_y )            {               y = novo_y;                              pulou = false;               forca = 0;            }         }   

         // imprime chao e verifica colisao         for ( i=0; i< total_chao1; i++ )         {                        rectfill( buffer, chao[i]->getx(), chao[i]->gety(), chao[i]->getx()+chao[i]->get_tamanho(), chao[i]->gety()+chao[i]->get_tamanho(), makecol(0,255,0) );                        if ( i < total_chao2 )            {               rectfill( buffer, chao2[i]->getx(), chao2[i]->gety(), chao2[i]->getx()+chao2[i]->get_tamanho(), chao2[i]->gety()+chao2[i]->get_tamanho(), makecol(0,255,0) );            }                        if ( chao[i]->colide(x, y, tamanho_personagem) == true )            {               y = chao[i]->gety() - tamanho_personagem;               pulou = false;                forca = 0;             }                     if ( i < total_chao2 )            {               if ( chao2[i]->colide(x, y, tamanho_personagem) == true )               {                     y = chao2[i]->gety() - tamanho_personagem;                     pulou = false;                     forca = 0;               }            }                     }               // Esse if vai barrar nosso personagem de passar pelo chão ( limite )         if (y > limite_cenario - tamanho_personagem)         {            y = limite_cenario - tamanho_personagem;                        pulou = false;            forca = 0;         }                     rectfill( buffer, x, y, x+tamanho_personagem, y+tamanho_personagem, makecol(255,255,0) );                  line(buffer, morro_x, morro_y, morro_x2, morro_y2, makecol(255, 0, 0 ));         line(buffer, morro2_x, morro2_y, morro2_x2, morro2_y2, makecol(255, 0, 0 ));                           ::fps_speed--;         ::fps++;      }            blit(buffer, screen, 0,0,0,0, SCREEN_W, SCREEN_H);      vsync();         }         destroy_bitmap( buffer );

Page 87: Biblioteca Allegro

               // destroi chão 1 e 2   for ( i=0; i< total_chao1; i++ )   {      delete chao[ i ];            if ( i < total_chao2 )      {         delete chao2[ i ];      }   }         allegro_exit();   return 0;}END_OF_MAIN();      void frame_rate(){   ::fps_antigo = ::fps;   ::fps = 0;}      void incrementa_speed(){   ::fps_speed++;}

FIM DE CÓDIGO...

Observe no código acima que na lógica aprensentada:

   novo_y = ( morro_y2 + (morro2_altura - tamanho_chao) ) - ( ( x-tamanho_personagem ) - morro_x);

Nós executamas o ( x-tamanho_personagem ) - morro_x para ter o X relativo a posição do morro.

E também executamos   morro_y2 + (morro2_altura - tamanho_chao)para ter o Y relativo ao Y do personagem.