Curso completo de DarkGdk
Gameprog - Escola de programação de jogos digitais
Contato: gameprog.br@gmail.com
Fase 8.4

index << >>



08.4 Colisão de sprites

Visão geral
A colisão é um dos aspectos que mais preenche de vida um jogo de videogame visto que ela detona a interação entre muitos objetos quando ocorre a intersecção de um ponto ou espaço comum entre eles. Basicamente a colisão ocorre quando dois objetos ocupam um mesmo ponto ou espaço em comum. Em termos de colisão de pontos, dois pontos colidem quando apresentam a mesma coordenada. Quando se pensa em um objeto maior, ocorre uma colisão quando um outro objeto adentra uma parte ou um ponto de sua área de ocupação. É muito comum no cálculo de colisões considerar que os objetos estão cercados por quadrados ou círculos e a partir daí basear todos os cálculos de colisão no posicionamento e dimensões dessas formas geométricas básicas. Em inglês, o quadrado ou retângulo que cerca o objeto é chamado de 'bounding rectangle' e o círculo de 'bounding circle'. Transportando esse raciocínio para um cenário 3D, para cálculo de colisões, os objetos são visualizados como se fossem cubos ou esferas. Em inglês, o cubo e a esfera que cercam os objetos são chamados respectivamente de bounding box e bounding sphere. Em objetos irregulares, como um avião 2d rascunhado na ilustração acima, é comum a distribuição de vários retangulos para tornar mais preciso o cálculo de colisão. Em um jogo de luta pode-se mapear o pé do lutador com um retângulo para verificar colisão do pé com o oponente. É comum a detecção de colisão de um objeto operar a partir de uma distância a partir da posição desse objeto, essa distância é chamada de deslocamento de colisão, em inglês, collision offset. Internamente o DirectX possui estruturas e funções que implementam estes conceitos dos quais a DarkGdk se utiliza para seu microsistema de detecção de colisão. Funções para teste de colisão nSpriteColidindo = dbSpriteHit( nSprite, nSpriveAlvo) Esta função retorna 1 se a sprite especificada colidiu contra a sprite alvo mencionada. Se a sprite alvo não for mencionada esta função retorna o número da sprite que porventura tenha impactado contra nSprite. Exemplo:
// Se o tiro acertou o inimigo, reduza a energia dele if (dbSpriteHit (sprt_tiro, sprt_inimigo) == 1) { inimigo_energia = inimigo_energia - 10; dbPlaySound (som_tiro); } // endif // Se o jogador se matou, termine o jogo... if (dbSpriteHit (sprt_jogador, sprt_inimigo) == 1) { dbPlaySound (som_explodiu); terminar_app = 1; break; } // endif
Veja nosso programa exemplo:

// colisao.cpp // Esse programa demonstra colisão de sprites #include "DarkGDK.h" // Protótipo das funções void initsys(); // inicializa o sistema void load_imagens(); // Carrega as imagens void load_efeitos_sonoros(); // Carrega os efeitos sonoros void menu(); // Mostra um menu de opções na tela void controle_principal(); // Controle principal do jogo void atualiza_tiro(); // atualiza posição do tiro disparado void atualiza_inimigo(); // atualiza posição do inimigo void mostrar_info(); // Mostrar status na tela void mudar_cor_difusa(void); // Muda a cor difusa do inimigo // Cores const int nPreto = 0; const int nBranco = 0xFFFFFF; const int nAzul = 0x0000FF; // Atores do jogo const int sprt_jogador = 1; const int sprt_inimigo = 2; const int sprt_tiro = 3; // Efeitos sonoros const int som_tiro = 1; const int som_explodiu = 2; // Controle do tiro disparado int tiro_disparado = 0; int tiro_viajando = 0; int terminar_app = 0; // Controle do inimigo int inimigo_distancia = 0; int inimigo_velocidade = 2; int inimigo_energia = 100; // String para mostrar texto char info[255]; // Estrutura para posicionar as sprites inicialmente struct Posicao { int xpos; int ypos; }; Posicao sprt[4]; // Criando 4 (0-3) estruturas de Posição // ---------------------------------------------------------------------------- void DarkGDK ( void ){ // Começo da aplicação DarkGdk initsys(); // Carrega imagens e efeitos sonoros load_imagens(); load_efeitos_sonoros(); menu(); dbSync(); dbWaitKey(); while ( LoopGDK ( ) ) { controle_principal(); if (terminar_app == 1) return; dbSync ( ); } // fim do while return; } // fim da função: DarkGDK // ---------------------------------------------------------------------------- void initsys(){ // Esta função inicializa o sistema dbSyncOn( ); dbSetWindowSize(600,480); dbSetWindowPosition(160,50); dbSetWindowTitle("colisao.cpp"); dbHideMouse(); dbSetCurrentBitmap(0); } // fim da função: initsys() // ---------------------------------------------------------------------------- void menu(){ // Variáveis de trabalho para mostrar texto int xpos = 260; int ypos = 40; int coluna = xpos; int linha = ypos; int ncx = 0; int espaco_entrelinhas = 20; // Nossa lista de opções char *slista[] = {"Setas - mover", "Espaco - Atirar", "q/botao direito - sair"}; dbCenterText (320,10, "Space Invader Gameprog"); // Mostre a lista for (ncx = 0; ncx < 3; ncx++) { linha = ypos + ( ncx * espaco_entrelinhas ); dbText (coluna, linha, slista[ncx]); } // fim do for(ncx) dbCenterText (320,240, "Pressione <enter> para continuar"); dbSync(); } // menu().end // ---------------------------------------------------------------------------- void controle_principal(){ // Posição inicial do jogador na tela sprt[sprt_jogador].xpos = 300; sprt[sprt_jogador].ypos = 420; // Posição inicial do inimigo na tela sprt[sprt_inimigo].xpos = 300; sprt[sprt_inimigo].ypos = 10; // Posição inicial do tiro alinhado com o jogador sprt[sprt_tiro].xpos = sprt[sprt_jogador].xpos + 45; sprt[sprt_tiro].ypos = 396; // Elabora as sprites a partir das imagens dbSprite (1, sprt[sprt_jogador].xpos, sprt[sprt_jogador].ypos, 1); dbSprite (2, sprt[sprt_inimigo].xpos, sprt[sprt_inimigo].ypos, 2); dbSprite (3, sprt[sprt_tiro].xpos, sprt[sprt_tiro].ypos, 3); // Esconde a imagem do tiro dbHideSprite (sprt_tiro); // Controla leitura do controle_principal while (dbMouseClick() != 2) { char *tecla = dbInKey(); if (!strcmp(tecla,"q")) { terminar_app = 1; break; } // endif // Controla movimentação da sprite(2) - nosso 'player' if (dbDownKey()==1) dbOffsetSprite (sprt_jogador, dbSpriteOffsetX(sprt_jogador), dbSpriteOffsetY(sprt_jogador)-2 ); if (dbUpKey()==1) dbOffsetSprite (sprt_jogador, dbSpriteOffsetX(sprt_jogador), dbSpriteOffsetY(sprt_jogador)+2); if (dbRightKey()==1) dbOffsetSprite (sprt_jogador, dbSpriteOffsetX(sprt_jogador)-2 , dbSpriteOffsetY(sprt_jogador)); if (dbLeftKey()==1) dbOffsetSprite (sprt_jogador, dbSpriteOffsetX(sprt_jogador)+2, dbSpriteOffsetY(sprt_jogador)); // O espaço dispara o tiro if (dbSpaceKey()==1) { if (tiro_disparado == 0) { tiro_disparado = 1; tiro_viajando = 1; dbShowSprite (sprt_tiro); // Configura posição do tiro conforme posição do jogador int dx, dy; dx = dbSpriteOffsetX(sprt_jogador); dy = dbSpriteOffsetY(sprt_jogador); dbOffsetSprite(sprt_tiro, dx, dy); }// Só dispara um tiro por vez }// endif (espaço) (tiro) if (tiro_viajando == 1) atualiza_tiro(); atualiza_inimigo(); // Se o tiro acertou o inimigo, reduza a energia dele if (dbSpriteHit (sprt_tiro, sprt_inimigo) == 1) { inimigo_energia = inimigo_energia - 10; dbPlaySound (som_tiro); } // endif // Se o jogador se matou, termine o jogo... if (dbSpriteHit (sprt_jogador, sprt_inimigo) == 1) { dbPlaySound (som_explodiu); terminar_app = 1; break; } // endif // Se o inimigo 'morrer' toque o efeito sonoro e troque a cor dele if (inimigo_energia <= 0) { dbPlaySound (som_explodiu); inimigo_energia = 100; mudar_cor_difusa(); } // endif // menu(); mostrar_info(); dbSync(); } // endwhile (leitura do controle_principal) // Vamos liberar a memória for (int item=1; item <= 3; item++) { if (dbImageExist(item)) dbDeleteImage (item); if (dbSpriteExist(item)) dbDeleteSprite (item); } // fim do if dbCLS(0); dbShowMouse(); dbCenterText (320,240, "Pressione a tecla <ESC> para finalizar"); dbSync(); dbWaitKey(); } // endfunc: controle_principal void mostrar_info(void){ int dx, dy; // Mostra posição do jogador dx = dbSpriteOffsetX(sprt_jogador); dy = dbSpriteOffsetY(sprt_jogador); sprintf (info, "Jogador.pos: (%d, %d)",dx, dy); dbText (10,60, info); // Mostra posição do inimigo dx = dbSpriteOffsetX(sprt_inimigo); dy = dbSpriteOffsetY(sprt_inimigo); sprintf (info, "Inimigo.pos: (%d, %d)", dx, dy); dbText (10,80, info); sprintf (info, "Inimigo.hp: %d", inimigo_energia); dbText (10,100, info); // Mostra a posição do mouse na janela sprintf(info, "colisao.cpp - mouse: %d , %d", dbMouseX(), dbMouseY()); dbSetWindowTitle (info); } // mostrar_info() void atualiza_tiro(void) { int dx = 0; int dy = 0; // Pega última posição da bala dx = dbSpriteOffsetX(sprt_tiro); dy = dbSpriteOffsetY(sprt_tiro); // Avança bala pra frente dbOffsetSprite (sprt_tiro, dx, dy + 2); // Mostra posição do tiro na tela sprintf(info, "tiro.pos: %d , %d", dx, dy+2); dbText (320,400, info); // Deleta tiro que sumiu da tela if (dy > 400) { dbOffsetSprite (sprt_tiro, dx, 0); tiro_disparado = 0; tiro_viajando = 0; dbHideSprite(sprt_tiro); } // endif } // atualiza_tiro() void atualiza_inimigo(void) { int dx = 0; int dy = 0; // Faz contagem da distância inimigo_distancia++; // Pega posição do inimigo dx = dbSpriteOffsetX(sprt_inimigo); dy = dbSpriteOffsetY(sprt_inimigo); // Inverte movimento nas fronteiras da tela if (inimigo_distancia == 160) { inimigo_velocidade = inimigo_velocidade * -1; inimigo_distancia = -120; } // Movimenta o inimigo na tela dbOffsetSprite (sprt_inimigo, dx + inimigo_velocidade, dy); } // atualiza_inimigo() void mudar_cor_difusa(void){ int tr, tg, tb; // Gera cor aleatória tr = dbRnd(255); tg = dbRnd(255); tb = dbRnd(255); // Configura individualmente cada canal de cor dbSetSpriteDiffuse (sprt_inimigo,tr,tg,tb); } // mudar_cor_difusa().fim void load_imagens() { // As imagens tem um fundo preto dbSetImageColorKey (0,0,0); // Carrega as imagens dbLoadImage ("c:\\gameprog\\gdkmedia\\bitmap\\ship7.png", sprt_jogador); dbLoadImage ("c:\\gameprog\\gdkmedia\\bitmap\\alien3.png", sprt_inimigo); dbLoadImage ("c:\\gameprog\\gdkmedia\\bitmap\\bolt.png", sprt_tiro); } // load_imagens() void load_efeitos_sonoros() { // Carrega os efeitos sonoros dbLoadSound ("c:\\gameprog\\gdkmedia\\som\\shoot.wav",som_tiro); dbLoadSound ("c:\\gameprog\\gdkmedia\\som\\explode.wav",som_explodiu); } // load_efeitos_sonoros()

index << >>


Produzido por Gameprog: Jair Pereira - Setembro/2013 © gameprog.br@gmail.com http://www.gameprog.com.br http://www.nucleoararat.com.br