terça-feira, 29 de maio de 2007

Vetores

Vetores são como variáveis, mas podem conter diversos valores.

Digamos, por exemplo, que eu queira trabalhar com moedas. Cada tipo de moeda tem um valor (em centavos): moeda1=1, moeda2=5, moeda3=10, moeda4=25, moeda5=50 e moeda6=100. Usando as variáveis dessa forma, se eu quisesse usar um for para mostrar o valor de todas as moedas, eu não conseguiria.

O vetor vem ao meu socorro. Por exemplo:
s16 moeda[6]={1,5,10,25,50,100};
for (i=0; i<6; i++) {
  PA_OutputText(0,0,i,"moeda[%d]=%d",i,moeda[i]);
}

O código acima apresentará uma saída asim:
moeda[0]=1
moeda[1]=5
moeda[2]=10
moeda[3]=25
moeda[4]=50
moeda[5]=100

Nós fizemos um vetor de 6 posições (de 0 a 5) contendo os valores das moedas do Brasil.

Vamos aplicar isso ao nosso jogo. O cenário tem áreas de areia, de terra, de grama e de água. O tanque não deve andar sobre a água. Mas o tipo de solo também influencia na velocidade do tanque. Então faremos o tanque andar na metade da velocidade na areia (1 pixel a cada 2 frames), em velocidade normal na grama (1 pixel por frame) e em velocidade dobrada na terra (2 pixels por frame). Usando a lógica de bit shifting que já estudamos, teríamos uma tabela contendo velocidade=0 se o terreno for água, 128 se for areia, 256 se for grama e 512 se for terra.

Eu dividi o background em quadrados de 16x16 pixels e coloquei num vetor. Como a imagem tem 256x192 pixels, isso divindo por 16 cada valor me gerou um vetor de 192 posições (16x12=192). O vetor é representado de forma linear, mas a tela tem linhas e colunas. Então é necessária uma fórmula para acessarmos todas as posições. Essa fórmula é: linha*largura+coluna. Neste caso é linha*16+coluna. A posição de linha e coluna é calculada relativamente ao centro do tanque para facilitar a programação.

Como existe a possibilidade de ter áreas onde o tanque não se move, ele jamais deve entrar com o centro nessas áreas. Quando ele entrar em uma área de velocidade 0, a posição do tanque deve ser restaurada para posição antiga, anterior à entrada nessa área. Se o tanque entrasse numa área de velocidade 0, ele jamais sairia dela, pois a sua velocidade seria 0.

Outra coisa que corrigi é que quando o tanque atingia uma borda da tela e o controle estivesse sendo pressionado na diagonal, o tanque continuava se movendo em apenas um dos eixos ao invés de parar. Agora ele pára.

Vamos ao código-fonte. Analisem o fonte e encontrem o que foi falado acima. Se houver alguma dúvida, façam a pergunta nos comentários.


// Includes
#include <PA9.h> // Include for PA_Lib
#include "gfx/all_gfx.c"
#include "gfx/all_gfx.h"
// Variaveis Globais
s16 angulo_body=0;
s16 angulo_turret=0;
s32 x=111<<8;
s32 y=(79+192+48)<<8;
s16 angulo_tiro=0;
s32 x_tiro=-8<<8;
s32 y_tiro=-8<<8;
s16 vel_tiro=4;
s8 atirando=0;
s16 vel_terra[192]={0,128,128,256,256,256,256,256,256,256,256,256,256,128,128,0,
  0,128,128,256,256,512,512,512,512,512,512,256,256,128,128,0,
  0,128,128,256,256,512,512,512,512,512,512,256,256,128,128,0,
  0,128,128,256,256,512,512,512,512,512,512,256,256,128,128,0,
  0,128,128,256,256,512,512,512,512,512,512,256,256,128,128,0,
  0,128,128,256,256,256,256,256,256,256,256,256,256,128,128,0,
  0,128,128,256,256,256,256,256,256,256,256,256,256,128,128,0,
  0,128,128,256,256,256,256,256,256,256,256,256,256,128,128,0,
  0,128,128,256,256,256,256,256,256,256,256,256,256,128,128,0,
  0,128,128,128,128,128,128,128,128,128,128,128,128,128,128,0,
  0,128,128,128,128,128,128,128,128,128,128,128,128,128,128,0,
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

void giraturret() {
  if (Stylus.Held) {
    angulo_turret=PA_GetAngle((x>>8)+16,(y>>8)+16-240,Stylus.X,Stylus.Y);
    PA_DualSetRotsetNoZoom(0,angulo_turret);
  }
}

void movetanque() {
  s16 centro_x=((x>>8)+15)>>4;
  s16 centro_y=((y>>8)-240+15)>>4;
  s16 tipoterra=0;
  s16 velocidade=0;
  s32 xvelho=x;
  s32 yvelho=y;
  if (centro_x<0||centro_x>15||centro_y<0||centro_y>11) {
    tipoterra=0;
    velocidade=0;
  } else {
    tipoterra=centro_y*16+centro_x;
    velocidade=vel_terra[tipoterra];
  }
  if ((Pad.Held.Left && Pad.Held.A) || (Pad.Held.Right && Pad.Held.Y) || (Pad.Held.Up && Pad.Held.B) || (Pad.Held.Down && Pad.Held.X) || (Pad.Held.X && Pad.Held.B) || (Pad.Held.Y && Pad.Held.A)) {
   //o usuário está sacaneando, não executa nada
  } else if (!(Pad.Held.Up || Pad.Held.Down || Pad.Held.Left || Pad.Held.Right || Pad.Held.A || Pad.Held.B || Pad.Held.X || Pad.Held.Y)) {
   //nenhuma direção foi pressionada
  } else {
    if ((Pad.Held.Left || Pad.Held.Y || Pad.Held.Right || Pad.Held.A) && !(Pad.Held.Up || Pad.Held.X || Pad.Held.Down || Pad.Held.B)) {
      angulo_body=256*(Pad.Held.Left || Pad.Held.Y);
      x+=velocidade*(Pad.Held.Right || Pad.Held.A)-velocidade*(Pad.Held.Left || Pad.Held.Y);
    } else if (!(Pad.Held.Left || Pad.Held.Y || Pad.Held.Right || Pad.Held.A) && (Pad.Held.Up || Pad.Held.X || Pad.Held.Down || Pad.Held.B)) {
      angulo_body=128*(Pad.Held.Up || Pad.Held.X)+384*(Pad.Held.Down || Pad.Held.B);
      y+=velocidade*(Pad.Held.Down || Pad.Held.B)-velocidade*(Pad.Held.Up || Pad.Held.X);
    } else {
      angulo_body=256*(Pad.Held.Left || Pad.Held.Y);
      if (angulo_body) {
        angulo_body=192*(Pad.Held.Up || Pad.Held.X)+320*(Pad.Held.Down || Pad.Held.B);
      } else {
        angulo_body=64*(Pad.Held.Up || Pad.Held.X)+448*(Pad.Held.Down || Pad.Held.B);
      }
      x+=velocidade*(Pad.Held.Right || Pad.Held.A)-velocidade*(Pad.Held.Left || Pad.Held.Y);
      y+=velocidade*(Pad.Held.Down || Pad.Held.B)-velocidade*(Pad.Held.Up || Pad.Held.X);
    }
    centro_x=((x>>8)+15)>>4;
    centro_y=((y>>8)-240+15)>>4;
    if (centro_x<0||centro_x>15||centro_y<0||centro_y>11) {
      tipoterra=0;
      velocidade=0;
    } else {
      tipoterra=centro_y*16+centro_x;
      velocidade=vel_terra[tipoterra];
    }
    if (velocidade==0 || x<0 || x>57088 || y<61440 || y>102144) {
      x=xvelho;
      y=yvelho;
    }
//    if (x<0) { x=0; }
//    if (x>57088) { x=57088; }
//    if (y<61440) { y=61440; }
//    if (y>102144) { y=102144; }
    PA_DualSetRotsetNoZoom(1,angulo_body);
    PA_DualSetSpriteXY(0,x>>8,y>>8);
    PA_DualSetSpriteXY(1,x>>8,y>>8);
  }
}

void atira() {
  if (atirando) {
    x_tiro+=PA_Cos(angulo_tiro)*vel_tiro;
    y_tiro-=PA_Sin(angulo_tiro)*vel_tiro;
    PA_DualSetSpriteXY(2,x_tiro>>8,y_tiro>>8);
    if (x_tiro<-8<<8 || x_tiro>260<<8 || y_tiro<-8<<8 || y_tiro>440<<8) {
      atirando=0;
    }
  } else {
    if (Pad.Newpress.L || Pad.Newpress.R) {
      atirando=1;
      angulo_tiro=angulo_turret;
      x_tiro=((x>>8)+12)<<8;
      y_tiro=((y>>8)+12)<<8;
      x_tiro+=PA_Cos(angulo_tiro)*24;
      y_tiro-=PA_Sin(angulo_tiro)*24;
      PA_DualSetRotsetNoZoom(2,angulo_tiro);
      PA_DualSetSpriteXY(2,x_tiro>>8,y_tiro>>8);
    }
  }
}

// Function: main()
int main(int argc, char ** argv)
{
  
  PA_Init(); // Initializes PA_Lib
  PA_InitVBL(); // Initializes a standard VBL
  PA_DualLoadSpritePal( 0, (void*)tank_turret_Pal);
  PA_DualCreateSprite( 0, (void*)tank_turret_Sprite, OBJ_SIZE_32X32, 1, 0, x>>8, y>>8);
  PA_DualLoadSpritePal( 1, (void*)tank_body_Pal);
  PA_DualCreateSprite( 1, (void*)tank_body_Sprite, OBJ_SIZE_32X32, 1, 0, x>>8, y>>8);
  PA_DualLoadSpritePal( 2, (void*)tiro_Pal);
  PA_DualCreateSprite( 2, (void*)tiro_Sprite, OBJ_SIZE_8X8, 1, 0, -8, -8);
  PA_DualSetSpriteRotEnable( 0, 0);
  PA_DualSetSpriteRotEnable( 1, 1);
  PA_DualSetSpriteRotEnable( 2, 2);
  PA_EasyBgLoad(0,3,set_bg_1);
  PA_EasyBgLoad(1,3,set_bg_2);
  PA_InitText(1,0);
  PA_SetTextCol(1,31,31,31);
  // Infinite loop to keep the program running
  while (1)
  {
    giraturret();
    movetanque();
    atira();
    PA_WaitForVBL();
  }
  return 0;
} // End of main()

8 comentários:

Leonardo Zimbres disse...

Vc podia colocar as novidades em bold.

Vetor é a mesma coisa que array, ou tem alguma diferença?

Guilherme - Homebrew Maker disse...

Boa idéia, vou começar a fazer isso.

Vetor é a mesma coisa que array. É que quando eu estudei basic, lá nos anos 80, não se usava a palavra array (que é um comando). O comando para definir array no basic era dim e os livros todos falavam em vetor. Array passou a ser usado só depois de 2000. Quando eu fiz a universidade também se estudava com o nome de vetor.

Unknown disse...

Tá ficando massa !

Eu ví na faculdade também como vetor, mas já conhecia o termo como array antes disso

Fabio Valentim disse...

Maneiro. Esse blog é um curso de criação de jogos?

Guilherme - Homebrew Maker disse...

Valentim, os tópicos com o assunto "Programação" são e programação de jogos para Nintendo DS (muitas idéias podem ser usadas em outras plataformas, mas o foco é em DS mesmo).

Anônimo disse...

Guilherme você já é praticamente meu ídolo! heheheh
Está sendo um ótimo tutorial e já estou conseguindo fazer umas coisas legais aqui!
Valeu mesmo!

Autor Almeida disse...

Fala Guilherme,

O que houve com o "curso"? Nao vai continuar? :)

Comprei um R4 só pra testar o que tenho feito :)

[]s e parabens pela iniciativa.

Anônimo disse...

Cara... excelente o blog!
Excelente as dicas!
Estou colocando em prática minha pouca habilidade em programação. E está tudo mt legal! Parabéns e por favor CONTINUEM!