GameSpaceLib & Sprites: personagens 2D com C++
Você conhece a GameSpaceLib? Não? Pois descubra aqui como essa biblioteca pode tornar bem mais fácil sua vida criando jogos em C++! Este artigo é de autoria de André Santee e apresenta os fundamentos para manipular sprites 2D usando esta biblioteca.

Parte I: Instalando o necessário
Passo 1 - Verificando o que é necessário
Este tutorial assume que você possua algum conhecimento em C++ e lógica da programação. Para seguir este tutorial é necessário que você possua alguma versão do Visual C++. Você pode baixar uma versão gratuita do compilador em: http://www.microsoft.com/express/. Este é um excelente compilador com todas as funcionalidades importantes que a versão paga possui.
Para este tutorial eu utilizei a versão Express 9.0 (2008) do Visual C++, mas você pode utilizar qualquer versão superior à 5.0.
Também é necessário que se tenha o DirectX 9.0c ou superior instalado.
Passo 2 - baixando a GameSpaceLib
Uma vez que você possui o Visual C++ instalado, podemos começar. Antes de tudo precisamos de uma biblioteca ou engine que faça o trabalho duro de acesso aos dispositivos de vídeo, áudio e entrada, para que o programador possa preocupar-se apenas com a programação da lógica do jogo ou aplicativo diverso. Neste tutorial vamos utilizar a ferramenta brasileira GameSpaceLib.
O que é GameSpaceLib?
GameSpace é uma biblioteca desenvolvida em C++ para o controle de dispositivos de entrada e saída: vídeo (2D), áudio, teclado, mouse e joysticks. A GameSpaceLib é gratuita e possui o código fonte-aberto.
Quais são as vantagens da GameSpaceLib?
Dentre outras bibliotecas multimídia como SDL e Allegro, a vantagem da GameSpaceLib é que ele possui uma interface muito mais fácil e intuitiva para programação.
Foi programada para acessar diretamente as funções da API, o que o torna veloz, pois utiliza todos os recursos da placa de vídeo e de forma direta, sem passar por outros motores ou frameworks para fazer a mediação.
Para baixar a GameSpaceLib acesse:
http://www.asantee.net/gamespace/ e escolha a versão 1.3.0 ou superior. Após baixado o arquivo, instale o SDK em seu computador.
Passo 3 - Criando o projeto
Utilizando a GameSpaceLib com o Microsoft Visual C++ Express:
1. Abra o Visual C++ Express, clique em File->New->Project.
2. No campo “Project types:” selecione Win32, e no lado esquerdo, no campo “Templates:” selecione “Win32 Console Application”.
3. No campo “Name:” digite o nome do projeto desejado (Ex.: Game2D).
4. Em “Location:” selecione o diretório onde deseja armazenar os dados do projeto.
5. Clique em OK.
Uma nova janela irá abrir. Clique em “Next”, em seguida verifique se a opção “Windows Application” está selecionada e marque o campo “Empty project”. Clique em “Finish”.
Pronto. Seu projeto está criado. Agora você precisa adicionar um arquivo .CPP à ele para entrar com o códito do tutorial. Clique em “Project->Add new item…”. No campo esquerdo “Templates:” selecione “C++ File (.cpp)”, escolha um nome para ele no campo “Name:” (Ex.: main.cpp) e clique em “Add”.
Uma tela em branco surgirá na área principal de seu compilador. É onde você digitará o código do arquivo .CPP principal.
Preparando o projeto para rodar com a GameSpaceLib:
Instale o SDK da GameSpaceLib.
Clique em “Tools->Options”. Amplie o campo “Projects and Solutions” (clique no +) e clique em “VC++ Directories”.
Ao seu lado esquerdo, clique no menu abaixo de “Show directories for:” e selecione “Include files”. Uma lista de diretórios surgirá.
Clique no botão que cria uma nova pasta, logo acima da lista de diretórios, e escolha a pasta “include” do local onde a GameSpaceLib foi instalada (Ex.: C:\Arquivos de programas\GameSpaceLib SDK\Include).
Volte ao menu “Show directories for:” e selecione “Library files”. Repita o mesmo processo anterior mas selecionando a pasta msvc\lib (Ex.: C:\Arquivos de programas\GameSpaceLib SDK\msvc\lib).
Clique em OK.
Abra a pasta onde você instalou o SDK. Então selecione e copie para a pasta de seu projeto (por exemplo: “c:\Jogo2D\Jogo2D\”) os seguintes arquivos:
-GameSpace.dll (contido no diretório \msvc\bin)
-audiere.dll (contido no diretório \msvc\bin)
Utilize a seguinte linha de código no início do arquivo .cpp principal para incluir o arquivo .lib ao projeto:
#pragma comment (lib, “GameSpace.lib”)
Pronto, agora é só começar a programar.
Utilizando a GameSpaceLib com o Dev-C++ 5:
O SDK da GameSpaceLib acompanha um arquivo DevPak que automaticamente instala todos os arquivos necessários para a compilação. O arquivo encontra-se dentro do diretório \mingw\ na pasta onde o SDK foi instalado. Para instalar o SDK ao Dev-C++ 5 basta executar o arquivo .DevPak e seguir as instruções em tela que surgirão em seguida.
Parte II - Desenhando um sprite simples
#pragma comment (lib, "GameSpace.lib")
#include
#include
INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )
{
GAMESPACE_VIDEO_HANDLER gsVideo;
GAMESPACE_INPUT_HANDLER gsInput;
GAMESPACE_AUDIO_HANDLER gsAudio;
GS_SPRITE gsSprite;
if (gsVideo.StartApplication(640, 480, "GameSpace", false))
{
gsInput.RegisterInputDevice(NULL);
gsAudio.RegisterAudioDevice(NULL);
gsVideo.HideCursor(true);
gsSprite.LoadSprite(&gsVideo, "character.bmp", GS_ARGB(255,0,255,0));
while (gsVideo.ManageLoop())
{
gsInput.UpdateInputData();
gsSprite.DrawSprite(GS_VECTOR2(100, 100));
}
}
return 0;
}
Vamos analisar o código:
#pragma comment (lib, "GameSpace.lib") #include #include
A primeira linha de código liga o arquivo GameSpace.lib ao projeto. Ele é necessário para que o compilador possa puxar as funções de GameSpace.dll.
Em seguida incluímos os arquivos gamespace.h e windows.h ao código. Eles são necessários para que tudo funcione.
INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )
{
Esta é a função principal do programa. É aqui que todo o código começa a ser executado. É equivalente à função main() do modo console.
GAMESPACE_VIDEO_HANDLER gsVideo;
GAMESPACE_INPUT_HANDLER gsInput;
GAMESPACE_AUDIO_HANDLER gsAudio;
Nesta parte do código declaramos todos os objetos da GameSpaceLib que serão usados. De vídeo, entrada e áudio respectivamente. Neste tutorial ainda não usaremos as funções de áudio da GameSpaceLib mas vamos declarar o objeto mesmo assim.
GS_SPRITE gsSprite;
Este é o objeto utilizado para carregar imagens de arquivos BMP, JPEG, PNG ou TGA e desenhá-las na tela. Esta forma de desenhar bitmaps na tela é denominada SPRITE. Num jogo, cada tipo de objeto (personagens, elementos do cenário, itens, etc.) que estiver em arquivos de imagem diferente vai precisar de uma declaração de um objeto GS_SPRITE distinta.
Posteriormente vamos utilizar um método (função membro) deste objeto para carregar o arquivo.
if (gsVideo.StartApplication(640, 480, "GameSpace", false))
{
O método GAMESPACE_VIDEO_HANDLER::StartApplication cria a janela ou especifica o modo tela cheia e prepara o fundo para que possamos desenhar os sprites nela.
No primeiro e segundo parâmetros especificamos a largura e altura, em pixels, da janela. Neste caso ela será de 640×480 pixels. O terceiro parâmetro define um título para a janela. Vamos chamá-la de “GameSpace”.
No último parâmetro definimos se queremos nossa aplicação no modo janela (true) ou se a queremos em tela-cheia (false). Neste caso eu escolhi o modo de tela cheia (fullscreen) que é mais comum em jogos.
Este método pode retornar true caso corra tudo bem, ou false caso haja um falha durante sua execução. Se ele tiver sucesso, prosseguimos com as demais instruções:
gsInput.RegisterInputDevice(NULL);
gsAudio.RegisterAudioDevice(NULL);
Os métodos GAMESPACE_INPUT_HANDLER::RegisterInputDevice e GAMESPACE_AUDIO_HANDLER::RegisterAudioDevice preparam os objetos para tratar dos dispositivos de entrada (mouse e teclado) e áudio. Devemos especificar NULL para ambos os argumentos.
gsVideo.HideCursor(true);
Esta função trata de esconder ou exibir o ponteiro do mouse. Neste caso estamos definindo que não queremos exibí-lo (true). Caso queiramos mostrar o cursor especificamos false.
gsSprite.LoadSprite(&gsVideo, "character.bmp", GS_ARGB(255,0,255,0));
Aqui é onde nós carregamos o sprite. No primeiro parâmetro devemos passar um ponteiro para o objeto de tratamento de vídeo (GAMESPACE_VIDEO_HANDLER) que estamos utilizando. Em seguida informamos o nome do arquivo que queremos carregar. Neste caso eu escolhi o seguinte sprite:

No último parâmetro informamos a cor que queremos ignorar na hora de desenhar o sprite. Neste caso é a cor verde. O macro GS_ARGB(ALPHA,VERMELHO,VERDE,AZUL) transforma três valores RGB mais o canal alpha (de transparência) em um valor de cor 32-bit.
Caso você queira, também pode definir o valor de transparência da figura no próprio arquivo com o formato PNG. Caso o faça, defina este parâmetro como 0×0 (zero em hexadecimal).
Note que a figura utilizada possui um mesmo personagem em diversas posições. Na primeira linha o vemos virado de frente, nas duas linhas seguintes, para os lados e por último, de costas. Se dividirmos essa imagem em pequenos retângulos e utilizarmos cada quadro por vez, teremos um personagem animado. Você verá como fazer isso adiante.
while (gsVideo.ManageLoop())
{
Aqui começamos o laço principal do programa. Em jogos ou em qualquer aplicação que envolva gráficos, o laço principal é fundamental para que possa haver a interação e animação.
O método ManageLoop trata automaticamente dos eventos do sistema, exibe o que está no back buffer (se houver algo) e prepara o back buffer para o desenho direto de sprites. Retorna true caso tenha obtido sucesso ou false caso o usuário do programa tenha requisitado seu encerramento. Esse método deve ser usado uma vez a cada rodada do laço principal.
Neste exemplo o programa irá se repetir enquanto ManageLoop retornar true. Com isso temos todo o laço principal pronto com apenas uma única função. Podemos fazer todo o trabalho de desenho de sprite dentro dele.
gsSprite.DrawSprite(GS_VECTOR2(100, 100));
Esta função desenha o sprite. A estrutura GS_VECTOR2 representa um ponto na tela, com os eixos x e y. GS_VECTOR2(x,y) especifica um ponto, em pixels, onde o sprite deve ser desenhado.
Pronto, enviamos a instrução para desenhar o sprite. Agora precisamos encerrar a cena e exibir na tela do computador o que foi desenhado:
gsInput.UpdateInputData();
O método GAMESPACE_INPUT_HANDLER::UpdateInputData atualiza a leitura do teclado e mouse. Deve ser chamado uma vez a cada execução do laço principal.
}
}
return 0;
}
As últimas linhas de código tratam de encerrar o laço, a condicional e a função principal.
Pronto! Agora temos uma programa que com algumas linhas de código cria a janela, carrega e desenha um sprite! Caso você queira obter informações mais aprofundadas sobre os objetos e métodos usados, visite: http://www.asantee.net/gamespace/reference.htm
Parte III - Animando o personagem
Agora faremos algumas adições ao código para que ele permita que controlemos o personagem com o teclado. Mudanças no código serão marcadas com negrito e comentadas:
#pragma comment (lib, "GameSpace.lib")
#include
#include
INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )
{
GAMESPACE_VIDEO_HANDLER gsVideo;
GAMESPACE_INPUT_HANDLER gsInput;
GAMESPACE_AUDIO_HANDLER gsAudio;
GS_SPRITE gsSprite;
if (gsVideo.StartApplication(640, 480, "GameSpace", false))
{
gsInput.RegisterInputDevice(NULL);
gsAudio.RegisterAudioDevice(NULL);
gsVideo.HideCursor(true);
gsSprite.LoadSprite(&gsVideo, "character.bmp", GS_ARGB(255,0,255,0));
gsSprite.SetupSpriteRects(4, 4);
GS_INTERPOLATOR interp;
const float fStride = 100.0f;
const float fSpeed = 1.0f;
int nDir = 0;
float nXpos = 100, nYpos = 100;
while (gsVideo.ManageLoop())
{
gsInput.UpdateInputData();
bool bMoving = false;
if (gsInput.KeyDown(GSK_LEFT))
{
nDir=1;
nXpos-=fSpeed;
interp.Set(0, 3, fStride);
bMoving = true;
} else
if (gsInput.KeyDown(GSK_RIGHT))
{
nDir=2;
nXpos+=fSpeed;
interp.Set(0, 3, fStride);
bMoving = true;
}
if (gsInput.KeyDown(GSK_DOWN))
{
nDir=0;
nYpos+=fSpeed;
interp.Set(0, 3, fStride);
bMoving = true;
} else
if (gsInput.KeyDown(GSK_UP))
{
nDir=3;
nYpos-=fSpeed;
interp.Set(0, 3, fStride);
bMoving = true;
}
if (!bMoving)
{
interp.Set(0, 0, 0.0f);
}
gsSprite.SetRect(interp.GetCurrentFrame(), nDir);
gsSprite.DrawSprite(GS_VECTOR2((float)nXpos, (float)nYpos));
gsVideo.PrintText(GS_VECTOR2(0,0),
"Utilize as setas para mover o personagem",
"Tahoma", 30.0f, 0xFFFFFFFF);
}
}
return 0;
}
Agora vamos analizar as novas linhas de código:
gsSprite.LoadSprite(&gsVideo, "character.bmp", GS_ARGB(255,0,255,0)); gsSprite.SetupSpriteRects(4, 4);
Você já deve ter notado que a imagem que utilizamos possui 4 linhas e 4 colunas. As linhas especificam uma direção diferente (baixo, esquerda, direita, cima) para a qual o personagem aponta. E em cada coluna temos uma etapa diferente no movimento de “caminhar” do personagem, nós chamaremos cada subdivisão da animação de “quadro”, ou “quadro de animação”:

O que o método GS_SPRITE::SetupSpriteRects faz é criar retângulos que dividem o bitmap em quadros de animação. Nos argumentos do método especificamos que a figura possui quatro colunas de quadros e que cada coluna possui 4 linhas.
GS_INTERPOLATOR interp;
O objeto GS_INTERPOLATOR é usado para criar animações suaves. Mais adiante veremos como ele funciona.
const float fStride = 100.0f; const float fSpeed = 1.0f;
Aqui definimos a constante fStride que especifica o tempo de espera entre a troca de um quadro de animação para o próximo. Este valor será usado posteriormente com o objeto GS_INTERPOLATOR.
Em seguida a constante fSpeed especifica uma velocidade de movimento para personagem. Experimente aumentar ou diminuir levemente este valor para ver o resultado.
int nDir = 0; float nXpos = 100, nYpos = 100;
A variável nDir irá armazenar a direção do personagem (baixo, esquerda, direita ou cima). Este valor pode ir de 0 a 3 e representa na verdade uma das 4 linhas que será usada. Lembre-se de que cada linha de quadros na imagem carregada representa uma direção diferente.
As variáveis nXpos e nYpos vão armazenar a posição do personagem nos eixos X e Y da tela.
bool bMoving = false;
Esta variável receberá o valor true caso o usuário pressione uma tecla para mover o personagem.
if (gsInput.KeyDown(GSK_LEFT))
{
nDir=1;
nXpos-=fSpeed;
interp.Set(0, 3, fStride);
bMoving = true;
} else
if (gsInput.KeyDown(GSK_RIGHT))
{
nDir=2;
nXpos+=fSpeed;
interp.Set(0, 3, fStride);
bMoving = true;
}
Aqui utilizamos o objeto GAMESPACE_INPUT_HANDLER pela primeira vez. O método KeyDown retorna true se a tecla especificada como argumento estiver sendo pressionada. Veja a lista de opções de argumento para essa função em: http://www.asantee.net/gamespace/reference.htm#GS_KEY
Caso a seta para esquerda esteja sendo pressionada, a direção do personagem será definida para a linha 2 (lembre-se que a primeira linha é a 0!) e fSpeed será decrementada de nXpos.
O método GS_INTERPOLATOR::Set pode ser usado para criar uma animação suave para animações baseadas em quadros. Nos argumentos do método definimos que os quadros da animação irão do 0 ao 3 (da primeira à quarta coluna na imagem do sprite) e que os quadros devem ser trocados a cada 100 milésimos (veja a declaração de fStride).
Caso a seta para direita seja pressionada, definimos a terceira linha como direção do personagem e incrementamos nXpos com fSpeed.
O mesmo fazemos para as setas CIMA e BAIXO do teclado:
if (gsInput.KeyDown(GSK_DOWN))
{
nDir=0;
nYpos+=fSpeed;
interp.Set(0, 3, fStride);
bMoving = true;
} else
if (gsInput.KeyDown(GSK_UP))
{
nDir=3;
nYpos-=fSpeed;
interp.Set(0, 3, fStride);
bMoving = true;
}
Assim controlamos todas as variáveis que vão definir a posição e animação do personagem.
if (!bMoving)
{
interp.Set(0, 0, 0.0f);
}
Caso o personagem esteja movendo-se, definir a animação para o quadro 0 (primeira coluna).
gsSprite.SetRect(interp.GetCurrentFrame(), nDir);
Com a função GS_SPRITE::SetRect definimos o quadro que queremos utilizar dentro da grade 4×4 em que a imagem foi dividida (com o método GS_SPRITE::SetupSpriteRects).
Primeiro definimos a coluna que queremos. O método GS_INTERPOLATOR::GetCurrentFrame retorna o quadro atual da animação (que foi definida entre 0 e 4 com Set). E nDir especifica qual linha do quadro queremos. Esta linha é especificada por nDir.
Agora que definimos o quadro atual do sprite podemos desenha-lo:
gsSprite.DrawSprite(GS_VECTOR2((float)nXpos, (float)nYpos));
Aqui desenhamos o sprite nas posição nXpos e nYpos. E aí temos nosso personagem interativo!
Para saber mais sobre a GameSpaceLib visite http://www.asantee.net/gamespace/
Você pode baixar o jogo-exemplo com o seu código-fonte aqui: Space Pong com GameSpaceLib.























), e eles ficam organizados como uma matriz.