
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()