Conheça a iniciativa da Biblioteca dos Desenvolvedores  
Índice da Biblioteca  
Área dos Usuários  
Fórum de Discussão  
Forúm
 
  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.

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 globais
int fps = 0;
int fps_antigo = 0;
int fps_speed = 0;

// prototipo do contador de frames
void 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);
   
   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;
}
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...
#include <allegro.h>
#include <iostream>
#include "star.h"

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


// construtor
Star::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);
}
   
// destrutor
Star::~Star()
{
   this->velocidade = 0;
   this->x = 0;
   this->y = 0;
   this->tamanho = 0;
}
   
// inicia o objeto
void 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 tela
void Star::verificar()
{
   if ( this->x > 0 )
   {
   
      this->x -= this->velocidade;
   }
   else
   {
      this->iniciar( this->id );
   }
}


FIM DE CÓDIGO...


CÓDIGO...
#include <allegro.h>
#include <iostream>
#include <ctime>
#include "star.h"

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


// Exemplo de scrolling falso

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

// prototipo do contador de frames
void 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);
   
   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 );
         
         
         ::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.

 

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 globais
int fps = 0;
int fps_antigo = 0;
int fps_speed = 0;

// prototipo do contador de frames
void 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);
   
   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 );
   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 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 globais
int fps = 0;
int fps_antigo = 0;
int fps_speed = 0;

// prototipo do contador de frames
void 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);
   
   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
   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;
   ::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 globais
int fps = 0;
int fps_antigo = 0;
int fps_speed = 0;

// prototipo do contador de frames
void 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);
   
   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);
   
   // 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
         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++;
}


FIM DE CÓDIGO...

Teste o código acima.. modifique o tamanho da tela, dos tiles ou do cenário para ver os efeitos.
Preste atenção na configuração da rolagem, que no exemplo está bem rápida.

Download do exemplo 1 - Clique Aqui

Download do exemplo 2 - Clique Aqui

Download do exemplo 3 - Clique Aqui

Download do exemplo 4 - Clique Aqui

Download do exemplo 5 - Clique Aqui

 


Contribuidor
Adriano Waltrick
23/08/2007


 

« Anterior

 

Próximo »

 
 

01/06/2007 (C) Copyright. Todos os Direitos Reservados. Leia a política de privacidade do portal.
É proibida a cópia de conteúdo deste site de acordo com a Lei Brasileira de Direitos Autorais.