Variáveis Globais (Continuando a Atirar)
Autor: Guilherme - Homebrew Maker | Publicado: 2007-05-02T20:00:00-07:00

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() {&nbsp;&nbsp;i++;}void func1 () {&nbsp;&nbsp;for (i=0; i&lt;40; i++) {&nbsp;&nbsp;&nbsp;&nbsp;func2();&nbsp;&nbsp;&nbsp;&nbsp;PA_OutputText(0,i&gt;&gt;1,'i=%d',i);&nbsp;&nbsp;}}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 &lt;PA9.h&gt;       // Include for PA_Lib#include &quot;gfx/all_gfx.c&quot;#include &quot;gfx/all_gfx.h&quot;// Variaveis Globaiss16 angulo_body=0;s16 angulo_turret=0;s16 velocidade=1;s16 x=111;s16 y=79;s16 angulo_tiro=0;s32 x_tiro=-8&lt;&lt;8;s32 y_tiro=-8&lt;&lt;8;s16 vel_tiro=4;s8 atirando=0;void giraturret() {&nbsp;&nbsp;if (Stylus.Held) {&nbsp;&nbsp;&nbsp;&nbsp;angulo_turret=PA_GetAngle(x+16,y+16,Stylus.X,Stylus.Y);&nbsp;&nbsp;&nbsp;&nbsp;PA_SetRotsetNoZoom(0,0,angulo_turret);&nbsp;&nbsp;}}void movetanque() {&nbsp;&nbsp;if ((Pad.Held.Left &amp;&amp; Pad.Held.A) || (Pad.Held.Right &amp;&amp; Pad.Held.Y) || (Pad.Held.Up &amp;&amp; Pad.Held.B) || (Pad.Held.Down &amp;&amp; Pad.Held.X) || (Pad.Held.X &amp;&amp; Pad.Held.B) || (Pad.Held.Y &amp;&amp; Pad.Held.A)) {&nbsp;&nbsp;  //o usu&aacute;rio est&aacute; sacaneando, n&atilde;o executa nada&nbsp;&nbsp;} 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)) {&nbsp;&nbsp;  //nenhuma dire&ccedil;&atilde;o foi pressionada&nbsp;&nbsp;} else {&nbsp;&nbsp;&nbsp;&nbsp;if ((Pad.Held.Left || Pad.Held.Y || Pad.Held.Right || Pad.Held.A) &amp;&amp; !(Pad.Held.Up || Pad.Held.X || Pad.Held.Down || Pad.Held.B)) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;angulo_body=256*(Pad.Held.Left || Pad.Held.Y);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x+=velocidade*(Pad.Held.Right || Pad.Held.A)-velocidade*(Pad.Held.Left || Pad.Held.Y);&nbsp;&nbsp;&nbsp;&nbsp;} else if (!(Pad.Held.Left || Pad.Held.Y || Pad.Held.Right || Pad.Held.A) &amp;&amp; (Pad.Held.Up || Pad.Held.X || Pad.Held.Down || Pad.Held.B)) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;angulo_body=128*(Pad.Held.Up || Pad.Held.X)+384*(Pad.Held.Down || Pad.Held.B);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;y+=velocidade*(Pad.Held.Down || Pad.Held.B)-velocidade*(Pad.Held.Up || Pad.Held.X);&nbsp;&nbsp;&nbsp;&nbsp;} else {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;angulo_body=256*(Pad.Held.Left || Pad.Held.Y);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x+=velocidade*(Pad.Held.Right || Pad.Held.A)-velocidade*(Pad.Held.Left || Pad.Held.Y);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (angulo_body) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;angulo_body=192*(Pad.Held.Up || Pad.Held.X)+320*(Pad.Held.Down || Pad.Held.B);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} else {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;angulo_body=64*(Pad.Held.Up || Pad.Held.X)+448*(Pad.Held.Down || Pad.Held.B);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;y+=velocidade*(Pad.Held.Down || Pad.Held.B)-velocidade*(Pad.Held.Up || Pad.Held.X);&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;if (x&lt;0) { x=0; }&nbsp;&nbsp;&nbsp;&nbsp;if (x&gt;223) { x=223; }&nbsp;&nbsp;&nbsp;&nbsp;if (y&lt;0) { y=0; }&nbsp;&nbsp;&nbsp;&nbsp;if (y&gt;159) { y=159; }&nbsp;&nbsp;&nbsp;&nbsp;PA_SetRotsetNoZoom(0,1,angulo_body);&nbsp;&nbsp;&nbsp;&nbsp;PA_SetSpriteXY(0,0,x,y);&nbsp;&nbsp;&nbsp;&nbsp;PA_SetSpriteXY(0,1,x,y);&nbsp;&nbsp;}}void atira() {&nbsp;&nbsp;if (atirando) {&nbsp;&nbsp;&nbsp;&nbsp;x_tiro+=PA_Cos(angulo_tiro)*vel_tiro;&nbsp;&nbsp;&nbsp;&nbsp;y_tiro-=PA_Sin(angulo_tiro)*vel_tiro;&nbsp;&nbsp;&nbsp;&nbsp;PA_SetSpriteXY(0,2,x_tiro&gt;&gt;8,y_tiro&gt;&gt;8);&nbsp;&nbsp;&nbsp;&nbsp;if (x_tiro&lt;-8&lt;&lt;8 || x_tiro&gt;260&lt;&lt;8 || y_tiro&lt;-8&lt;&lt;8 || y_tiro&gt;200&lt;&lt;8) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;atirando=0;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;} else {&nbsp;&nbsp;&nbsp;&nbsp;if (Pad.Newpress.L || Pad.Newpress.R) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;atirando=1;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;angulo_tiro=angulo_turret;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x_tiro=(x+12)&lt;&lt;8;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;y_tiro=(y+12)&lt;&lt;8;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x_tiro+=PA_Cos(angulo_tiro)*24;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;y_tiro-=PA_Sin(angulo_tiro)*24;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PA_SetRotsetNoZoom(0,2,angulo_tiro);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PA_SetSpriteXY(0,2,x_tiro&gt;&gt;8,y_tiro&gt;&gt;8);&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;}}// Function: main()int main(int argc, char ** argv){&nbsp;&nbsp;&nbsp;&nbsp;PA_Init();    // Initializes PA_Lib&nbsp;&nbsp;PA_InitVBL(); // Initializes a standard VBL&nbsp;&nbsp;PA_Init16bitBg(0,0);&nbsp;&nbsp;PA_Draw16bitRect(0,0,0,255,191,32768+10*1024+31*32+25);&nbsp;&nbsp;PA_LoadSpritePal(0, 0, (void*)tank_turret_Pal);&nbsp;&nbsp;PA_CreateSprite(0, 0, (void*)tank_turret_Sprite, OBJ_SIZE_32X32, 1, 0, x, y);&nbsp;&nbsp;PA_LoadSpritePal(0, 1, (void*)tank_body_Pal);&nbsp;&nbsp;PA_CreateSprite(0, 1, (void*)tank_body_Sprite, OBJ_SIZE_32X32, 1, 0, x, y);&nbsp;&nbsp;PA_LoadSpritePal(0, 2, (void*)tiro_Pal);&nbsp;&nbsp;PA_CreateSprite(0, 2, (void*)tiro_Sprite, OBJ_SIZE_8X8, 1, 0, -8, -8);&nbsp;&nbsp;PA_SetSpriteRotEnable(0, 0, 0);&nbsp;&nbsp;PA_SetSpriteRotEnable(0, 1, 1);&nbsp;&nbsp;PA_SetSpriteRotEnable(0, 2, 2);&nbsp;&nbsp;&nbsp;&nbsp;// Infinite loop to keep the program running&nbsp;&nbsp;while (1)&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;giraturret();&nbsp;&nbsp;&nbsp;&nbsp;movetanque();&nbsp;&nbsp;&nbsp;&nbsp;atira();&nbsp;&nbsp;&nbsp;&nbsp;PA_WaitForVBL();&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;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.