quarta-feira, 2 de maio de 2007
Variáveis Globais (Continuando a Atirar)
Variáveis globais são aquelas que valem para todo o programa e não apenas para uma função. Como o C trabalha? Variáveis declaradas dentro de uma função são apenas daquela função. Variáveis declaradas fora de funções valem para todo o programa. Não é interessante usar só variáveis globais? Não. Variáveis globais consomem mais memória que variáveis locais, pois continuam ocupando o seu espaço na memória depois que a função termina de executar. Além disso, se eu mudar o valor de uma variável em uma função e esta variável estiver sendo usada em outra função, o valor na outra função também será alterado. Olhem o exemplo:
s16 i=0;
void func2() {
i++;
}
void func1 () {
for (i=0; i<40; i++) {
func2();
PA_OutputText(0,i>>1,'i=%d',i);
}
}
No exemplo acima, vai imprimir os números de 0 a 39 pulando de dois em dois, apesar do for em func1 mandar i++. Isso complica muito a interpretação e pode mascarar erros. Então vamos usar variáveis globais para aquilo que envolve todo o programa, como a posição do tanque, o ângulo do canhão, a posição do tiro, etc.
Para organizar melhor a função main, vamos usar funções para as diversas ações do jogo. Desta forma a função main contém o esqueleto principal, e o detalhamento de cada ação fica em uma função diferente. Olhem o fonte do programa como ficou:
// 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;
s16 velocidade=1;
s16 x=111;
s16 y=79;
s16 angulo_tiro=0;
s32 x_tiro=-8<<8;
s32 y_tiro=-8<<8;
s16 vel_tiro=4;
s8 atirando=0;
void giraturret() {
if (Stylus.Held) {
angulo_turret=PA_GetAngle(x+16,y+16,Stylus.X,Stylus.Y);
PA_SetRotsetNoZoom(0,0,angulo_turret);
}
}
void movetanque() {
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);
x+=velocidade*(Pad.Held.Right || Pad.Held.A)-velocidade*(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);
}
y+=velocidade*(Pad.Held.Down || Pad.Held.B)-velocidade*(Pad.Held.Up || Pad.Held.X);
}
if (x<0) { x=0; }
if (x>223) { x=223; }
if (y<0) { y=0; }
if (y>159) { y=159; }
PA_SetRotsetNoZoom(0,1,angulo_body);
PA_SetSpriteXY(0,0,x,y);
PA_SetSpriteXY(0,1,x,y);
}
}
void atira() {
if (atirando) {
x_tiro+=PA_Cos(angulo_tiro)*vel_tiro;
y_tiro-=PA_Sin(angulo_tiro)*vel_tiro;
PA_SetSpriteXY(0,2,x_tiro>>8,y_tiro>>8);
if (x_tiro<-8<<8 || x_tiro>260<<8 || y_tiro<-8<<8 || y_tiro>200<<8) {
atirando=0;
}
} else {
if (Pad.Newpress.L || Pad.Newpress.R) {
atirando=1;
angulo_tiro=angulo_turret;
x_tiro=(x+12)<<8;
y_tiro=(y+12)<<8;
x_tiro+=PA_Cos(angulo_tiro)*24;
y_tiro-=PA_Sin(angulo_tiro)*24;
PA_SetRotsetNoZoom(0,2,angulo_tiro);
PA_SetSpriteXY(0,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_Init16bitBg(0,0);
PA_Draw16bitRect(0,0,0,255,191,32768+10*1024+31*32+25);
PA_LoadSpritePal(0, 0, (void*)tank_turret_Pal);
PA_CreateSprite(0, 0, (void*)tank_turret_Sprite, OBJ_SIZE_32X32, 1, 0, x, y);
PA_LoadSpritePal(0, 1, (void*)tank_body_Pal);
PA_CreateSprite(0, 1, (void*)tank_body_Sprite, OBJ_SIZE_32X32, 1, 0, x, y);
PA_LoadSpritePal(0, 2, (void*)tiro_Pal);
PA_CreateSprite(0, 2, (void*)tiro_Sprite, OBJ_SIZE_8X8, 1, 0, -8, -8);
PA_SetSpriteRotEnable(0, 0, 0);
PA_SetSpriteRotEnable(0, 1, 1);
PA_SetSpriteRotEnable(0, 2, 2);
// Infinite loop to keep the program running
while (1)
{
giraturret();
movetanque();
atira();
PA_WaitForVBL();
}
return 0;
} // End of main()
O que o programa faz:
Bom, quase todo o programa já foi explicado nos últimos artigos. Somente a rotina de tiro foi alterada, para o tiro sair do canhão ao invés do centro da tela, e permitir que o canhão se mova enquanto o tiro também se move. A melhor maneira de fazer isso é substituir o loop dentro da rotina de tiro (que iria paralizar o tanque enquanto o tiro se movesse) por uma única alteração de posição. O loop passa a ser o principal do programa, o loop infinto da função main.
Na função atira, o programa verifica se foi disparado um tiro. Se existe algum tiro disparado, atualiza a posição dele. Se passar do limite da tela, encerra o disparo, permitindo que um novo disparo seja feito. Se o flag de tiro não está ativo (flag é uma variável que representa se algo aconteceu ou não), o programa verifica se foi pressionado o botão L ou o R. Se foi, inicia o tiro a 24 pixels de raio do centro do canhão aplicados seno e cosseno do ângulo do canhão. Isso vai assegurar que nenhum tiro saia de cima do tanque ou do meio do canhão. Outra coisa é que o programa fixa o ângulo do tiro igual ao do canhão no momento do disparo. Se o canhão trocar de ângulo depois de disparar, ele não influenciará o tiro.
A função main pode ser reduzida mais ainda, criando uma função "inicia", onde são colocadas todos aqueles comandos anteriores ao loop infinito.
Assinar:
Postar comentários (Atom)
Nenhum comentário:
Postar um comentário