| |
Cenário em Arquivos
Cenário em Arquivos
Esse artigo tem como objetivo mostrar como carregar várias fases de um jogo através de arquivos textos. Com certeza existem muitas outras maneiras de se fazer isso, mas aqui irei exemplificar uma forma simples e fácil de entender.
Com base nessas informações você poderá montar a sua própria maneira de organizar as fases do seu jogo.
Arquivo de Fases |
Fase 1 |
Fase 2 |
Fase 3 |
####################
#..................#
#.........########.#
###########........#
#..................#
#.........##########
#.#########........#
#..................#
#.........########.#
###########........#
#..................#
#.........##########
#.#########........#
#..................#
#################### |
####################
#------------------#
#-###-###--###-###-#
#------------------#
#-###-###--###-###-#
#------------------#
#-###-###--###-###-#
#------------------#
#-###-###--###-###-#
#------------------#
#-###-###--###-###-#
#------------------#
#-###-###--###-###-#
#------------------#
#################### |
####################
#..................#
#.#.######.#####.#.#
#.#..............#.#
#.#.###.####.###.#.#
#...#..........#...#
#.#.#.########.#.#.#
#..................#
#.#.#.########.#.#.#
#...#..........#...#
#.#.###.####.###.#.#
#.#..............#.#
#.#.#####.######.#.#
#..................#
#################### |
Abaixo vamos apresentar as texturas utilizadas e o resultado final do cenário montado.
Texturas |
Parede |
Grama |
Chão |

|

|

|
Representação |
Representação |
Representação |
# (Sustenido) |
- (Traço) |
. (Ponto) |
Resultado Final |
Fase 1 |
Fase 2 |

|

|
Fase 3 |
|
A parte mais importante do código é a função CarregaMatriz() que vamos explicar logo abaixo.
O restante do código você, obrigatoriamente, já deve estar familiarizado se estiver acompanhando os tutoriais anteriores.
Adotamos o tamanho da tela como sendo 640x480 e os tiles terão 32pixels de altura e largura.
Dessa forma teremos 20 tiles na horizontal e 15 tiles na vertical.
Se houver dúvida faça: 640/32 = 20 tiles na horizontal e 480/32 = 15 tiles na vertical.
No código de exemplo usei uma matriz tridimensional onde o primeiro string identifica a fase os outros 2 identificam a quantidade de tiles na horizontal e na vertical.
A ilustração abaixo se trata de uma visão da matriz referente à primeira fase. Onde a cor azul identifica a fase, a cor amarela identifica os tiles na horizontal e a cor laranja identifica os tiles na vertical. Observe bem o gráfico abaixo para poder entender a forma de ler o arquivo mais adiante.
|
|
C0 |
C1 |
C2 |
C3 |
C4 |
C5 |
C6 |
C7 |
C8 |
C9 |
C10 |
C11 |
C12 |
C13 |
C14 |
C15 |
C16 |
C17 |
C18 |
C19 |
1 |
L0 |
# |
# |
# |
# |
# |
# |
# |
# |
# |
# |
# |
# |
# |
# |
# |
# |
# |
# |
# |
# |
L1 |
# |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
# |
L2 |
# |
. |
. |
. |
. |
. |
. |
. |
. |
. |
# |
# |
# |
# |
# |
# |
# |
# |
. |
# |
L3 |
# |
# |
# |
# |
# |
# |
# |
# |
# |
# |
# |
. |
. |
. |
. |
. |
. |
. |
. |
# |
L4 |
# |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
# |
L5 |
# |
. |
. |
. |
. |
. |
. |
. |
. |
. |
# |
# |
# |
# |
# |
# |
# |
# |
# |
# |
L6 |
# |
. |
# |
# |
# |
# |
# |
# |
# |
# |
# |
. |
. |
. |
. |
. |
. |
. |
. |
# |
L7 |
# |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
# |
L8 |
# |
. |
. |
. |
. |
. |
. |
. |
. |
. |
# |
# |
# |
# |
# |
# |
# |
# |
. |
# |
L9 |
# |
# |
# |
# |
# |
# |
# |
# |
# |
# |
# |
. |
. |
. |
. |
. |
. |
. |
. |
# |
L10 |
# |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
# |
L11 |
# |
. |
. |
. |
. |
. |
. |
. |
. |
. |
# |
# |
# |
# |
# |
# |
# |
# |
# |
# |
L12 |
# |
. |
# |
# |
# |
# |
# |
# |
# |
# |
# |
. |
. |
. |
. |
. |
. |
. |
. |
# |
L13 |
# |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
. |
# |
L14 |
# |
# |
# |
# |
# |
# |
# |
# |
# |
# |
# |
# |
# |
# |
# |
# |
# |
# |
# |
# |
Note que você sabe que obrigatoriamente a quantidade de tiles na largura tem que ser 20, então, levando em conta essa informação vamos criar uma variável do tipo char chamada “sLinha” com 20 posições de caracteres.
char sLinha[20];
Apenas para entendimento, se agente atribuir o texto “BDJogos” para a string acima o resultado vai ser o seguinte:
|
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
sLinha |
B |
D |
J |
o |
g |
o |
s |
\n |
|
|
|
|
|
|
|
|
|
|
|
|
Até aqui você obrigatoriamente deve entender que se pegarmos a posição sLinha[0] o valor que ela deve retornar é B, sLinha[1] = D ... E assim sucessivamente.
É dessa forma que vamos preencher a matriz com os caracteres do arquivo texto.
Como nossa matriz é tridimensional e, nesse caso, iremos carregar todas as fases de uma vez só na matriz para posteriormente montar o cenário, devemos ter 3 sequências de repetição FOR.
Uma para a fase, uma para os tiles na horizontal e uma para os tiles na vertical.
Entre uma fase e outra iremos carregar o arquivo de fase para leitura e entre uma linha e outra iremos preencher a matriz.
O modo que a função preenche a matriz segue da esquerda para a direita e de cima para baixo.
Posiciona no Y, e varre o X atribuindo os valores na matriz[iFase][x][y]. Execute o código de exemplo para entender melhor.
Após entender a função CarregaMatriz() a função para desenhar as texturas na tela é fácil.
Precisamos apenas interpretar os caracteres nos arquivos e substituí-los por um objeto ou textura.
Para o código abaixo funcionar dentro da pasta do seu projeto você deve ter uma pasta chamada “fases” e outra chamada “tiles”. Dentro de cada uma você coloca seus respectivos arquivos de fases e imagens que vão utilizar na montagem do cenário.
Segue a tela abaixo para exemplificar a disposição dos arquivos dentro da pasta do seu projeto.

Chega de teoria. Abaixo, segue o código para carregar 3 tipos de cenários diferentes.
CÓDIGO...
001: 002: 003: 004: 005: 006: 007: 008: 009: 010:
011: 012: 013: 014: 015: 016: 017: 018: 019: 020:
021: 022: 023: 024: 025: 026: 027: 028: 029: 030:
031: 032: 033: 034: 035: 036: 037: 038: 039: 040:
041: 042: 043: 044: 045: 046: 047: 048: 049: 050:
051: 052: 053: 054: 055: 056: 057: 058: 059: 060:
061: 062: 063: 064: 065: 066: 067: 068: 069: 070:
071: 072: 073: 074: 075: 076: 077: 078: 079: 080:
081: 082: 083: 084: 085: 086: 087: 088: 089: 090:
091: 092: 093: 094: 095: 096: 097: 098: 099: 100:
101: 102: 103: 104: 105: 106: 107: 108: 109: 110:
111: 112: 113: 114: 115: 116: 117: 118: 119: 120:
121: 122: 123: 124: 125: 126: 127: 128: 129: 130:
131: 132: 133: 134: 135: 136: 137: 138: 139: 140:
141: 142: 143: 144: 145: 146: 147: 148: 149: 150:
151: 152: 153: 154: 155: 156: 157: 158: 159: 160:
161: 162: 163: 164: 165: 166: 167: 168: 169: 170:
171: 172: 173: 174: 175: 176: 177: 178: 179: 180:
181: 182: 183: 184: 185: 186: 187: 188: 189: 190:
191: 192: 193: 194: 195: 196: 197: 198: 199: 200:
201: 202: 203: 204: 205: 206: 207: 208: 209: 210:
211: 212: 213: 214: 215: 216: 217: 218: 219: 220:
221: 222: 223: 224: 225: 226: 227: 228:
|
|
#include <allegro.h>
#include <iostream>
#include <fstream>
using namespace std;
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();
void Teclado();
void CarregaMatriz();
void CarregaTexturas();
void DesenhaCenario(BITMAP *, int );
//A variável abaixo guarda o local onde estão as fases em arquivo texto.
const char *ArqFases[] = {"fases/fase1.txt", "fases/fase2.txt", "fases/fase3.txt"};
const int iQtdeFases = 3; // Jogo com 3 fases
const int iTamTilesX = 32; // Largura do Tile
const int iTamTilesY = 32; // Altura do Tile
const int iTilesX = 20; // Qtde de Tiles na horizontal - 32*20 = 640;
const int iTilesY = 15; // Qtde de Tiles na vertical - 32*15 = 480;
/*
Com base nas variáveis acima temos o tamanho da tela 640x480, conforme abaixo:
(iTamTilesX * iTilesX) = SCREEN_W;
(iTamTilesY * iTilesY) = SCREEN_H;
(32 * 20) = 640;
(32 * 15) = 480;
*/
int iFaseAtual = 0; // Guarda o index da fase atual.
//Variável do tipo BITMAP responsável por guardar as texturas
BITMAP *Textura[3] = {NULL, NULL, NULL};
/*
Variáveis Mapa tridimensional sendo o primeiro ecrãn a Fase e os
outros dois correspondem a quantidade de Tiles na horizontal e vertical.
*/
int Mapa[iQtdeFases][iTilesX][iTilesY];
int main()
{
allegro_init();
install_keyboard();
set_color_depth(32);
set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0);
set_window_title( "Fases em arquivos textos" );
install_timer();
install_int( frame_rate, 1000 );
install_int_ex( incrementa_speed, BPS_TO_TIMER(60) );
//Habilita Acentuação
set_uformat(U_ASCII);
BITMAP *Buffer = NULL;
Buffer = create_bitmap(SCREEN_W, SCREEN_H);
CarregaMatriz();
CarregaTexturas();
while (!key[KEY_ESC])
{
while ( ::fps_speed > 0 )
{
clear_bitmap(Buffer);
DesenhaCenario(Buffer, iFaseAtual);
Teclado();
textprintf_ex( Buffer, font, 8, 10, makecol(255,255,0), -1, "FPS: %d", ::fps_antigo );
textprintf_centre_ex(Buffer, font, SCREEN_W/2, (SCREEN_H/2)-10, makecol(255, 255, 0),
-1, "FASE: %d", iFaseAtual+1);
textprintf_centre_ex(Buffer, font, SCREEN_W/2, SCREEN_H/2, makecol(255, 255, 0), -1,
"Pressione espaço para mudar de fase.");
::fps_speed--;
::fps++;
}
blit (Buffer,screen, 0, 0, 0, 0,SCREEN_W, SCREEN_H);
vsync();
}
//Desaloca as imagens da memória
destroy_bitmap(Buffer);
destroy_bitmap(Textura[0]);
destroy_bitmap(Textura[1]);
destroy_bitmap(Textura[2]);
allegro_exit();
return 0;
}
/*
A função abaixo é a mais importante com relação ao arquivo de fases. Ela serve
para carregar os valores que estão dentro do arquivo de fase para a matriz.
Com base na matriz iremos montar o cenário de cada fase e posicionar suas
respectivas texturas ou objetos, dependendo de como você modificar o código.
*/
void CarregaMatriz()
{
int x = 0;
int y = 0;
int iFase = 0;
char sLinha[ iTilesX ]; // Qtde de caracteres por linha
//O FOR abaixo é para preencher todas as fases.
for (iFase=0; iFase<iQtdeFases; iFase++)
{
// Abre o arquivo de fases para leitura com base no iFase e o Array de fases
ifstream oLendoFase(ArqFases[iFase], ios::in );
//Preenche a matriz na horizontal e vertical
for (y=0; y<iTilesY; y++)
{
/*
Atribui na variável sLinha a linha atual do arquivo texto.
Toda vez que essa rotina for chamada ele pula para a proxima linha e
joga o valor para sLinha, até finalizar as 15 linhas.
*/
oLendoFase >> sLinha;
/*
Já com o sLinha preenchido, agora, o FOR abaixo varre todas as
posições dessa linha e acrescenta na linha da matriz. Após terminar
de varrer o sLinha sai do for e espera próxima linha, se houver.
*/
for (x=0; x<iTilesX; x++)
{
Mapa[iFase][x][y] = sLinha[x];
}
}
oLendoFase.close();
}
}
/*
A função abaixo é responsável por declarar e definir as imagens que iremos
utilizar no Array Textura. Essa imagens devem estar dentro da pasta imagens no
nosso projeto,caso contrário o programa irá apresentar a mensagem de erro fatal.
*/
void CarregaTexturas()
{
// Define o índice 0 da textura como sendo parede
Textura[0] = create_bitmap(iTamTilesX, iTamTilesY);
Textura[0] = load_bitmap("tiles/parede.bmp", NULL);
// Define o índice 1 da textura como sendo grama
Textura[1] = create_bitmap(iTamTilesX, iTamTilesY);
Textura[1] = load_bitmap("tiles/grama.bmp",NULL);
// Define o índice 2 da textura como sendo chão
Textura[2] = create_bitmap(iTamTilesX, iTamTilesY);
Textura[2] = load_bitmap("tiles/chao.bmp",NULL);
}
/*
Essa função é responsável por desenhar as texturas do cenário. O cenário a ser
carregado é informado através de um argumento. Essa função usa como base a matriz
carregada anteriormente para desenhar as texturas.
*/
void DesenhaCenario(BITMAP *Cenario, int iFase)
{
int x = 0;
int y = 0;
for (y=0; y<iTilesY; y++)
{
for (x=0; x<iTilesX; x++)
{
if ((char)Mapa[iFase][x][y] == '#') // Parede
draw_sprite(Cenario,Textura[0], x*iTamTilesX, y*iTamTilesY);
else
if ((char)Mapa[iFase][x][y] == '-') // Grama
draw_sprite(Cenario,Textura[1], x*iTamTilesX, y*iTamTilesY);
else
if ((char)Mapa[iFase][x][y] == '.') // Chão
draw_sprite(Cenario,Textura[2], x*iTamTilesX, y*iTamTilesY);
}
}
}
/*
A função abaixo é responsável por controlar as entradas do teclado, qualquer
tecla pressionada durante a execução do jogo.
*/
void Teclado()
{
/*
Quando declaramos uma variável como sendo static quer dizer que mesmo saindo
da função ela não vai perder o valor dela. Dessa forma, utilizamos a variável
abaixo para controlar o tempo de uma tecla a outra.
*/
static int Buffer_Teclado = 0;
//Tudo que estiver dentro o IF abaixo será executado a cada 30 ciclos de CPU
if (Buffer_Teclado == 0)
{
// Muda o Cenário caso aperte a tecla espaço
if (key[KEY_SPACE])
{
if (iFaseAtual < (iQtdeFases-1))
iFaseAtual++;
else
iFaseAtual = 0;
Buffer_Teclado = 30;
}
} else Buffer_Teclado--;
}
void frame_rate()
{
::fps_antigo = ::fps;
::fps = 0;
}
void incrementa_speed()
{
::fps_speed++;
}
END_OF_MAIN();
|
FIM DE CÓDIGO...
Ao executar o código acima você irá perceber que ele mostra apenas um cenário montado. Aperte a barra de espaço para mostrar os demais cenários.
De qualquer forma, você já consegue fazer um jogo de puzzle com vários cenários carregados a partir de um arquivo de dados.
Mais adiante após estudar
mais sobre organização de cenários dentro de arquivos, estarei disponibilizando mais materiais sobre o assunto.
Para melhor entendimento do código acima compile, execute e altere o código como bem entender. Quaisquer dúvidas estaremos no fórum para tentar ajudar.
Abraço.
Contribuidor
Bruno A. Rovela
10/06/2008
Voltar
|
|