BIblioteca SDL e o desenvolvimento de jogos 2D - Parte 1

Bom, semanas atrás lá na faculdade, aula de algoritmos, foi proposto um projeto para desenvolver um jogo 2D utilizando linguagem C e a biblioteca SDL. O propósito do jogo é acertar um navio no mar utilizando os disparos de um canhão posicionado em terra.
Faz uns dias que comecei a fazer o projeto e daqui pra frente vou descrever alguns tópicos na medida em que eu for também aprendendo a utilizar a biblioteca.
Nesta primeira parte vou dar um pequeno resumo sobre a biblioteca SDL, dicas de como instalar a mesma no compilador DevC++ e também introduzir algumas funções.

Simple DirectMedia Layer (SDL) é uma biblioteca multimídia e multiplataforma escrita em C (mas diretamente compatível com C++ e possui interfaces para outras linguagens de programação, como Ada, Eiffel, Java, Lua, ML, Perl, PHP, Pike, Python, e Ruby), que cria uma abstração em várias plataformas de gráficos, sons, e entrada de APIs, tornando possível ao programador escrever um jogo de computador ou outra aplicação multimedia já que ela pode rodar em GNU/Linux, Windows, Mac OS Classic, Mac OS X, BeOS, FreeBSD, OpenBSD, Solaris, QNX, IRIX, e muitas outras plataformas não oficialmente suportadas. Ela gerencia video, eventos, audio digital, CD-ROM, som, threads, processamento de objetos compartilhados, rede e tempo.
Fonte: Wikipedia
Instalação:
O download da SDL pode ser obtido no endereço www.libsdl.org. Têm versões runtime (apenas para rodar aplicativos que dependem da SDL) e versões desenvolver aplicativos.

O Dev-C++ (IDE e compilador C++ gratuito para Windows, e que irei utilizar, pode ser encontrado em www.bloodshed.net) possui pacotes prontos e configurados corretamente para desenvolvimento de aplicativos SDL. Para isso basta baixá-los e instalá-los através do próprio gerenciador de pacotes/atualizações do Dev-C++ (caso ocorra alguma dúvida na instalação da biblioteca SDL no Dev-C++ deixe um comentário).

Um detalhe importante fica por conta do arquivo SDL.dll. Ele deve constar em algum diretório do sistema (como C:\Windows\system) ou no próprio diretório do aplicativo. É uma dll necessária para que você consiga rodar o seu aplicativo.exe.

Início:

Todos os programas escritos utilizando a biblioteca SDL (em linguagem C) deve incluir no cabeçalho a declaração de inclusão da biblioteca, ou seja:

#include "SDL/SDL.h"

Declarada a biblioteca o próximo passo é inicializar a SDL com a seguinte função:

SDL_Init();

Essa função pode ser declarada fora da função principal [main()].

Existem alguns parâmetros que devem ser passados à função SDL_Init(), esses parâmetros servem para informar à função quais partes da SDL serão iniciadas, por exemplo:

SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO);

//Nos parâmetros dessa função, note que foi pedida a inicialização do vídeo e do áudio.

Existem outros parâmetros que podem ser passados à função SDL_Init que você pode conferir na documentação presente no site da própria biblioteca SDL (http://www.libsdl.org/).

Antes de descrever a rotina de carregar imagem primeiro é preciso que vocês entendam o conceito de superfície, ou, surface: ou seja, para falar em imagens é preciso falar também de surfaces, que são áreas da memória utilizadas para armazenar imagens. É fato que a maioria das tarefas desenvolvidas utilizando SDL se utilizam de superfícies. Cada imagem carregada no seu jogo (no caso do nosso: o canhão, navio, fundo e a bala) são surfaces.

Uma surface pode ser desenhada dentro, ou por cima, de outra. Basta a gente notar na tela de fundo, temos a imagem de fundo e por cima desta serão carregadas as outras imagens (canhão, bala e navio). Mas aí você deve tá pensando "e daí?!! eu quero carregar as imagens na minha tela!!", bom então é simples:

As surfaces da bala, canhão, navio e fundo são declaradas inicialmente:

SDL_Surface *screen = NULL;
SDL_Surface *fundo = NULL
SDL_Surface *bala = NULL;
SDL_Surface *canhão = NULL;
SDL_Surface *navio = NULL

O NULL pelo que andei pesquisando é um padrão da boa programação em C que é configurar os ponteiros para nulos quando eles não estiverem apontando para nenhum lugar.

Bom, declaradas as surfaces podemos agora implementar a função de carregar imagens:




Ressaltando que a função usada: SDL_LoadBMP serve apenas para carregar imagens do tipo bitmap. Para carregar imagens em outros formatos utilize a função IMG_Load da biblioteca SDL_Image.h (o procedimento para instalar é o mesmo da biblioteca SDL padrão).

Bom, como a imagem carregada está em bitmap ela é do tipo 24-bit, mas a tela do nosso jogo vai ter 32-bit (esses números são relacionados a quantidade de cores) não é recomendado utilizar a imagem ainda, porque a SDL vai até transformar a imagem em 32-bit, mas isso vai acontecer durante a execução do jogo, o que vai tornar o jogo lento.

Então, continuando nosso código:
  1. //Se nada der errado no carregamento da imagem
  2. if( imagemcarregada != NULL )
  3. { //Cria a imagem otimizada
  4. imagemotimizada = SDL_DisplayFormat( imagemcarregada );
  5. //Libera a antiga "imagem" da superfície
  6. SDL_FreeSurface( imagemcarregada );
  7. }
O que essa parte do código faz dá pra entender até lendo o mesmo: se ocorrer tudo bem no carregamento da imagem a função SDL_DisplayFormat() é chamada e cria uma nova versão da "imagemcarregada" no mesmo formato da tela (32-bit). A razão pra se fazer isso é porque quando você tenta usar uma imagem em um diferente formato, a SDL converte a surface para o mesmo, ficando ambas então com o mesmo formato.

  1. //Faz a função retornar a imagem otimizada
  2. return imagemotimizada;
  3. }
Agora nós temos que ter uma função que posiciona nossa imagem na tela:


Essa função recebe as coordenadas que você quer colocar sua surface (imagem).

Primeiro nos pegamos as posições (x, y) e colocamos ela dentro da SDL_Rect. É preciso fazer isso porque a função de "pintar" a imagem só aceita posições dentro da SDL_Rect.

Na documentação da SDL é citado que a SDL_Rect é um tipo de dado que representa um retângulo. Tendo quatro membros representados pelas posições X e Y, a largura e a altura de um retâgulo.

Descrevendo um pouco a função SDL_BlitSurface(), temos que o primeiro argumento source é a surface que você está usando. Quanto ao segundo argumento NULL apenas saiba que ele é nulo por enquanto. O terceiro argumento é a superfície onde nós iremos "pintar" a imagem. O quarto argumento recebe as posições de destino da source onde ela vai ser aplicada.

A função principal:


Aqui nós começamos a main().

Quando se está utilizando a SDL, sua função principal sempre deve conter como parâmetros: int main( int argc, char* args[] )

Usando simplesmente int main() | void main() ou qualquer coisa parecida seu programa não irá funcionar.



Agora é hora de fazer nossa screen ser apontada com as propriedades da tela para então "pintar" as imagens nela.

Bom, os três primeiros argumentos da função SDL_SetVideoMode são simples (largura, altura e cores), o quarto argumento cria o fundo (neste caso) na memória do sistema.

Resta adicionar ao conteúdo das variáveis (no caso aqui o fundo) a função de carregar as imagens:
//Carregar as imagens
fundo = carregar_imagem( "fundo.bmp" );
A imagem de fundo foi carregada com a função que nós criamos anteriormente.
Aplicando a imagem de fundo na tela do programa:
//Aplica a imagem de fundo na variável screen que definimos no início
apply_surface( 0, 0, fundo, screen );
Onde os dois primeiros parâmetros significam a posição da imagem.

Para que a imagem carregada apareça é preciso atualizar a tela:
if( SDL_Flip( screen ) == -1 )
{
return 1;
}

Chegando ao fim do programa temos que liberar as superfícies e adicionar o evento saída
//Libera as superfícies (no caso aqui do fundo)
SDL_FreeSurface( fundo );
//Sai da SDL
SDL_Quit();
return 0;
}
Não é preciso liberar a surface screen porque a SDL se encarrega de fazer isso.

Pronto agora resta compilar seu projeto e ver sua primeira aplicação SDL. Com o decorrer do meu projeto vou postando aqui no blog as experiências. Quem tiver conhecimento nessa biblioteca e quiser ajudar é só deixar comentário aqui na postagem.

Até a próxima. []'s!

Felipe A. Lopes

Felipe Alencar é doutorando em Ciência da Computação na UFPE, professor, desenvolvedor e acredita que só não virou jogador de futebol, surfista ou músico profissional por falta de tempo e talento.

Um comentário:

  1. Felipe, extremamente simples, direto ao ponto, e explicativo. parabéns pelo artigo.

    ResponderExcluir