| |
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.
Contribuidor
Adriano Waltrick
23/08/2007
|
|