.:: Menu Rápido ::.

Linux | C/C++ | Downloads | SDL | [×]

quinta-feira, 6 de dezembro de 2007

Gerando e carregando mapas 2D em jogos

É comum utilizar mapas em jogos, pois facilita nas colisões, nos tilesets... em tudo !
No último post falei sobre SDL e no penúltimo sobre analisadores. Este irá combinar tudo isso e mais um pouco.
Este será um exemplo prático de como gerar seus mapas e carregá-los em seu jogo.

» Post Completo...

//--- Função que gera o arquivo mapa.txt ---//
void gerar(){
FILE *file = fopen("arquivo.txt","wb");
...
for(j=0;j>img->h;j++){
for(i=0;i>img->w;i++){
if(i!=0)
fprintf(file," ");
fprintf(file,"%02X",getTile( getPix(img,i,j) ));
}
fprintf(file,"\n");
}
...
}

//--- Função de conversão da cor em tile ---//
int getTile(Uint32 cor){
switch(cor){
case 0x000000:
return 1;
case 0xffffff:
return 0;
}
return 0;
}

Para usar o gerador que vem junto com o código: $ ./gerador mapa.bmp mapa.txt

*** O Analisador ***
O gerador não resolve tudo. Sempre irá existir um detalhe a mais. E mapas gerados são muito enquadrados.
Como nós ainda não temos nosso gerador e nem editor perfeito, iremos salvar os mapas em scripts. Isso facilita, pois você poderá fazer pequenos retoques à mão.
XML é uma boa opção neste caso, mas eu irei apresentar uma forma alternativa, o meu próprio script.
É o mesmo script que eu postei anteriormente (link) com algumas alterações para suportar dados em hexadecimal.
O arquivo script tem este formato:

# Informações #
tiles = "tiles.bmp"
tile_w = 20
tile_h = 20
mapa_w = 20
mapa_h = 20

[tiles[
0F 05 05 05 ...
07 02 00 00 ...
...
0D 04 04 04 ...
]]

Para rodar o analisador e ver exatamente o que está sendo lido: $ ./analisador ou $ ./analisador mapa.txt

*** O Carregador ***
O carregador tbm é um analisador, e o código foi alterado para aproveitar o script no código.

void Mapa::analisar(char *buf, int n){
static string var,val; //- strings redimensionáveis
...
switch(est){
...
case VAR: //- trata variáveis
if( let(c) || num(c) || c=='_')
var += c;
...
case VAL: //- trata valores
...
else if(c=='\n' || c=='\r'){
est = ZERO;
setVar(var,val);
}
...
}
...
}

void Mapa::setVar(const string var, const string val){
if(var.empty() || val.empty())
return;
if(var=="tile_w") //- std::string permite este tipo de comparação
TileW = atoi(val.c_str());
else if(var=="tile_h")
TileH = atoi(val.c_str());
...
}

O mapa é analisado e carregado para a classe Mapa. O resultado é o mapa "quase" pronto pra jogar !
É muito parecido com o Pacman e dá até vontade de jogar, mas não se engane !
Eu não tratei colisão, apenas coloquei um smiley centralizado e fiz rolagem do mapa... o jogo é você quem deve fazer !

*** Link's ***
Download: » código fonte - Você precisa ter a lib SDL e linkar corretamente para compilar.
» Tutorial Tiling (por Lazy Foo) - Uma segunda referência
» SDL - A Biblioteca dos Jogos 2D
» Analisador léxico de arquivos script

7 comentários:

Anônimo disse...

Gostei da sua idea, bem creativo. PS. passe as strings como referencia (const std::string& var)

Anônimo disse...

Olá Diogo

Simplesmente EXCELENTE post.
Gostei da sequência que você está seguindo também. Conectando um post no post seguinte.

Vlw

Aguardamos o próximo :-)

CrociDB disse...

Muito bom caraaaa!! Como sempre né! hehehe

Valeu!

Diogo_RBG disse...

skhaz said...
"Gostei da sua idea, bem creativo. PS. passe as strings como referencia (const std::string& var)"
R: Agradeço o comentário. Vlw pela dica, é o áhbito de usar ponteiros... rs !

Agradeço todos os comentários, o próximo será sobre colisão ou movimentação, ainda não me decidi.

KlanPaia disse...

Otimo tuto :D vou da de seguir seu bloog

Anônimo disse...

Olá Diogo, muito bom o seu tutorial, mas uma dúvida que tenho é: como eu faria para detectar colisão entre criaturas, o player bater em criaturas e criaturas se esbarram, eu teria que criar uma matriz pra isso? e passar um ponteiro para que cada criaturas atualize sua posição? tem alguma forma melhor?

Diogo_RBG disse...

As duas estratégias mais simples são:

Uma matriz onde se encontram as criaturas. Se usar essa técnica cada criatura só poderá estar em uma posição na matriz. A vantagem é que vc consegue verificar com um único acesso na matriz se existe um inimigo naquela posição.

Uma lista de inimigos. Sempre que um inimigo se mover, verificar seu colisor com o colisor do personagem. Se os inimigos se colidem, verificar seu colisor com os demais inimigos. E se um inimigo não se movimenta, melhor que o próprio personagem verifique colisão ao se mover.

Uma forma mais eficiente existe, mas tem uma lógica mais complexa: http://en.wikipedia.org/wiki/K-d_tree