Não é qualquer alien que vai chegando assim!

Share

Continuando nosso exemplo anterior sobre SDL, vamos tentar por fim ao passeio do nosso amigo alien pela tela. Para isto vamos adicionar uma mira e disparar alguns mísseis nele.

Para realizar esta tarefa vamos usar outro recurso do SDL, a biblioteca SDL_gfx que vai nos permitir desenhar primitivas na tela. Vamos ainda adicionar mais controles pelo teclado para controlar nossa mira.

district9

Configurando o Ambiente

Ubuntu

No Ubuntu, com um usuário com acesso ao suders instale os arquivos de desenvolvimento necessários:

sudo apt-get install libsdl1.2-dev  libsdl-image1.2-dev libsdl-gfx1.2-dev

E para compilar nosso programa no Linux adicionamos a diretiva -lSDL_gfx.

gcc -lSDL -lSDL_image -lSDL_gfx arquivo.c -o executavel

Para termos acesso à biblioteca SDL/SDL_gfxPrimitives.h de desenho de primitivas termos que inserir o arquivo nos nossos includes:

#include <SDL/SDL.h>
#include <math.h>
#include <SDL/SDL_image.h>
#include <SDL/SDL_gfxPrimitives.h>

Desenhando e movimentando o alvo

Para guardar nosso alvo vamos usar um simples círculo de raio 15 e para guardar os dados de nosso alvo vamos definir uma variável alvoPos, do tipo SDL_Rect:

   SDL_Rect alvoPos;
   alvoPos.x = 320;
   alvoPos.y = 400;
   alvoPos.h = 15;
   alvoPos.w = 15;

Agora o desenho dele será feito por último, logo antes de realizar a troca do buffer. No nosso exemplos usamos um alvo amarelo e um pouco transparente, misturando as cores RGB 255 255 0 e o alfa 200:

      SDL_BlitSurface(alienImage,NULL,screen,&alienPos);
      ellipseRGBA(screen, alvoPos.x, alvoPos.y,
                  alvoPos.w, alvoPos.h, 255, 255, 0, 200);
      SDL_Flip(screen);

Agora vamos adicionar o movimento acrescentando de forma análoga ao controle do pressionamento da tecla ESC. Precisamos de variáveis para indicar quando cada botão está pressionado:

   int leftPressed = 0, rightPressed = 0,
       upPressed = 0, downPressed = 0,
       spacePressed = 0,missilFired = 0;

Alteramos o controle de teclas para alterar estas variáveis se as teclas foram pressionadas ou soltas:

      if( SDL_PollEvent( &event ) ){
         switch(event.type){
         case SDL_KEYDOWN:
	         switch(event.key.keysym.sym) {
            case SDLK_ESCAPE:
               SDL_Quit();
               exit(0);                  
               break;
        	   case SDLK_LEFT:
            leftPressed = 1;
            break;
            case SDLK_RIGHT:
               rightPressed = 1;
               break;
            case SDLK_DOWN:
               downPressed = 1;
               break;
            case SDLK_UP:
		         upPressed = 1;
		         break;
            case SDLK_SPACE:
		         spacePressed = 1;
		         break;
            default:
               break;
            } 
         break;// SDL_KEYDOWN
	 case SDL_KEYUP:
	    switch(event.key.keysym.sym) {
	       case SDLK_LEFT:
                   leftPressed = 0;
                   break;
               case SDLK_RIGHT:
                   rightPressed = 0;
                   break;
               case SDLK_DOWN:
                  downPressed = 0;
                  break;
               case SDLK_UP:
                  upPressed = 0;
                  break;
               case SDLK_SPACE:
                  spacePressed = 0;
                  break;
               default:
		   break;
            }
            break;// SDL_KEYUP
        default:
           break;
        }
      }

Agora alteramos nosso laço principal para realizar as alterações em alvoPos com base nas variáveis definidas:

      if (leftPressed) {
         if (alvoPos.x > 0){
            alvoPos.x-=10;
         }
      }
      if (rightPressed) {
         if (alvoPos.x < 640-alvoPos.w){
            alvoPos.x+=10;
         }
      }
      if (upPressed) {
         if (alvoPos.y > 0) {
            alvoPos.y-=10;
	 }
      }
      if (downPressed) {
         if (alvoPos.y < 480-alvoPos.h) {
            alvoPos.y+=10;
         }
}

Ou de forma mais obscura compacta usando operadores ternários:

      alvoPos.x -= leftPressed?(alvoPos.x > 0)?10:0:0;
      alvoPos.x += rightPressed?(alvoPos.x < 640-alvoPos.w)?10:0:0;
      alvoPos.y -= upPressed?(alvoPos.y > 0)?10:0:0;
      alvoPos.y += downPressed?(alvoPos.y < 480-alvoPos.h)?10:0:0;

Com isto já podemos apontar para o invasor, mas ainda não podemos atirar.

Criando o efeito de um tiro

Para criar o efeito de um tiro, vamos precisar guardar a posição inicial, a posição atual e a posição final. A posição final será igual a do nosso alvo quando apertarmos a barra de espaço.

Novamente vamos usar variáveis SDL_Rect para guardar estes dados:

   SDL_Rect missilOrigem;
   missilOrigem.x = 320;
   missilOrigem.y = 480+5;
   missilOrigem.h = 5;
   missilOrigem.w = 5;
 
   SDL_Rect missilPos;
   missilPos = missilOrigem;
 
   SDL_Rect missilDestino;
   missilDestino = missilOrigem;

Ao pressionarmos a barra de espaço, o valor do alvo vai para missilDestino. Para calcular a velocidade do míssil usamos umas relações trigonométricas com base na origem do tiro.

         if (spacePressed) {
            missilFired = 1;
            missilPos.x = missilOrigem.x;
            missilPos.y = missilOrigem.y;
            missilDestino.x = alvoPos.x;
            missilDestino.y = alvoPos.y;
         }
         if(missilFired){
            if( missilDestino.x == missilPos.x
               && missilDestino.y == missilPos.y){
               missilFired = 0;
               missilPos.x = missilOrigem.x;
               missilPos.y = missilOrigem.y;
            } else {
               float base = pow(pow(missilDestino.x-320,2)+pow(missilDestino.y-480,2),0.5);
               float dx = -15*(missilOrigem.x-missilDestino.x)/base;
               float dy = -15*(missilOrigem.y-missilDestino.y)/base;
               missilPos.x += dx;
               missilPos.y += dy;
            }
         }

Colhendo os troféus

O último passo é verificar se o míssil acerta o alvo e realizar a contabilidade dos pontos marcados. Vamos criar duas variáveis para guardar o número de acertos e alienígenas que passaram pela tela:

   int score = 0;
   int wave = 1;

O teste de colisão do míssil com o alien é simples de ser implementado. Basta checarmos as posições e em caso de colisão, voltar todo mundo para um lugar fora da tela:

      if(missilFired){
        filledEllipseRGBA(screen, missilPos.x, missilPos.y,
                  missilPos.w, missilPos.h, 200, 255, 0, 255);
      if(missilPos.x>alienPos.x && missilPos.x<alienPos.x+alienPos.w){
         if(missilPos.y>alienPos.y && missilPos.y<alienPos.y+alienPos.h){
            score++;
            alienPos.y = 0;
            missilPos = missilOrigem;
            missilFired = 0;
         }
      }

Por fim, apresentamos o resultado na tela antes de realizar a troca entre o buffer:

      sprintf(scores,"Acertos: %d(%.2f%%) Aliens: %d",score,100.0*(float)score/wave,wave);
      stringRGBA(screen, 10, 10, scores,0,0,255,255);
      SDL_Flip(screen);

O nosso código porquinho final fica da seguinte forma:

#include <SDL/SDL.h>
#include <math.h>
#include <SDL/SDL_image.h>
#include <SDL/SDL_gfxPrimitives.h>
int main( int argc, char* argv[])
{
   SDL_Surface *screen;
   SDL_Event event;
   SDL_Init(SDL_INIT_EVERYTHING);
   screen = SDL_SetVideoMode(640, 480, 32, SDL_DOUBLEBUF | SDL_HWSURFACE);
 
   SDL_Surface *alienImage = IMG_Load("space_invader.bmp");
   SDL_SetColorKey(alienImage, (SDL_SRCCOLORKEY|SDL_RLEACCEL),
      SDL_MapRGB(alienImage->format, 255, 255, 255));
 
   SDL_Rect alienPos;
   alienPos.x = 0;
   alienPos.y = 10;
   alienPos.h = alienImage->h;
   alienPos.w = alienImage->w;
 
   SDL_Rect alvoPos;
   alvoPos.x = 320;
   alvoPos.y = 400;
   alvoPos.h = 15;
   alvoPos.w = 15;
 
   SDL_Rect missilOrigem;
   missilOrigem.x = 320;
   missilOrigem.y = 480+5;
   missilOrigem.h = 5;
   missilOrigem.w = 5;
 
   SDL_Rect missilPos;
   missilPos = missilOrigem;
 
   SDL_Rect missilDestino;
   missilDestino = missilOrigem;
 
   int score = 0;
   int wave = 1;
 
   float c = 0.0;
   int leftPressed = 0, rightPressed = 0,
       upPressed = 0, downPressed = 0,
       spacePressed = 0,missilFired = 0;
 
   char scores[40];
 
   while(1){
      SDL_FillRect(screen, NULL, 0);
 
      SDL_BlitSurface(alienImage,NULL,screen,&alienPos);
      ellipseRGBA(screen, alvoPos.x, alvoPos.y,
         alvoPos.w, alvoPos.h, 255, 255, 0, 200);
      if(missilFired){
        filledEllipseRGBA(screen, missilPos.x, missilPos.y,
           missilPos.w, missilPos.h, 200, 255, 0, 255);
        if(missilPos.x>alienPos.x && missilPos.x<alienPos.x+alienPos.w){
           if(missilPos.y>alienPos.y && missilPos.y<alienPos.y+alienPos.h){
              score++;
              alienPos.y = 0;
              missilPos = missilOrigem;
              missilFired = 0;
           }
        }
      }
 
      sprintf(scores,"Acertos: %d(%.2f%%) Aliens: %d",score,100.0*(float)score/wave,wave);
      stringRGBA(screen, 10, 10, scores,255,0,0,255);
      SDL_Flip(screen);
 
      if(c>2*M_PI){
         c = 0.0;
      } else {
         c = c + 0.05;
         alienPos.x = (600/2)*(1+cos(c));
      }
      if(alienPos.y>480){
         alienPos.y = -(alienImage->h);
         wave++;
      } else {
         alienPos.y = alienPos.y + 10;
      }
      if (leftPressed) {
         if (alvoPos.x > 0)
            alvoPos.x-=10;
      }
      if (rightPressed) {
         if (alvoPos.x < 640-alvoPos.w)
            alvoPos.x+=10;
      }
      if (upPressed) {
         if (alvoPos.y > 0) {
            alvoPos.y-=10;
         }
      }
      if (downPressed) {
         if (alvoPos.y < 480-alvoPos.h) {
            alvoPos.y+=10;
         }
      }
      if (spacePressed) {
         missilFired = 1;
         missilPos.x = missilOrigem.x;
         missilPos.y = missilOrigem.y;
         missilDestino.x = alvoPos.x;
         missilDestino.y = alvoPos.y;
      }
      if(missilFired){
         if(missilDestino.x == missilPos.x&&missilDestino.y == missilPos.y){
            missilFired = 0;
            missilPos.x = missilOrigem.x;
            missilPos.y = missilOrigem.y;
         } else {
            float base = pow(pow(missilDestino.x-320,2)+pow(missilDestino.y-480,2),0.5);
            float dx = -15*(missilOrigem.x-missilDestino.x)/base;
            float dy = -15*(missilOrigem.y-missilDestino.y)/base;
            missilPos.x += dx;
            missilPos.y += dy;
         }
      }
 
 
      if( SDL_PollEvent( &event ) ){
         switch(event.type){
         case SDL_KEYDOWN:
            switch(event.key.keysym.sym) {
               case SDLK_ESCAPE:
                  SDL_Quit();
                  exit(0);                  
               break;
               case SDLK_LEFT:
                  leftPressed = 1;
               break;
               case SDLK_RIGHT:
                  rightPressed = 1;
               break;
               case SDLK_DOWN:
                  downPressed = 1;
               break;
               case SDLK_UP:
                  upPressed = 1;
               break;
               case SDLK_SPACE:
                  spacePressed = 1;
               break;
               default:
               break;
            } 
         break;// SDL_KEYDOWN
         case SDL_KEYUP:
            switch(event.key.keysym.sym) {
               case SDLK_LEFT:
                  leftPressed = 0;
               break;
               case SDLK_RIGHT:
                  rightPressed = 0;
               break;
               case SDLK_DOWN:
                  downPressed = 0;
               break;
               case SDLK_UP:
                  upPressed = 0;
               break;
               case SDLK_SPACE:
                  spacePressed = 0;
               break;
               default:
               break;
            }
         break;// SDL_KEYUP
         default:
         break;
         }
      }
      SDL_Delay(50);
   }
   SDL_FreeSurface(alienImage);
   SDL_Quit();
}

Agora precisamos por ordem no nosso código (não sei se já falei que ele está muito porco desorganizado). Então na próxima parte vamos cortar esse código monstro usando funções e adicionar algumas animações para dar um tchan a mais.

Share
Sobre

Um geek ermitão

Publicado em Hobby Marcado com: ,

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

*