64
3. PROGRAMAÇÃO: PARTE I Nessa seção vamos fazer um estudo dos comandos de controle do Blitz3D, aqueles que permitem que os programas tomem decisões e assim consigam “raciocinar”, isso é, criar inteligência artificial. Para fazer isso, usamos comandos condicionais, operadores relacionais, operadores lógicos, operadores matemáticos e comandos de repetições. Vamos abordar um de cada vez. Também vamos abordar a principal estrutura de organização de código: as funções. Nosso objetivo nesse tópico, e no tópico posterior, é dar fundamentos de programação para que, a partir do modulo 5 iniciemos a pratica de desenvolvimento de jogos. Operadores matemáticos Os operadores matemáticos são utilizados para efetuar cálculos matemáticos no ambiente de programação. O objetivo aqui não é estudar todos os operadores e funções matemáticas que existem, mas apenas aqueles que são mais utilizados em jogos. Em um jogo, a matemática é usada, por exemplo, para calcular quantos tiros um jogador ainda tem para disparar, para traçar rotas de tiros em parábolas ou até mesmo para gerenciar a física de jogos de carros ou aviões. Você vai ter que se acostumar a ela para criar bons jogos. Soma Podemos efetuar somas com o Blitz3D por meio do operador de soma que é representado pelo sinal de mais “+”. O resultado da soma deve ser guardado em uma variável, senão se perderá. Exemplo 1 soma = 1 + 1 Print soma WaitKey O exemplo acima mostra um caso simples de soma. Somamos duas unidades e colocamos o resultado dentro da variável soma. A seguir, damos um exemplo de soma de duas variáveis onde a resistência total do heroi é dada pela resistência do escudo mais a resistência da armadura. Exemplo 2 armadura = 3 escudo = 4 Heroi_resistencia = armadura + escudo Print "Heroi Resistencia: " + Heroi_resistencia WaitKey Subtração A subtração é realizada por meio do símbolo “-“. Da mesma forma que ocorre com a operação de soma, é necessário armazenar o resultado dentro de uma variável para não perder o mesmo. No exemplo abaixo um inimigo desfere um golpe contra nosso Herói. O golpe primeiro será amenizado pela resistência do Herói e só depois irá gerar o dano, pois o 1

Modulo 2

Embed Size (px)

Citation preview

Page 1: Modulo 2

3. PROGRAMAÇÃO: PARTE I

Nessa seção vamos fazer um estudo dos comandos de controle do Blitz3D, aqueles que permitem que os programas tomem decisões e assim consigam “raciocinar”, isso é, criar inteligência artificial. Para fazer isso, usamos comandos condicionais, operadores relacionais, operadores lógicos, operadores matemáticos e comandos de repetições. Vamos abordar um de cada vez.Também vamos abordar a principal estrutura de organização de código: as funções. Nosso objetivo nesse tópico, e no tópico posterior, é dar fundamentos de programação para que, a partir do modulo 5 iniciemos a pratica de desenvolvimento de jogos.

Operadores matemáticosOs operadores matemáticos são utilizados para efetuar cálculos matemáticos no ambiente de programação. O objetivo aqui não é estudar todos os operadores e funções matemáticas que existem, mas apenas aqueles que são mais utilizados em jogos.Em um jogo, a matemática é usada, por exemplo, para calcular quantos tiros um jogador ainda tem para disparar, para traçar rotas de tiros em parábolas ou até mesmo para gerenciar a física de jogos de carros ou aviões. Você vai ter que se acostumar a ela para criar bons jogos.

SomaPodemos efetuar somas com o Blitz3D por meio do operador de soma que é representado pelo sinal de mais “+”. O resultado da soma deve ser guardado em uma variável, senão se perderá.

Exemplo 1soma = 1 + 1Print somaWaitKey

O exemplo acima mostra um caso simples de soma. Somamos duas unidades e colocamos o resultado dentro da variável soma. A seguir, damos um exemplo de soma de duas variáveis onde a resistência total do heroi é dada pela

resistência do escudo mais a resistência da armadura.Exemplo 2armadura = 3escudo = 4Heroi_resistencia = armadura + escudoPrint "Heroi Resistencia: " + Heroi_resistenciaWaitKey

SubtraçãoA subtração é realizada por meio do símbolo “-“. Da mesma forma que ocorre com a operação de soma, é necessário armazenar o resultado dentro de uma variável para não perder o mesmo. No exemplo abaixo um inimigo desfere um golpe contra nosso Herói. O golpe primeiro será amenizado pela resistência do Herói e só depois irá gerar o dano, pois o dano real recebido é igual a força do golpe, menos a força do sistema de defesa.

Exemplo 3golpe = 10Heroi_resistencia = 5Heroi_vida = 100golpe = golpe - Heroi_resistenciaPrint "Golpe amenizado pela resistencia: " + golpeHeroi_vida = Heroi_vida - golpePrint "Vida Depois do Golpe: " + Heroi_vidaWaitKey

MultiplicaçãoA multiplicação é implementada por meio do caractere estrela “*”.

Exemplo 4mult = 2 * 2 Print multWaitKey

Um exemplo um pouco mais complexo de multiplicação. Como prêmio por fazer um favor a um feiticeiro, ele nos deu uma espada mágica que aumenta nossa força em 20%. Para indicar essa influência sobre o nosso herói, vamos multiplicar a força real dele por 1.2, assim teremos os 20%.

1

Page 2: Modulo 2

Exemplo 5Espada_forca# = 1.2Heroi_forca# = 50Print "Forca atual do heroi: " + Heroi_forca# Heroi_forca# = Heroi_forca# * Espada_forca#Print "Forca do heroi com espada: " + Heroi_forca# WaitKey

DivisãoO cálculo de divisão é realizado no Blitz3D por meio do operador barra “/”.

Exemplo 6div = 21 / 3Print "Resultado da divisão: " + divWaitKey

Nosso herói e seus dois companheiro chegaram em uma encruzilhada. Existem três caminhos a seguir e cada um deve ir por um lado diferente. Para que nenhum deles mora de fome ou por necessidades pelo caminho, nosso heroi vai ter que repartir o alimento e o dinheiro com os dois colegas.

Exemplo 7Qtd_pessoas = 3Heroi_conta = 1200Heroi_alimento = 300Heroi_conta = Heroi_conta / Qtd_pessoasHeroi_alimento = Heroi_alimento / Qtd_pessoasPrint "Alimento atual: " + Heroi_alimentoPrint "Dinheiro atual: " + Heroi_contaWaitkey

RestoO Operador “Mod” é responsável por retornar o resto de uma operação de divisão. Quando dividimos um número por outro, às vezes a divisão é inteira e não sobra resto, por exemplo (4/2=2) onde o resto é 0. Mas, se dividirmos (5/2=2) o resto é 1. Esse 1 de resto é perdido se o sistema não o capturar de alguma forma. A forma de capturar o resto de uma divisão é usar o operador Mod. O cálculo de resto é muito importante para o gerenciamento de mapas complexos e criação de sistemas infinitos, mas isso será estudado mais à frente. O importante aqui é você aprender a como fazer essa operação.

No exemplo anterior, de divisão de alimento e dinheiro, imagine que tivéssemos um valor que não daria uma divisão perfeita, isso é, sobrasse algumas moedas. Pelo calculo que fizemos acima isso se perderia, pois o Blitz3D ia arredondar o cálculo.Venha no exemplo abaixo que temos uma quantidade de dinheiro que não pode ser dividida com perfeição por 3, mas agora usamos o operador Mod para pegar o resto da operação. Assim, o nosso herói poderá ficar com o que sobrar da divisão, afinal, não dá para dividir moedas de ouro ao meio.

Exemplo 8Heroi_conta = 1202Qtd_pessoas = 3resultado = Heroi_conta / Qtd_pessoasPrint "Resultado da divisão: " + resultadoresto = Heroi_conta Mod Qtd_pessoasPrint "O resto da divisão: " + restoHeroi_conta = resultado + restoPrint "A conta do heroi tem: " + Heroi_contaWaitKey

Perceba que, para obtermos o resto de uma operação de divisão, basta montarmos a mesma operação e substituir a barra de divisão “/” por “Mod”.

Raiz quadradaA raiz quadrada de um número é um numero que, se multiplicado por ele mesmo gera o primeiro. Assim, a raiz quadrada de 4 é 2, pois “2 x 2 = 4”. A raiz de 9 é 3, pois “3 x 3 = 9”. Para descobrirmos a raiz quadrada de um número usamos a função [Sqr( )]. A operação de raiz quadrada é muito importante para física de jogos, cálculos de força e colisões matemáticas.

Exemplo 9Print "A raiz de 9 é: " + Sqr(9)Print "A raiz de 3 é: " + Sqr(3)WaitKey

Para demonstrar algumas das funcionalidades da raiz quadrada, abaixo criamos um exemplo do uso dela para calcular a distância entre dois pontos. Uma funcionalidade como essa é muito importante para inteligência artificial de unidades

2

Page 3: Modulo 2

inimigas controladas pelo computador. Elas normalmente atacam a partir do momento que o personagem chegue a uma certa distância de um soldado inimigo, sua área de visão.

Exemplo 10Graphics 400,300SeedRnd MilliSecs()AX = Rnd(50, 350)AY = Rnd(50, 250)BX = Rnd(50, 350)BY = Rnd(50, 250)Color 255,0,0Plot AX, AYPlot BX, BYxd = BX - AXyd = BY - AY DISTANCIA = Sqr((xd^2) + (yd^2))Print "A DISTANCIA ENTRE OS DOIS É: " + DISTANCIAWaitKey

Nessa aplicação usamos o comando Rnd() para gerar posições aleatórias, ele é semeado pelo comando SeedRnd MilliSecs() que dessa forma garante que os valores sorteados serão sempre diferentes.Os numeros que estão dentro de Rnd limitam o escopo do sorteio, assim Rnd(50,250) se limita a sortear um numero entre 50 e 250. SeedRnd MilliSecs()AX = Rnd(50, 350)AY = Rnd(50, 250)BX = Rnd(50, 350)BY = Rnd(50, 250)

Também usamos o comando Color para mudar a cor da paleta de desenho; esse comando recebe saturações red, green e blue, assim, o comando Color 255,0,0 deixa a paleta de desenho na cor vermelha (red=255).Color 255,0,0

Para exibir os pontos usamos o comando Plot que exibe um ponto de 1 pixel na tela. Esse comando precisa de apenas dois parâmetros para funcionar, a posição x e a posição y em que deverá ser exibido.Plot AX, AYPlot BX, BYO operador "^" é de exponenciação, isso é diz quantas vezes um número é multiplicado por ele mesmo. Dessa forma "x^2" é o mesmo que "x*x"

e "x^3" é o mesmo que "x*x*x". Assim, a distância dos dois pontos é igual à raiz quadrada (Sqr) da soma do quadrado das distâncias x e y.xd = BX - AXyd = BY - AY DISTANCIA = Sqr((xd^2) + (yd^2))

O condicional IfO operador de condição If é com certeza o mais importante comando de controle que existe em qualquer linguagem de programação. Esse operador testa uma condição e se essa condição for verdadeira, ele executa um comando ou um bloco de comandos. Veja alguns exemplos lógicos cotidianos de usos de comando condicional If. Lembre-se, If significa "Se" e Then significa "Então"Em jogos:

Lógica: Se apertar a tecla barra de espaço, dispare um tiro.Código: If KeyHit(57) Then Disparar()

Lógica: Se a vida for igual a 0 então o jogador deve morrer.Código: If vida=0 Then Morrer()

Lógica: Se nosso herói levar um tiro, isso é, se ele colidir com um tiro, perde 10 de vida.Código: If Colide(heroi, tiro) Then vida = vida - 10Em programas em geral:

Lógica: Um terminal dispenser de um banco só libera o dinheiro se o saldo for maior que o valor solicitado para o saque.Código: If Saldo > Saque Then Sacar(Saque)

Lógica: Uma venda em uma loja virtual só será possível se existirem produtos no estoque, isso é, se o estoque é maior que zero.Código: If Produto_estoque > 0 Then Produto_vender()Como você pode ver, realmente o condicional If é a base de praticamente todo tipo de decisão que um programa de computador toma. É por meio dele que fazemos a máquina "pensar". Assim, podemos dizer que ele também é o principal comando de interação entre o homem e a máquina e jogos são essencialmente interação.

3

Page 4: Modulo 2

Como o If funciona?O condicional If só compreende dois tipos de dados Verdadeiro (true) ou Falso (false). Para ele, o número 0 (zero) significa falso, e todo número diferente de zero é verdadeiro, assim 1 é verdadeiro, -50 é verdadeiro e assim por diante.Veja no exemplo abaixo que colocamos o zero como condição de teste para o if, e por isso o comando não executa, pois zero é falso.

Exemplo 11If 0 Then Print "passou no teste"Print "Fim" WaitKeyAgora vamos substituir o zero por outro numero qualquer e o comando vai executar, pois qualquer número diferente de zero é verdadeiro.

Exemplo 12If 999 Then Print "passou no teste"Print "Fim" WaitKeyO que devemos entender é que o Blitz3D troca a condição por verdadeiro ou falso. Quando ele encontra o número zero, troca esse por falso e se encontra outro número, troca o mesmo por verdadeiro.Assim se dá também com as demais condições, como verificações de comparação ou funções. A maioria das funções sempre retornam um valor e é por meio desse valor que o blitz3d faz a verificação de falso e verdadeiro. No caso de comparações é mais simples, pois comparações sempre retornam verdadeiro ou falso.veja algumas comparações:If (1 = 1) -> verdadeiroIf (2 = 1) -> falsox = 5If (x=5) -> verdadeiro If (x=x) -> verdadeiroIf (x=3) -> falsoIf (x=y) -> falso (como y não foi declarado, ele é igual a zero).No exemplo abaixo provamos a tese acima. Declaramos a variável "a=1" e fazemos testes. Veja que só o primeiro teste é concluído com sucesso e assim, só o primeiro comando Print é executado.

Exemplo 13

a = 1If (a=1) Then Print "A passou no teste (a=1)"If (a=2) Then Print "A passou no teste (a=2)"WaitKeyPara manter a compatibilidade com a sintaxe da linguagem Basic, Mark Sibly manteve o comando auxiliar Then para o If. Ele é muito útil para iniciantes, pois ele separa visualmente a condição do comando a ser executado criando a sequência lógica do tipo:If condição Then comandoMas ele não é obrigatório e seus algoritmos vão funcionar normalmente sem o uso dele. Veja o exemplo anterior feito sem o uso dos mesmos.

Exemplo 14a = 1If (a=1) Print "A passou no teste (a=1)"If (a=2) Print "A passou no teste (a=2)"WaitKeyOs parênteses também não são obrigatórios na seção de teste de condição, mas aconselho usar os mesmos para não misturar visualmente uma coisa com outra. Quanto mais legível um programa, menos demorada e menos cansativa será sua compreensão e manutenção.

Primeira aplicação com decisão Aprendemos a fazer o computador tomar decisões e interagir com o usuário por meio do comando If. Nessa aplicação simples, o usuário poderá escolher o que o computador irá fazer por ele, e o computador vai obedecer.

Exemplo 15Graphics 640, 480Text 10, 10, "********************** PC OBEDIENTE *************************"Text 10, 50, "[1] Ordene ao computador para Puxar o saco do Mark Sibly"Text 10, 80, "[2] Pegunte a ele sobre a UniDev"Text 10, 110, "[3] Mande ele dizer qual é o melhor programa para jogos"Locate 10, 150 opt = Input$(" DIGITE O NUMERO DA OPÇÃO E APERTE ENTER: ")ClsText 10, 10, "********************** PC OBEDIENTE *************************"If (opt = 1) Text 10, 50, "O Mark é o Melhor arquiteto e programador do mundo!"

4

Page 5: Modulo 2

If (opt = 2) Text 10, 50, "A UniDev é The Best, só aqui vc encontra tudo sobre jogos!" If (opt = 3) Text 10, 50, "Isso é fácil, O BLITZ3D!" If (opt < 1 Or opt > 3) Text 10, 50, "Opção inválida!" WaitKeyNa primeira linha configuramos o tamanho da tela:Graphics 640, 480Nas linhas subseqüentes apenas exibimos mensagens na tela:Text 10, 10, "********************** PC OBEDIENTE *************************"Text 10, 50, "[1] Ordene ao computador para Puxar o saco do Mark Sibly"Text 10, 80, "[2] Pegunte a ele sobre a UniDev"Text 10, 110, "[3] Mande ele dizer qual é o melhor programa para jogos"Com o comando Locate, posicionamos o curso na posição "10, 150" para a partir dali executarmos a entrada de dados via teclado pelo comando Input.Locate 10, 150 opt = Input$(" DIGITE O NUMERO DA OPÇÃO E APERTE ENTER: ")Usamos o comando Cls para limpar a tela das exibições anteriores.ClsDai então fazemos as novas exibições a partir da decisão tomada. Se escolhemos a opção 1, a segunda linha será exibida; se escolhemos 2, a terceira linha será exibida; caso nossa opção foi 3, a última linha é monstrada. A primeira linha sempre será exibida e caso tenhamos digitado um valor desconhecido, (se a opção é menor que 1 ou a opção é maior que 3) é exibida uma mensagem de aviso. Text 10, 10, "********************** PC OBEDIENTE *************************"If (opt = 1) Text 10, 50, "O Mark é o Melhor arquiteto e programador do mundo!" If (opt = 2) Text 10, 50, "A UniDev é The Best, só aqui vc encontra tudo sobre jogos!" If (opt = 3) Text 10, 50, "Isso é fácil, O BLITZ3D!" If (opt < 1 Or opt > 3) Text 10, 50, "Opção inválida!"

Meu primeiro JogoAgora que você já teve uma breve introdução ao condicional if, já é capaz de fazer o computador raciocinar e assim, já pode criar alguns tipos de jogos. Nosso primeiro jogo vai ser bem simples, será um jogo de adivinhação. O Blitz3D vai sortear um número e você terá que adivinhar qual é.

Exemplo 16Graphics 640, 480Text 100, 100, "******************************************************"Text 100, 112, "********************* ADIVINHA ***********************"Text 100, 124, "******************************************************"Text 100, 160, "O Blitz3D sorteou um número de 1 a 5, adivinha qual é."Locate 130, 190opt = Input$(" Sua tentativa é: ")SeedRnd MilliSecs()numero = Rnd(1,5)ClsColor 0,255,0If opt = numero Text 250, 150, "Você é O CARA!"If opt <> numero Text 280, 150, "ERROU!"WaitKeyVeja que nesse exemplo usamos o gerador de números aleatórios Rnd(1,5) para sortear um número de 1 a 5. Ele é semeado pelo comando SeedRnd para que a cada vez que o programa for executado dê números diferentes.SeedRnd MilliSecs()numero = Rnd(1,5)O resultado é exibido em cor verde, para isso usamos o comando Color para mudar a cor da paleta de desenho.Color 0,255,0Se o número escolhido é o mesmo que o sorteado, você vence, caso contrário, perde. O operador "<>" significa diferente.If opt = numero Text 250, 150, "Você é O CARA!"If opt <> numero Text 280, 150, "ERROU!"

Operadores relacionaisOs operadores relacionais têm a função de efetuar comparações entre dois valores ou variáveis. Eles são muito importante para oferecer suporte aos comandos condicionais, pois são eles que fazem as principais comparações para os condicionais poderem tomar decisões.Os principais operadores relacionais são: de igualdade, maior, menor, maior ou igual, menor ou igual e diferente.

Operador de igualdade

5

Page 6: Modulo 2

O operador de igualdade no Blitz3D é o mesmo que o operador de atribuição “=”. A função desse operador é comparar dois valores ou variáveis para verificar se seus valores são idênticos.Veja um caso onde o jogador possui uma chave e, para passar de fase, precisa passar por uma porta trancada. Verificamos se a chave da porta é a mesma chave que o jogador possui e caso isso seja verdadeiro, a porta se abre.

Exemplo 17MinhaChave$ = "branca"ChaveDaPorta$ = "branca"If (MinhaChave$=ChaveDaPorta$) Print "Pode Entrar"WaitKey

Operador maior queO operador que implementa a relação “maior que” é o operador “>”. Com esse operador podemos testar se um valor ou variável é maior que outro valor ou variável.Exemplos comuns de uso do operador maior que, é para controle de vidas, vitalidade e tiros. Para dar um tiro, sua munição deve ser "maior que zero". Se sua vitalidade não for maior que zero, você morre. Se o seu número de vidas não for maior que zero, o jogo acaba.

Exemplo 18tiros = 100If (tiros>0) Print "ATIRANDO!"WaitKeyBom, é lógico que em um jogo a coisa é bem mais complexa. Para dar um tiro, primeiro verificamos se existe munição disponível, efetuamos o disparo e diminuímos em um a quantidade de munição.O programa abaixo dispara tiros aleatórios na tela, enquanto houver balas.

Exemplo 19Graphics 400, 300tiros = 10Text 10, 10, "Aperte Space para disparar!" Color 255, 0, 0While (tiros > 0)SeedRnd MilliSecs()x = Rnd(10,390)y = Rnd(10,290) If KeyHit(57) Plot x,y : tiros = tiros - 1

WendClsColor 255, 255, 255Print "Acabaram os tiros"WaitKeyA primeira coisa que fizemos foi configura o tamanho da tela para 400 por 300.Graphics 400, 300Definimos a quantidade de tiros iniciais como 10.tiros = 10Aqui eximimos uma mensagem e logo após mudamos a cor da paleta de desenho para vermelho, assim os tiros serão vermelhos.Text 10, 10, "Aperte Space para disparar!" Color 255, 0, 0Estamos criando um loop do tipo While-Wend. Tudo que estiver entre o While e o Wend vai executar de forma indefinida enquanto a condição do While for verdadeira. A condição que colocamos foi (tiros > 0) o que quer dizer, "enquanto tivermos tiros disponíveis", isso é, enquanto a variável tiros for maior que zero, o loop executará e todos os comandos dentro dele também.While (tiros > 0)Toda vez que o loop fizer um ciclo, sorteamos uma posição x e y para onde o tiro deverá ser disparado.SeedRnd MilliSecs()x = Rnd(10,390)y = Rnd(10,290) Mas o tiro só será disparado se a tecla barra de espaços for pressionada (tecla 57). O comando KeyHit verifica se uma tecla foi pressionada; retorna verdadeiro caso tenha sido e falso caso não tenha sido pressionada. Veja que aproveitamos a mesma linha, a mesma condição If para efetuar duas operações. Primeiro exibimos o tiro, Plot x,y ; depois debitamos o tiro disparado do nosso estoque de munição tiros = tiros - 1. Isso foi possível graças ao operador ":" que possibilita colocarmos dois ou mais comandos em uma mesma linha.If KeyHit(57) Plot x,y : tiros = tiros - 1Depois de dispararmos o décimo tiro, a variável tiros ficará igual a zero e a condição para o loop While (tiros > 0) não mais será satisfeita. Assim sairemos do loop e o processamento seguirá pelas linhas abaixo. A tela será limpa Cls, mudamos a cor da paleta de desenho para branca

6

Page 7: Modulo 2

novamente Color 255, 255, 255, e por fim anunciamos que acabaram os tiros.ClsColor 255, 255, 255Print "Acabaram os tiros"

Operador menor queVerifica se uma variável ou valor é menor que outro. É implementado pelo operador “<”. O uso desse operador é semelhante ao anterior, por isso dispensa maiores detalhes. Podemos por exemplo, verificar se a vida é menor que 1, ao invés de verificar se é igual a zero.

Exemplo 20vida = 10If (vida<1) Print "To morto!"If (vida>0) Print "To vivo!"WaitKey

Operador diferente deO operador “diferente de” verifica se duas variáveis ou valores são diferentes. Ele é implementado no Blitz3D por meio do operando “<>”. O operador diferente tem uma função muito importante; enquanto o operador de igualdade é totalmente exclusivo, isso é, realiza algo se somente um único caso for verdadeiro, ele faz o contrário, somente um caso não preenche sua satisfação.Isso em lógica se daria assim:Igualdade -> somente uma chave abre uma portaDiferença -> todos podem tomar leite, menos os intolerantes á lactose.Existem diversas situações em jogos que necessitaríamos desse operador. Imagine por exemplo que você criou um jogo onde existem diversos tipos de seres e eles possuem diversos tipos de poderes. Alguns seres tem imunidade sobre alguns poderes. Por exemplo, um Paladino, por ser um cavaleiro sagrado, ele possui a benção de Deus e por isso os ataques de magia negra não o afetarão. Assim para que um ataque de magia negra provoque dano, o tipo da unidade deve ser diferente de paladino.

Exemplo 21UnidadeTipo$ = "Elfo"UnidadeVida = 100

AtaqueMagiaNegra = 33If (UnidadeTipo$<>"Paladino") UnidadeVida = UnidadeVida - AtaqueMagiaNegraPrint "Vida depois do Ataque de Magia Negra: " + UnidadeVidaWaitKeyFaça um teste, mude o tipo de unidade para "Paladino" e verifique que ele não sofrerá o ataque.

Operador maior ou igualEsse operador é responsável por verificar se um valor ou variável é maior ou igual a outro. Ambas as situações dão resultado positivo: tanto se for maior como se for igual. Ele é implementado por “>=”.

Exemplo 22a = 3If (a>=2) Print "A é maior que 2"If (a>=3) Print "A é igual a 3"WaitKey

Operador menor ou igualSemelhante ao “maior ou igual”, mas nesse caso o teste é se é menor ou igual. Sua implementação se dá por “<=”. Os bancos ficam atentos a essa situação. Se o seu crédito acabou ou está negativo. Nos dois casos, se for igual a zero ou se for menor que zero, será enviada uma notificação.

Exemplo 23ClienteA_saldo = -100ClienteB_saldo = 0If (ClienteA_saldo<=0) Print "Notificar falta de crédito para Cliente A"If (ClienteB_saldo<=0) Print "Notificar falta de crédito para Cliente B"WaitKey

CONDICIONAISVamos fazer agora um estudo mais profundo sobre condicionais. Veremos as outras formas com que o condicional “If” pode se compor para gerar estruturas mais complexas e eficazes de seleção. Também vamos fazer um estudo sobre o condicional “Select Case”.

Outras faces do If

7

Page 8: Modulo 2

O comando condicional If também pode ser usado para controlar um bloco de código que só será executado se esse condicional for verdadeiro. Para isso usamos esse comando junto ao seu auxiliar “EndIf”. O comando “If” marca o início do bloco de código condicional e o “EndIf” marca o seu termino. Assim, tudo que estiver entre os dois comandos vai ser executado se a condição for verdadeira.No exemplo abaixo, o jogador perdeu todas as suas vidas, portanto o jogo irá ser encerrado. Encerrar um jogo ativa uma série de coisas, como, exibir mensagem de saída, gravar escores, exibir status final, etc. Tudo pode se ativado por apenas um condicional If em conjunto com o comando EndIf para criar um bloco de código condicional. Para exemplificar isso usamos comandos Print que exibem mensagens desses passos lógicos, mas em um jogo chamaríamos funções para concretizar esses passos.

Exemplo 24vidas = 0If vidas=0Print "Você está morto"Print "Gravando Scores"Print "Encerrando a aplicação"EndIfWaitKeyOutra estrutura lógica que podemos compor com o If é de construir uma situação onde é executado um comando, ou bloco de código, se a verificação for verdadeira e outro comando, ou bloco de código, se a condição for falsa. Para isso fazemos uso do seu auxiliar “Else”, que significa “caso contrário”.Abaixo, uma porta só vai se abrir se tivermos a chave (Chave1 = 1), caso contrário, será exibida uma mensagem de que precisamos pegar a chave.

Exemplo 25Chave1 = 0If Chave1 = 1Print "Abrindo a Porta!" ElsePrint "Você precisa pegar a chave!"EndIfWaitKey

No exemplo acima, mude “Chave1 = 0” para “Chave1 = 1” e veja o resultado.

If aninhadoÉ possível compor estruturas lógicas ainda mais complexas por meio de vários comandos If aninhados. Aninhar um If é colocar um dentro do outro, podendo, assim, serem feitas seleções mais precisas.Imagine uma situação em um jogo em que para passar de fase são necessárias duas condições. A primeira delas é que o monstro tenha sido derrotado e a segunda é que você tenha a chave para abrir a porta.

Exemplo 26monstro1$ = "ok"chave1$ = "ok"If monstro1$ = "ok"If chave1$ = "ok"Print "Passou de Fase!"ElsePrint "Falta Pegar a Chave!"EndIfElsePrint "volte e mate o monstro!"EndIfWaitKeyTeste 1: mude chave1$ = "ok" para chave1$ = "" e veja o resultado.Teste 2: mude monstro1$ = "ok" para monstro1$ = "" e veja o resultado.

Cuidado com o uso excessivo ou incorreto de If. Devem ser evitados testes desnecessários, e criação de muitos Ifs aninhados pois além de deixar o código mais lento, pode ser mais propício a erros. A condição com maior propensão a ser verdadeira deve ser a primeira a ser testada para maximizar a performance de seu algoritmo. Procure sempre a solução mais simples.

Select CaseEsse comando de seleção é excelente para evitar uma série de Ifs, ou Ifs aninhados. Ele deixa sua estrutura mais compreensível e o algoritmo mais rápido. O Comando Select registra qual variável ou

8

Page 9: Modulo 2

condição deverá ser testada. O comando Case testa o conteúdo da variável para ver se é igual ao seu caso. Assim, a estrutura Select-Case se dá assim.Select variável a ser testadaCase valor testado

Comando A grande vantagem desse comando é poder testar vários casos (case) para uma mesma variável e só será executado o case que for verdadeiro, isso é, o case que seu valor corresponder ao valor atual da variável testada. Esse comando é muito importante para criar programação genérica, isso é, um mesmo código que seja executado por diversas entidades diferentes ou possa ser usado em diversas situações. Imagine um jogo que seja disponível para o usuário escolher jogar com um dos 5 tipos de seres diferentes: soldado, mago, bruxo, elfo e ogro. Como eles possuem muita coisa em comum, não seria prudente criar uma função para cada um, ou um classe para cada um deles. Criamos, por exemplo uma só função de ataque, e por meio do Select Case, diferimos o ataque correto para cada entidade. Veja abaixo.

Exemplo 27Heroy_Tipo$ = "Ogro"Select Heroy_Tipo$Case "Soldado"Print "Disparando Tiro"Case "Mago"Print "Usando ataque de fogo"Case "Bruxo"Print "Invocando Devil Summon"Case "Elfo"Print "Disparando Flexa"Case "Ogro"Print "Golpe com Clava"End SelectWaitKeyMude a linha que define o tipo de unidade atual Heroy_Tipo$ = "Ogro" e defina o heroi como "Soldado", "Mago", "Bruxo" ou "Elfo". Veja que com o auxílio dessa estrutura, cada unidade dispara o seu golpe específico.Como podemos ver, a estrutura Select Case se inicia com Select e termina em End Select. Esse

último comando define onde termina a estrutura de seleção.Além de disparar eventos para cada caso em particular, o Select Case também é capaz de suportar uma condição padrão. Caso nenhum dos case sejam validados, o comando padrão executa. Para isso usamos o Default.

Exemplo 28Pessoa_Tipo$ = "Normal"Select Pessoa_Tipo$Case "Vegetariano"Print "Não Come Carne"Case "Judeo"Print "Não come carne de porco"Default Print "Come de Tudo"End SelectWaitKeyO exemplo acima demonstra com perfeição a potencialidade dessa estrutura. Devemos criar Case para casos específicos e criar um Default para o comportamento padrão. Veja que o comportamento alimentar padrão da maioria das pessoas é comer de tudo. Alguns grupos minoritários possuem certas restrições alimentares e para eles criamos cases específicos. Lembre-se, Default significa "padrão" e case, "caso", criando já a idéia de casos específicos frente a um comportamento padrão.

OPERADORES LÓGICOSOs operadores lógicos são capazes de modificar ou combinar operadores relacionais. Assim eles são capazes de construir estruturas lógicas extremamente complexas ou criar relações que um compilador não possui primitivamente. Os principais operadores lógicos são NOT, AND, OR e XOR que em português equivalem a não, e, ou e ou exclusivo.

Operador Not Esse operador é capaz de inverter os valores verdadeiro e falso de uma operação, valor ou variável. Lembre-se que verdadeiro é todo valor diferente de zero e falso é igual a zero.

Exemplo 29a = 0

9

Page 10: Modulo 2

b = 100Print "valor de a: " + aPrint "valor de Not a: " + (Not a)Print "valor de b: " + bPrint "valor de Not b: " + (Not b)WaitKeyComo o valor de “a” era igual a zero, que é igual a falso, o Not transformou o mesmo em 1, isso é, verdadeiro. Todo valor diferente de zero é verdadeiro. Ao perceber que o valor de “b” era 100, o operador Not o entende como verdadeiro e o transforma em falso, isso é, 0.Os principais usos do operador not são: inverter a verificação de uma condição e criar chaves de tipo liga-desliga. Em Blitz3D você vai se cansar de ver um caso de inversão de verificação, como do tradicional loop While com fuga em Esc. Esse loop vai executar enquanto a tecla Esc não for pressionada, pois sua condição é "enquanto a tecla 1 não for pressionada".

Exemplo 30While Not KeyHit(1)Print "Loop Executando"WendEndNosso próximo exemplo mostra como criar uma chave liga-desliga. Veja que ao pressionarmos a barra de espaço If KeyHit(57) a chave é invertida pelo operador not chave = Not chave.

Exemplo 31chave = 0While Not KeyHit(1)ClsText 100, 100, "Pressione Space mudar Chave"If KeyHit(57) chave = Not chaveIf chaveText 140, 140, "Chave Ligada"ElseText 140, 140, "Chave Desligada"EndIfFlip WendEnd

Operador AndEsse operador é capaz de criar uma dupla condição, isso é, para que um comando seja executado, duas condições terão que ser verdadeiras. Seria o mesmo que criar uma estrutura de If aninhado. Se alguma das

condições forem falsas ou ambas forem falsas, o And vai retornar falso.

Exemplo 32a = 1b = 2If (a=1) And (b=2) Print "Ambas são verdadeiras"ElsePrint "Não passou no teste"EndIfWaitKeyTeste: mude os valores de a e b e verifique que a condição não é satisfeita.O operador And, dessa forma, se consolida como um importante recurso para construir a IA (inteligência artificial) de jogos, afinal e dá a possibilidade de criar verificações e raciocínios mais complexos. Imaginemos uma situação onde, para passar de fase no jogo, deveríamos ter realizado uma missão (missao1) e termos derrotado o chefe da fase (chefe1). Essa dupla condição poderia ser verificada de maneira mais simples assim:

Exemplo 33Missao1$ = "OK"Chefe1$ = "OK"If (Missao1$ = "OK") And (Chefe1$ = "OK") Print "Entrando na fase 2..."ElsePrint "Mate o chefe e conclua a missão" EndIfWaitKeyVeja que passamos a fase, pois as duas condições são verdadeiras, mas experimente mudar as seguintes variáveis:1º teste: mude Missao1$ = "OK" para Missao1$ = "".2º teste: mude Chefe1$ = "OK" para Chefe1$ = "".

Entendendo o operador AndA tabela abaixo mostra os resultados para cada combinação com o operador And. Como podemos ver, só terá resultado final verdadeiro (true) se os dois argumentos (condição) forem verdadeiros.Condição 1

Operador

Condição 2 RESULTADO

verdadeiro And verdadeiro VERDADEIROverdadeiro And falso FALSO

10

Page 11: Modulo 2

falso And verdadeiro FALSOfalso And falso FALSO

Um outro uso muito comum para esse operador é criar ataques especiais, com combinação de teclas. Por exemplo, a tecla "Z"(44) dispara o ataque 1 e a tecla "X"(45) dispara o ataque 2, mas se pressionarmos as duas ao mesmo tempo, disparamos o ataque especial.

Exemplo 34While Not KeyHit(1)golpe1 = 0golpe2 = 0golpe3 = 0If KeyDown(44) golpe1 = 1 If KeyDown(45) golpe2 = 1 If (golpe1 = 1) And (golpe2 = 1)golpe1 = 0 golpe2 = 0golpe3 = 1EndIfIf (golpe1 = 1) Print "Deferindo Golpe1!"If (golpe2 = 1) Print "Deferindo Golpe2!"If (golpe3 = 1) Print "Deferindo Golpe Especial!"Wend

Operador OrCom o operador Or nós podemos criar uma expressão onde apenas um dos elementos precisa ser verdadeiro para que a expressão toda seja verdadeira. Se ambos forem verdadeiros, o resultado também é verdadeiro. Para que não passe no teste, os dois elementos da expressão devem ser falsos.

Exemplo 35a = 1b = 2If (a=1) Or (b=0) Print "Só a primeira é verdadeira e passou no teste"If (a=0) Or (b=2) Print "Só a segunda é verdadeira e passou no teste"If (a=1) Or (b=2) Print "Ambas verdadeiras e passou no teste"If (a=0) Or (b=0) Print "Ambas falsas, isso não imprime"WaitKeyVeja a tabela de operações lógicas para o operador Or. Perceba que para termos um resultado falso é necessário que as duas condições sejam falsas.Condição 1 Operador Condição 2 RESULTADOverdadeiro Or verdadeiro VERDADEIRO

verdadeiro Or falso VERDADEIROfalso Or verdadeiro VERDADEIROfalso Or falso FALSOEsse operador é muito útil para criar inteligência artificial e também para controles. Você pode utilizá-lo por exemplo para definir mais de uma tecla ou forma de controle para disparar um tiro. No exemplo abaixo, tanto a tecla barra de espaços (57) quanto a tecla enter (28) disparam o tiro.

Exemplo 36While Not KeyHit(1)If KeyHit(57) Or KeyHit(28) Print "Disparando Missel!" WendWaitKey

Operador XorO operador Xor cria uma análise disjuntiva onde apenas uma das condições deve ser verdadeira para que o resultado da expressão seja verdadeiro. Caso as duas condições sejam verdadeiras, dará falso. Caso nenhuma seja verdadeira, também será falso.

Exemplo 37a = 1b = 2If (a=1) Xor (b=0) Print "Só a primeira é verdadeira e passou no teste"If (a=0) Xor (b=2) Print "Só a segunda é verdadeira e passou no teste"If (a=1) Xor (b=2) Print "Ambas verdadeiras, isso não imprime"If (a=0) Xor (b=0) Print "Ambas falsas, isso não imprime"WaitKeyVeja a tabela lógica desse operador. Se os duas condições forem verdadeiras ele dará falso. Condição 1

Operador Condição 2 RESULTADO

verdadeiro Xor verdadeiro FALSOverdadeiro Xor falso VERDADEIROfalso Xor verdadeiro VERDADEIROfalso Xor falso FALSO

Embora esse operador seja muito usado em operações lógicas e em matemática, será muito difícil você achar um uso para ele em jogo. Mas vamos tentar usar um pouco a imaginação...Em seu jogo de RPG você é discípulo de um mago. Ele te envia para um missão e diz "Vá até a

11

Page 12: Modulo 2

caverna encantada. Lá você vai encontrar duas gemas mágicas, escolha apenas uma delas!". Assim se você pegar apenas uma delas, você passou no teste, se não pegar nenhuma ou pegar as duas, você está frito.

Exemplo 38GemaVermelha = 1GemaVerde = 1If (GemaVermelha = 1) Xor (GemaVerde = 1)Print "Você está pronto para a próxima tarefa!"ElsePrint "Teimoso, vai acabar virando um sapo!"EndIfWaitKey

LOOPSOs loops, também conhecidos como laços, são estruturas que criam repetições. Eles são usados para diversas finalidades como fazer cálculo de incremento, controlar vários dados ou objetos de uma mesma família, ou até manipulação e exibição de cenários complexos de jogos.

For NextOs laços desse tipo são ideais para criar contadores ou estrutura de controle com repetições pré-determinadas. Por meio dele podemos definir na própria estrutura do laço quantas vezes o mesmo vai ser repetido.O exemplo abaixo exibe 50 vezes o valor de x na tela. Como a variável vai sendo incrementada, o seu valor vai aumentando.

Exemplo 39For x = 1 To 50Print "O valor de x é: " + xNextWaitKey

Nesse exemplo declaramos que a variável do contador será a variável “x”, também dizemos que ela deve ser iniciada a partir do valor 1, pois x = 1. Na estrutura do laço também devemos dizer até que ponto o laço vai executar e para isso usamos o comando “To”. Nesse exemplo declaramos que o laço executará até que a variável x seja igual a “50”, por meio dessa declaração: To 50. For x = 1 To 50

Essa linha toda poderia ser traduzida por “Faça x igual a 1 e execute o loop até que x seja igual a 50”. Como não declaramos o valor de incremento, ele será feito de 1 em 1. Portanto esse linha faz com que o loop seja executado 50 vezes.NextEsse comando diz onde é o final do bloco de código que deve ser executado dentro do loop. Ao se deparar com esse comando, o programa volta para o cabeçalho do loop, incrementa a variável e verifica se chegou ao valor limite, se não chegou executa o código novamente. Se chegou, vai para a linha de código posterior ao Next.No exemplo abaixo, usamos o auxiliar Step de For-Next para especificar de quanto será o incremento em cada iteração do loop. Como estabelecemos Step 5 então nossa variável x vai aumentar de 5 em 5: 1, 6, 11, 16 ... 46.

Exemplo 40For x = 1 To 50 Step 5Print "------------------------"Print " O valor de x é: " + xNextWaitKey

No exemplo abaixo criamos dois loops, sendo que um deles é aninhado. Estamos usando essa estrutura para a criação de mapas de cenário. Nosso exemplo é bem simples e estamos usando apenas uma imagem (tile) para compor o mapa.

Exemplo 41Graphics 800,600tile = LoadImage("midia/agua.png")For y = 0 To 500 Step 100For x = 0 To 700 Step 100 DrawImage tile, x, y NextNextWaitKey

Como nesse programa estamos pela primeiro vez trabalhando com imagens, vamos dar uma pequena introdução sobre esse assunto, que será tratado de forma mais rigorosa mais a frente.tile = LoadImage("midia/agua.png")

12

Page 13: Modulo 2

O comando LoadImage carrega uma imagem do disco rígido (ou de cd ou disquete) para uma variável.Veja que nossa imagem está na pasta "midia" por isso colocamos o operador de escopo. Se a imagem estivesse na mesma pasta do programa, não precisaríamos passar sua localização. Nesse exemplo, estamos carregando a imagem agua.png que está pasta midia para a variável tile. Só depois de carregarmos uma imagem podemos realizar operações com ela. A partir de agora aquela imagem vai ser invocada por meio da variável tile.DrawImage tile, x, y No nosso exemplo só usamos a imagem para uma finalidade, exibir a mesma na tela. Fazemos isso com o comando DrawImage. Esse comando exibe uma imagem contida em uma variável em uma posição x, y da tela. Veja que estamos usando as variáveis x, y para dizer onde a imagem será exibida.For y = 0 To 500 Step 100For x = 0 To 700 Step 100 DrawImage tile, x, y NextNextVeja que criamos 2 loops. Um com a variável y e outro com a x. Como vamos usar uma imagem de 100 x 100 para compor o cenário, nossos loops são incrementados de 100 em 100 Step 100. O loop y cuida de incrementar a posição y em que a imagem será exibida e o loop x a posição x. Como a nossa tela tem 800 de largura, temos que, de 0 a 700 de 100 em 100, existem 8 unidades (0,100,200,300,400,500,600,700) e como nosso tile tem 100 de largura, assim 100 x 8 = 800. Logo, toda a tela é recoberta na horizontal. A tela tem 600 de altura, temos que, de 0 a 500 de 100 em 100, existem 6 unidades (0,100,200,300,400,500) e como nosso tile tem 100 de altura, assim 100 x 6 = 600. Logo, toda a tela é recoberta no sentido vertical. Loop WhileO laço do tipo While cria uma repetição de tempo indefinido, enquanto uma condição for satisfeita. Assim, sua estrutura é (While condição – Wend). O Wend é o seu comando de retorno do loop.

No exemplo abaixo, criamos uma estrutura While que pode substituir um loop For-Next.

Exemplo 42While (x < 51) Print "------------------------"Print " O valor de x é: " + xx = x + 1 WendWaitKeyPerceba que como esse tipo de laço é de verificação de condição, o incremento tem que ser feito manualmente, dentro do loop. Fizemos a seguinte verificação como condição “Enquanto x for menor que 51”.Um dos principais usos desse tipo de laço é criar loops de controle para capturar eventos. Assim são muito usados em programas interativos, como multimídia, menus e jogos. Veja um exemplo muito usado em jogos do Blitz3D, enquanto a tecla ESC não for pressionada, o loop do jogo fica em execução.

Exemplo 43While Not KeyHit(1)Print "-*- Jogo em Execução -*-"WendEnd

No exemplo abaixo, criamos um simples programa de desenho. Usamos um loop While para capturar eventos. A barra de espaços sorteia a cor da paleta de desenho e as setas direcionais movem o pincel.

Exemplo 44Graphics 640, 480x = 320y = 240While Not KeyHit(1)If KeyHit(57)SeedRnd MilliSecs()r = Rnd(0,255)g = Rnd(0,255)b = Rnd(0,255)Color r, g, bEndIfIf KeyHit(205) x = x + 1If KeyHit(203) x = x - 1If KeyHit(208) y = y + 1If KeyHit(200) y = y - 1Plot x, y

13

Page 14: Modulo 2

WendEnd

A lógica do programa é usar o loop para poder rastrear eventos do teclado. As setas direcionais alteram as posições x e y onde o ponto é exibido e a tecla barra de espaço sorteia uma nova cor para a paleta de desenho.Abaixo temos um exemplo não funcional do uso do loop While para implementar a arquitetura de um jogo. Dentro do loop do jogo colocamos chamadas das funções de controle do jogo, que deverão ser declaradas mais abaixo, logo depois do comando End. Em breve explicaremos como usar funções.While Not KeyHit(1) Verificar_Entradas()Processar_Logica()Processar_Fisica()Renderizar() WendEnd

Repeat UntilEsse tipo de laço garante que o bloco de código vai executar pelo menos uma vez, mesmo que a condição seja falsa ou verdadeira. Bem por isso o teste de condição se dá no final do bloco, no comando Until.Veja um exemplo onde ele só executa uma vez.

Exemplo 45x = 0Repeat Print "-*- Jogo em Execução -*-" Until x = 0WaitKeyEndO significado de Until é “até”, portanto ele executa até que a condição seja verdadeira. Isso inverte a lógica do While, que executa enquanto a condição for verdadeira. Por isso que no exemplo acima, mesmo a condição sendo verdadeira, só executa uma vez.No exemplo a segui criamos um loop que se encerra com o apertar da tecla ESC. Mas perceba que, como a lógica de execução é inversa do While, aqui não fizemos uso do operador lógico

Not (Not KeyHit(1)), pois agora o loop executa até que a tecla 1 seja pressionada.

Exemplo 46Repeat Print "-*- Jogo em Execução -*-" Until KeyHit(1)End

FUNÇÕESDentro do ambiente de desenvolvimento do Blitz3D as funções são as estruturas mais elaboradas para projetar o seu software de forma a prover reuso de código, organização e modularidade ao mesmo. As principais características de uma função são: capacidade de passar argumentos, capacidade de retornar dados e controle de escopo. Por meio dessas características as funções são capazes de tornar-se uma estrutura de código auto-suficiente, independente de outros trechos do programa. Em uma arquitetura perfeita de software no Blitz3D, uma função deve ser projetada e usada de forma a manter um único canal de dependência: a sua própria invocação. Isso garante que o código é eficiente, fácil de ser testado, de manutenção simples, fácil de ser modificado e poderá ser reutilizado em outros projetos.

Um exemplo mínimoVamos dar um exemplo de uma função mais simples possível e seu uso. Os elementos essenciais de uma função são: sua declaração, seu bloco de código e sua invocação.

Exemplo 47exibe()WaitKeyEndFunction exibe()Print "funciona!"End Function

Vamos a linha que declara a função:Function exibe()O comando Function instrui ao Blitz3D que vamos declarar uma função, o passo seguinte é dar um

14

Page 15: Modulo 2

nome para a função, nesse caso escolhemos o nome exibe pois vamos exibir uma mensagem. O nome deve sempre ser finalizado por um par de parênteses "()" que é a área de argumentos de uma função. Como essa função é simples, não recebe argumentos, por isso os parênteses estão vazios.Print "funciona!"O passo seguinte é declarar o bloco de código que deve executar quando a função é invocada. No nosso exemplo colocamos apenas uma linha, mas poderiam ser milhões.End FunctionDevemos dizer ao Blitz3D onde se encerra a declaração da função e isso se dá com a declaração End Function.exibe()Para usar uma função devemos chamar pelo seu nome. Como nesse exemplo nossa função não suporta argumentos, chamamos a função com os parênteses vazios.

Usando Funções em JogosSe as funções não existissem, nossos jogos seriam uma massa contínua e desorganizada de código. Provavelmente muitas partes do código dependeriam de muitas outras e seria muito difícil compreender, alterar e fazer manutenção no código. Reuso então, impensável.Por meio das funções podemos criar blocos lógicos de código com finalidades específicas. Cada bloco de código tem uma responsabilidade própria e funciona de maneira bastante autônoma. Vamos ver como isso funciona na prática em um jogo.

Exemplo 48Graphics 800, 600SetBuffer BackBuffer()Global nave = LoadImage("midia/f15.bmp")Global px = 380Global py = 450While Not KeyHit(1)Controle()Draw()WendEndFunction Controle()If KeyDown(203) px = px - 1If KeyDown(205) px = px + 1

End FunctionFunction Draw()DrawImage nave, px, pyFlipClsEnd Function

Veja que agora o código está bem estruturado. Dentro o loop fazemos apenas invocações ás funções e cada função é responsável por fazer sua parte do trabalho de composição da aplicação. Nossa função de Controle é responsável pelos controles na nave e a função Draw, responsável por exibir a imagem na tela. Mais para a frente vamos incrementar esse programa e você terá uma idéia mais completa da estrutura de um jogo e suas funcionalidades.

Passando DadosAs funções são capazes de receber dados (argumentos) de quem as invoca, para serem usados no corpo da função. É possível declarar no cabeçalho da função quais os tipos de argumentos que essa função aceita. Podemos declarar desde que não suportará nenhum, como no exemplo anterior, ou que suportará apenas um tipo específico (isso é, um int, um float ou uma string), ou também podemos declará-la para suportar vários argumentos. Vamos analisar cada caso.

Passando parâmetros inteirosPara que uma função possa aceitar um dado do tipo inteiro ela deve possuir uma variável de inteiros no cabeçalho de sua declaração. Lembre-se que inteiro são variáveis com ausência de sufixos ou então com sufixo "%"A função de exemplo abaixo exibe o quadrado de um número e suporta apenas números inteiros. Caso você não se recorde, o quadrado de um número é o numero resultante da multiplicação desse número por ele mesmo.

Exemplo 49calcula(3)WaitKeyEndFunction calcula(numero)Print "O quadrado é: " + ( numero * numero) End Function

15

Page 16: Modulo 2

A primeira coisa que fizemos foi declara a funçãoFunction calcula(numero)Declaramos uma função de nome calcula, perceba que ela possui uma variável dentro dos parênteses numero. Essa variável não possui sufixo, portanto é de inteiros. Print "O quadrado é: " + ( numero * numero) No bloco de código estamos recuperando o valor da variável do cabeçalho da função na operação de multiplicação (numero * numero). É assim que funciona o mecanismo de operações abstratas de uma função. Declaramos uma variável genérica e a mesma pode receber qualquer dado do tipo dela. Dai recuperamos a variável dentro da função e fazemos operações com ela. Assim criamos um mecanismo abstrato capaz de fazer operações com qualquer tipo de dados, basta passar o que queremos calcular.calcula(3)Nessa linha temo o exemplo do uso da função. Invocamos a mesma pelo seu nome e passamos para ela o valor que desejamos que ela calcule. Como o valor é um inteiro, portanto corresponde ao tipo estabelecido, ela aceita como valor para a variável do cabeçalho da função e opera com ele em seu interior.Veja no exemplo a seguir vários usos da função criada.

Exemplo 50calcula(1)calcula(7)calcula(111)WaitKeyEndFunction calcula(numero)Print "O quadrado é: " + ( numero * numero) End Function

Usando passagem de argumentos em jogosO real poder das funções está no fato de elas poderem passar argumentos, dessa forma podemos criar apenas uma função para efetuar diversas trabalhos diferentes. Imagine uma situação onde, em um jogo de tiro, o personagem perde vira ao receber um tiro ou um golpe e ganha vida ao coletar itens, mas cada tiro ou golpe tira quantidades diferentes de vida, afinal

existem armas diferentes... E, cada item também restaura quantidades diferentes de vida. Tudo isso parece muito complexo, mas pode ser implementado apenas por uma função que aceite um único parâmetro. Uma função SetVida(valor). Vamos á prática.

Exemplo 51Global HeroiVida = 100;tiro1 disparadotiro1 = 1;tiro2 disparadotiro2 = 1;item1 coletadoitem1 = 1;item2 coletadoitem2 = 1If tiro1 SetVida(-20)If tiro2 SetVida(-50)If item1 SetVida(10)If item2 SetVida(25)WaitKeyEndFunction SetVida(Valor)HeroiVida = HeroiVida + ValorPrint "A vida do Heroi é: " + HeroiVidaEnd FunctionNesse programa usei uma simples atribuição para representar um evento de colisões:tiro1 = 1item1 = 1Em um jogo, os eventos receber um tiro ou pegar um item são disparado por uma colisão, mas isso ficaria muito complexo para esse momento.If tiro1 SetVida(-20)If tiro2 SetVida(-50)If item1 SetVida(10)If item2 SetVida(25)

Nessas linhas verificamos se o evento foi disparado, isso é, se fomos atingidos pelo tiro ou se pegamos o item. Caso seja verdadeiro, acionamos a função de configuração de vida e passamos a ela a quantidade de vida que cada evento tiro ou item resulta.

Passando parâmetros do tipo floatComo você se lembra, uma variável de inteiros não suporta um valor de ponto flutuante, assim ele será arredondado. O mesmo se dá com parâmetros de funções. Veja, no exemplo abaixo,

16

Page 17: Modulo 2

o que ocorre se passarmos um número decimal para uma função de inteiros.

Exemplo 52calcula(1.8)WaitKeyEndFunction calcula(numero)Print "O quadrado é: " + ( numero * numero) End FunctionPara evitar que ocorram arredondamentos desse tipo em funções que podem, por ventura, receber um parâmetro de ponto flutuante, é necessário que declaremos uma função como receptora de argumentos do tipo float. Para isso, em seu cabeçalho devemos colocar variáveis do tipo float, com o sufixo Sharp “#”.Vamos a uma nova versão do programa acima.

Exemplo 53calcula(1.8)WaitKeyEndFunction calcula(numero#)Print "O quadrado é: " + ( numero# * numero#) End FunctionAgora sim ela funciona corretamente.

Passando textos para funçõesPara que uma função aceite argumentos em forma de texto essa deve ter em seu cabeçalho uma variável de string, com sufixo dólar “$”. Veja no exemplo abaixo uma função que suporte argumentos em texto.

Exemplo 54exibe("Blitz3D é Legal")exibe("Eu amo Blitz3D")WaitKeyEndFunction exibe(texto$)Print "---------------------------------------------"Print "-------------- " + texto$ + " --------------" Print "---------------------------------------------"End FunctionVocê provavelmente vai usar uma função que necessite receber dados em strings para criar seu sistema de escores e fazer login e para trocar mensagens em um jogo multiplay.

Exemplo 55

Print "------------ enviar mensagem ------------"While Not KeyHit(1)Print ""msm$ = Input$("Digite a mensagem: ")msm$ = "JOGADOR1: " + msm$ EnviarMensagem(msm$)WendEndFunction EnviarMensagem(M$)Print "-> " + M$ End FunctionPassando vários parâmetrosTeoricamente, você pode criar funções com suporte a uma quantidade ilimitada de argumentos. E você pode colocar tipos diferentes de argumentos misturados. Veja no exemplo abaixo uma função que aceita vários inteiros para somá-los depois.

Exemplo 56soma(1, 10, 5, 333, 1028)WaitKeyEndFunction soma(a,b,c,d,e)Print "O resultado é: " + (a+b+c+d+e)End FunctionVeja agora o exemplo de uma função que aceita os três tipos diferentes de argumentos. Dois inteiros que deverão dizer em que local da tela a mensagem irá ser exibida. Um texto com o nome e um float com a média da nota do aluno.

Exemplo 57aluno(100,100,"Daniel", 9.5)WaitKeyEndFunction aluno(x,y,nome$,nota#)Text x,y, "Nome:" + nome$ + " " + "Nota:" + nota#End Function

Retornando dadosA segunda característica muito importante das funções é a sua capacidade de retornar dados. O fato de ela poder receber e retornar dados é que dá a ela sua capacidade modular de ser uma unidade de software autônoma. Ela pode ser separada do resto do programa como uma prestadora de serviços. Passamos a ela nosso problema (os parâmetros) e ela processa e nos dá a resposta (retorno de valores).Para que uma função possa retornar um valor precisamos configurar mais duas coisas nela:

17

Page 18: Modulo 2

devemos dizer qual é o tipo de dado que a função retorna e usar o comando Return para dar a resposta.

Função retornando um inteiroPara que uma função possa retornar um valor inteiro devemos usar o comando Return. Como ocorre com as variáveis, as funções são por padrão de inteiros; assim, não é necessário colocar um sufixo de inteiros em uma função. Vamos fazer isso nesse exemplo apenas para ter idéia de como se faz.

Exemplo 58 cubo = calcula_cubo%(3)Print cuboWaitKeyFunction calcula_cubo%(x)Return x*x*xEnd Function

Perceba que ao declarar a função, a declaramos com um sufixo "%" de inteiros.Function calcula_cubo%(x)É esse sufixo na função que determina qual o tipo de valor ela vai retornar.Return x*x*xO comando Return é o que retorna o valor para quem chama. Como queremos que essa função retorne o cubo do numero passado como argumento, fizemos (x*x*x) isso é, o cubo do número, pois o cubo é um numero multiplicado por ele mesmo duas vezes.cubo = calcula_cubo%(3)Perceba que na invocação da função colocamos uma variável cubo para capturar o valor que ela retorna. Esse é o procedimento padrão para pegar o valor de retorno de uma função.Como Dissemos, uma função sem tipo de retorno definido é por padrão uma função de inteiros. Portanto, o exemplo abaixo também funciona.

Exemplo 59cubo = calcula_cubo(2)Print cuboWaitKeyFunction calcula_cubo(x)Return x*x*xEnd Function

Você também pode colocar o valor dos cálculos internos em uma variável interna e mandar que retorne o valor dessa variável, o que é o procedimento mais comum.

Exemplo 60cubo = calcula_cubo(7)Print cuboWaitKeyFunction calcula_cubo(x)calc = x*x*xReturn calcEnd FunctionRetornar um floatPara que uma função retorne um float é necessário marcá-la como uma função de retorno de floats com o sufixo sharp “#”.

Exemplo 61divide# = DIV#(10,3)Print divide#WaitKeyFunction DIV#(x#,y#)calc# = x#/y#Return calc#End Function

Retornar uma stringUma função que pretenda retornar um texto deve possuir em sua definição o sufixo “$”. Veja um exemplo de uma função que envia um texto e o recebe de volta com alguns ornamentos.

Exemplo 62texto$ = tratar$("Hoje é meu dia de sorte")Print texto$WaitKeyFunction tratar$(x$)tratado$ = "|*-*-< " + x$ + " >-*-*|"Return tratado$End Function

Usando retorno de dados em JogosVamos dar seqüência àquele projeto do joguinho do avião F15. Adicionamos algumas funcionalidades extras: colocamos asteróides em movimento pontuação e detectamos colisões. O uso de retorno de dados nesse jogo se dá na nossa função de verificação de colisões; se algum dos asteróides se chocar com a nave, a função de

18

Page 19: Modulo 2

colisões retorna o valor 1 e então encerramos o jogo.

Exemplo 63Graphics 800, 600SetBuffer BackBuffer()Global nave = LoadImage("midia/f15.bmp")Global px = 380Global py = 450Global pedra1 = LoadImage("midia/pedra1.png")Global p1x = Rnd(0,700)Global p1y = -Rnd(100,700)Global pedra2 = LoadImage("midia/pedra2.png")Global p2x = Rnd(0,700)Global p2y = -Rnd(100,700)Global pedra3 = LoadImage("midia/pedra3.png")Global p3x = Rnd(0,700)Global p3y = -Rnd(100,700)Global pedra4 = LoadImage("midia/pedra1.png")Global p4x = Rnd(0,700)Global p4y = -Rnd(100,700)Global pedra5 = LoadImage("midia/pedra2.png")Global p5x = Rnd(0,700)Global p5y = -Rnd(100,700)Global PONTOS = 0While Not KeyHit(1)Controle() Fisica()If Colide() Then GameOver()Logica() Draw()WendEndFunction Controle()If KeyDown(203) px = px - 1If KeyDown(205) px = px + 1End FunctionFunction Logica()Pontos = Pontos + 1End FunctionFunction Fisica() p1y = p1y + 3

p2y = p2y + 3p3y = p3y + 3p4y = p4y + 3p5y = p5y + 3If p1y > 600p1y = -Rnd(100,500)p1x = Rnd(0,700)EndIfIf p2y > 600p2y = -Rnd(100,500)p2x = Rnd(0,700)EndIfIf p3y > 600p3y = -Rnd(100,500)p3x = Rnd(0,700)EndIfIf p4y > 600p4y = -Rnd(100,500)p4x = Rnd(0,700)EndIfIf p5y > 600p5y = -Rnd(100,500)p5x = Rnd(0,700)EndIfEnd FunctionFunction Draw()DrawImage nave, px, pyDrawImage pedra1, p1x, p1yDrawImage pedra2, p2x, p2yDrawImage pedra3, p3x, p3y DrawImage pedra4, p4x, p4y DrawImage pedra5, p5x, p5y Color 255,0,0Text 10, 10, "PONTOS: " + PONTOSFlipClsEnd FunctionFunction Colide()If ImagesCollide(nave,px,py,0,pedra1,p1x,p1y,0) Then Return 1If ImagesCollide(nave,px,py,0,pedra2,p2x,p2y,0) Then Return 1If ImagesCollide(nave,px,py,0,pedra3,p3x,p3y,0) Then Return 1If ImagesCollide(nave,px,py,0,pedra4,p4x,p4y,0) Then Return 1If ImagesCollide(nave,px,py,0,pedra5,p5x,p5y,0) Then Return 1End FunctionFunction GameOver()ClsText 350, 290, "GAME OVER"Text 310, 310, "Você fez " + PONTOS + " Pontos"FlipDelay(3000)End End Function

19

Page 20: Modulo 2

Explicando...Graphics 800, 600SetBuffer BackBuffer()

Nessas duas linhas estamos configurando o modo gráfico do jogo. Estamos configurando a tela para rodar em 800 x 600 e estamos ligando o modo de double buffer. Explicaremos melhor isso na parte de gráficos 2d.Global nave = LoadImage("midia/f15.bmp")Global px = 380Global py = 450

Estamos carregando a imagem da nave para a variável nave e estamos definindo a sua posição inicia no eixo x como 380 e sua posição y como 450. As variáveis px e py são responsáveis por controlar a posição da nave. Perceba que todas as variáveis estão sendo declaradas como Global, isso porque elas serão acessadas de dentro de funções e só variáveis globais são reconhecidas dentro de funções.Global pedra1 = LoadImage("midia/pedra1.png")Global p1x = Rnd(0,700)Global p1y = -Rnd(100,700)

Usamos a mesma estrutura para construir todos os asteróides. Carregamos a imagem dele para uma variável Global (nesse caso a variável pedra1), definimos uma variável para controlar a sua posição x (p1x) e uma variável para a posição y (p1y). Para que os asteróides não apareçam sempre no mesmo lugar, a sua posição é dada por um sorteio de número aleatório Rnd(). Lembre-se de que as pedras descerão da parte superior da tela para a parte inferior e como elas tem 100 pixels de tamanho, sua posição inicial y deve ser de pelo menos -100, por isso usamos o sorteio p1y = -Rnd(100, 700), assim sua posição inicial será de -100 até -700.Global PONTOS = 0Essa variável é responsável por controlar a pontuação do jogador.While Not KeyHit(1)Controle() Fisica()If Colide() Then GameOver()Logica() Draw()Wend

End

Acima está o nosso loop de controle do jogo. Veja que ele é bem estruturado do ponto de vista lógico. Primeiro invocamos a função de controle, depois a de física, em seguida a de Colisões, logo após a de lógica e por último a de desenho. Veja que caso a função de colisões retorne algum valor, a função GameOver é acionada.Function Controle()If KeyDown(203) px = px - 1If KeyDown(205) px = px + 1End Function

Acima temos nossa função de controle. Ele verifica se as teclas seta para a esquerda e seta para a direita foram pressionadas. Caso isso seja verdadeiro, movimenta a nave por meio da alteração de suas variáveis posicionais.Function Logica()Pontos = Pontos + 1End Function

Nossa função de lógica não tem segredos. Ela apenas vai contando pontos. Cada vez que o loop faz uma iteração, isso é, que ele dá uma volta, conta 1 ponto.Function Fisica() p1y = p1y + 3p2y = p2y + 3p3y = p3y + 3p4y = p4y + 3p5y = p5y + 3

A primeira parte de nossa função de física apenas movimenta os asteróides. Faz isso um por um, alterando o valor da variável py de cada um deles. Como a variável vai aumentando, eles vão se movendo da parte superior da tela para a parte inferior.If p1y > 600p1y = -Rnd(100,500)p1x = Rnd(0,700)EndIf

A segunda parte da função de física verifica se ele já cruzaram a área de visibilidade da parte inferior da tela. Se eles já cruzaram esse limite, vão ser reposicionados na parte superior da tela. Assim fazemos o mesmo sorteio que fizemos no inicio do programa. Usando essa técnica não

20

Page 21: Modulo 2

precisamos ficar criando infinitas instâncias de meteoros para o jogo, só cinco objetos deu conta.Function Draw()DrawImage nave, px, pyDrawImage pedra1, p1x, p1yDrawImage pedra2, p2x, p2yDrawImage pedra3, p3x, p3y DrawImage pedra4, p4x, p4y DrawImage pedra5, p5x, p5y Color 255,0,0Text 10, 10, "PONTOS: " + PONTOSFlipClsEnd Function

A função de desenho exibe as imagens na tela. Veja que estamos exibindo a nave, os meteoros e os pontos. O comando Flip atualiza a tela e o comando Cls apaga a exibição para deixar a tela limpa para a próxima composição.Function Colide()If ImagesCollide(nave,px,py,0,pedra1,p1x,p1y,0) Then Return 1If ImagesCollide(nave,px,py,0,pedra2,p2x,p2y,0) Then Return 1If ImagesCollide(nave,px,py,0,pedra3,p3x,p3y,0) Then Return 1If ImagesCollide(nave,px,py,0,pedra4,p4x,p4y,0) Then Return 1If ImagesCollide(nave,px,py,0,pedra5,p5x,p5y,0) Then Return 1End Function

Essa é a nossa função de colisões. Veja que a verificação de colisões é feita por meio do comando ImagesCollide(). Esse comando é um pouco complexo mas não é difícil. Devemos colocar por seqüência Imagem1, posiçãox, posiçãoy e frame e para a segunda imagem os mesmos valores. O veja, por exemplo a primeira linha, a primeira imagem da verificação é nave, a posição x é dada por px e a posição y por py, já o frame é 0 pois não é uma imagem animada. A segunda imagem a ser verificada é a imagem pedra1, a posição x dela é p1x, a posição y é p1y e o frame é 0. Assim é possível verificar colisões entre nave e pedra1.Veja que se a colisão acontece, é acionado Return 1. Esse comando encerra a execução de uma função, e retorna um valor. Assim, quando um

asteróide colidir com a nave, a função é forçada a encerrar e retorna o valor 1.Function GameOver()ClsText 350, 290, "GAME OVER"Text 310, 310, "Você fez " + PONTOS + " Pontos"FlipDelay(3000)End End Function

Ai está nossa função final. Ela limpa a tela e exibe a mensagem de final de jogo e a pontuação do jogador. Por fim ela usa o comando Delay(3000) para fazer com que a imagem permaneça na tela por 3000 milésimos de segundos, isso é, 3 segundos. O comando Delay() faz o processamento para por um tempo especificado.Viu como foi fácil? Você já saber o suficiente de programação para fazer jogos básicos muito bem estruturados!

Controle de escopoA terceira característica importante das funções é o fato de possuir escopo diferente do resto do código. Assim, variáveis externas às funções não são reconhecidas por essa e variáveis criadas dentro dela são aniquiladas quando a execução do código chega em End Function.

Exemplo 64x = 10Print "X de fora: " + xfunc()Print "X de fora: " + xWaitKeyEndFunction func()x = 333Print "X de dentro: " + xEnd FunctionPerceba que declaramos o valor de x no código principal com o valor “10” nessa linha:x = 10Logo em seguida ordenamos sua exibição.Print "X de fora: " + xA exibição confirmou a declaração anterior.func()Nessa linha acima, invocamos a função que possui o segundo corpo de código:x = 333

21

Page 22: Modulo 2

Print "X de dentro: " + xPerceba que estamos criando e definindo o conteúdo de uma nova variável local da função. Essa variável não tem nada a ver com a variável x anterior que é de escopo do código externo á função. Isso pode ser demonstrado no código a seguir.Print "X de fora: " + xEssa linha vem após a invocação da função e está fora dela. Perceba que a variável “x” externa possui o mesmo valor, isso é “10” pois a mesma não foi afetada pela declaração interna da função.Assim, podemos deduzir que as funções possuem cada qual um escopo à parte. Cada função possui escopo próprio diferente do escopo das demais e do escopo do código principal.O exemplo a seguir demonstra que o escopo é destruído toda vez que a função passa pelo comando End Function, isso é, a cada vez que a função é chamada, as variáveis são criadas e destruídas.

Exemplo 65func()func()WaitKeyEnd Function func()x = x + 100Print "O valor de X: " + xEnd FunctionPerceba que as duas invocações produzem o mesmo resultado: “100”. Se a função conservasse as variáveis, na segunda invocação o resultado deveria ser “200”.

Burlando o escopoA regra de escopo das funções existe para que sejamos obrigados a usar os mecanismos de passagem de dado pelo cabeçalho e receber o resultado pelo return. Além disso, esse mecanismo de escopo evita erros de duplicação de variáveis e alteração de valores acidentalmente.Existe uma maneira de você conseguir burlar essa regra de escopo. Isso pode ser feito por meio de variáveis de escopo global. Uma variável definida como global tem validade em qualquer parte do

programa. O próximo exemplo demonstra isso, uma variável definida como global é alterada dentro de uma função.

Exemplo 66Global x = 100Print "Fora da Função x = " + xfunc()Print "Fora da Função x = " + xWaitKeyEndFunction func()x = x + 100Print "Dentro da Função x = " + xEnd FunctionPerceba que a variável foi reconhecida dentro da função, pois x + 100 resultou em 200. Veja também que esse valor se manteve fora da função, pois o Print final, que é no código principal, também exibiu esse valor. Assim, uma variável global possui escopo universal, isso é, é valida em qualquer lugar.

Garantindo o escopo localComo vimos, por boa prática devemos usar as funções sempre com escopo local para evitar erros com alterações inesperadas de valores de variáveis. Ocorre que embora seja desaconselhável manter variáveis globais em um programa isso às vezes ocorre por razões diversas. Assim pode ser que acidentalmente acessemos uma delas dentro de uma função. Para evitar isso, quando declaramos variáveis internas de uma função, é uma boa prática dizer que essa é uma variável local. Isso pode ser feito por meio do comando de controle de escopo Local.Veja um exemplo onde alteramos o código anterior, definindo a variável x interna da função como Local.

Exemplo 67Global x = 100Print "Fora da Função x = " + xfunc()Print "Fora da Função x = " + xWaitKeyEndFunction func()Local x = x + 100Print "Dentro da Função x = " + x

22

Page 23: Modulo 2

End Function

4. PROGRAMAÇÃO: PARTE II

Vamos iniciar agora nosso último bloco de técnicas de programação. Depois de estudar esse bloco você terá aprendido os principais recursos das linguagens de programação e estará apto de desenvolver qualquer tipo de jogo.

TypesTypes são tipos de dados compostos que você mesmo cria e depois de criá-los você pode criar variáveis dele. Como eles são tipos de dados compostos, uma variável de um type pode conter vários campos internos como inteiros, strings, floats, etc.A principal vantagem dos types é que você cria modelos de objetos. Por exemplo, um personagem de um jogo possui vários dados como, uma imagem, uma posição x, uma posição y, vida, dinheiro, força, armas, etc. Em um type você pode criar uma estrutura coesa com todos esses dados, sem precisar ficar criando várias variáveis soltas. Depois você pode criar quantos objetos quiser dessa estrutura.Imagina que em um jogo você vai ter 1.000 soldados inimigos e cada um deles devem possuir pelo menos 10 variáveis de controle. Em vez de criar 10.000 variáveis, você pode criar apenas 1 type com 10 campos.Vamos ao exemplo prático.

Exemplo 1Type TinimigoField pxField pyField NOME$End Type soldados.Tinimigo = New Tinimigosoldados\px = 100soldados\py = 100soldados\NOME$ = "Ogro1" Text soldados\px, soldados\py, soldados\NOME$WaitKey

Vamos explicar esse programa linha por linha.

Em primeiro momento declaramos o type.Type Tinimigo

Estamos declarando que iremos construir um modelo de types. Para isso usamos o comando “Type” e demos a esse novo modelo o nome “Tinimigo”.Field px

Estamos declarando um campo para o type. Para declarar um campo usamos o comando “Field” que em inglês significa “campo”. Estamos dando o nome “px” para esse primeiro campo, pois ele vai guardar a posição x do personagem. Um campo é na verdade uma variável interna do type. Podemos ter quantas variáveis internas desejarmos.Field py

Estamos declarando o nosso segundo campo, para conter a posição y do personagem.Field NOME$

Estamos, nessa linha, declarando o nosso terceiro e último campo “NOME$”, que agora é do tipo string; portanto deverá conter um texto. Vamos usar ele para dar a identidade do objeto.End Type

Por fim estamos encerrando a declaração do modelo por meio do comando “End Type”.

Os passos seguintes são de como usamos o type.soldados.Tinimigo = New Tinimigo

Nessa linha estamos declarando uma variável do type; a variável é “soldados”. Para declarar uma variável colocamos o nome da variável mais o operador ponto “.”, mais o nome do type, depois o operador de atribuição “=”, seguido do operador “New” e o nome do type mais uma vez, assim a forma de declarar é:variável.type = New type

Depois de já termos uma variável declarada podemos declarar o conteúdo de cada campo.soldados\px = 100

Perceba que acessamos o campo “px” da variável de type e lhe atribuímos o valor “100”. Fizemos isso por meio do operador barra invertida “\”. A sintaxe para o acesso aos campos da variável do typo é:variável\campo

O passo seguinte foi continuar a declarar os conteúdos da variável.

23

Page 24: Modulo 2

soldados\py = 100

Aqui declaramos o conteúdo do campo “py”.soldados\NOME$ = "Ogro1"

E por fim o conteúdo do campo “NOME$”. Perceba que por ser um campo de strings declaramos como conteúdo um texto, o nome do objeto.Text soldados\px, soldados\py, soldados\ID$

Já com todos os conteúdos declarados, podemos utilizar a variável do type. Na linha acima usamos os campos px e py para dar as coordenadas para o comando text e exibimos o nome do objeto do type que está guardado no campo “NOME$”.

Muitas variáveisÉ possível declarar quantas variáveis desejarmos de um mesmo type. No exemplo anterior utilizamos o modelo para criarmos apenas uma variável, a variável soldados, mas não existe um limite para quais e quantas podem ser criadas. Veja um novo exemplo.

Exemplo 2Type TinimigoField pxField pyField NOME$End Typesoldados.Tinimigo = New Tinimigosoldados\px = 170soldados\py = 100soldados\NOME$ = "Ogro1"bicho.Tinimigo = New Tinimigobicho\px = 50bicho\py = 130bicho\NOME$ = "Elefante"pessoa.Tinimigo = New Tinimigopessoa\px = 140pessoa\py = 70pessoa\NOME$ = "Fada"Text soldados\px, soldados\py, soldados\NOME$Text bicho\px, bicho\py, bicho\NOME$Text pessoa\px, pessoa\py, pessoa\NOME$WaitKey

Listas de objetosComo vimos no exemplo anterior, podemos declarar muitas variáveis de um mesmo type. Mas existem situações em que possuímos vários objetos ou seres de uma mesma natureza. Por exemplo, a variável soldados; em um jogo real temos até centenas de

soldados e não apenas um. Como identificar e tratar todos eles de uma maneira simples e dinâmica?

O Blitz3D possui um mecanismo interno de listas de objetos de types que permite manipular várias instâncias de uma mesma variável de tipos. No exemplo declaramos várias vezes objetos do tipo soldados.

Exemplo 3Type TinimigoField pxField pyField NOME$End Typesoldados.Tinimigo = New Tinimigosoldados\px = 170soldados\py = 100soldados\NOME$ = "Ogro1"soldados.Tinimigo = New Tinimigosoldados\px = 100soldados\py = 130soldados\NOME$ = "Ogro2"soldados.Tinimigo = New Tinimigosoldados\px = 50soldados\py = 140soldados\NOME$ = "Ogro3"WaitKey

Veja que nesse exemplo declaramos três vezes a variável “soldados” e na verdade não estamos redefinindo a mesma variável, estamos criando uma lista do tipo “soldados” com 3 elementos.

Acessando dados da listaA maneira mais fácil de acessar os dados de uma lista de objetos de types é por meio de um loop do tipo For-Each-Next. Esse tipo de loop detecta automaticamente quantos elementos existem dentro de uma lista e passa por todos eles, um por um.

Exemplo 4Type TinimigoField pxField pyField NOME$End Typesoldados.Tinimigo = New Tinimigosoldados\px = 170soldados\py = 100soldados\NOME$ = "Ogro1"soldados.Tinimigo = New Tinimigosoldados\px = 100soldados\py = 130

24

Page 25: Modulo 2

soldados\NOME$ = "Ogro2"soldados.Tinimigo = New Tinimigosoldados\px = 50soldados\py = 140soldados\NOME$ = "Ogro3"For soldados.Tinimigo = Each TinimigoText soldados\px, soldados\py, soldados\NOME$Next WaitKey

Veja que a sintaxe desse tipo de loop é bem simples “variável.type = Each type”

Acessando um objeto específico em uma listaPara acessar um elemento específico de uma lista você pode fazer a busca por algum dado dele, normalmente o seu nome ou sua ID, com a ajuda de um comando de comparação “If”. Em jogos provavelmente você também deverá fazer busca pela posição dele ou por um dado que diga qual é sua área de atuação, pois provavelmente você deseje usar a IA de inimigos apenas que estejam no mesmo quadrante que o personagem principal do jogo, o qual a câmera segue.Mas independente de como você irá fazer a identificação o mecanismo é o mesmo, um loop For-Each-Next e um comando If. No exemplo abaixo fazemos um busca pelo “Ogro2” e só ele será usado.

Exemplo 5Type TinimigoField pxField pyField NOME$End Typesoldados.Tinimigo = New Tinimigosoldados\px = 170soldados\py = 100soldados\NOME$ = "Ogro1"soldados.Tinimigo = New Tinimigosoldados\px = 100soldados\py = 130soldados\NOME$ = "Ogro2"soldados.Tinimigo = New Tinimigosoldados\px = 50soldados\py = 140soldados\NOME$ = "Ogro3"For soldados.Tinimigo = Each TinimigoIf soldados\NOME$ = "Ogro2"Text soldados\px, soldados\py, soldados\NOME$EndIfNext

WaitKey

É claro que em um jogo a estrutura não será essa. Provavelmente você terá uma função apenas para pegar a identidade dos personagens e várias para tratar o comportamento deles. Então o exemplo abaixo, que usa estruturas abstratas e reutilizáveis é o mais correto para um ambiente real de jogo.

Exemplo 6Type TinimigoField pxField pyField NOME$End Typesoldados.Tinimigo = New Tinimigosoldados\px = 170soldados\py = 100soldados\NOME$ = "Ogro1"soldados.Tinimigo = New Tinimigosoldados\px = 100soldados\py = 130soldados\NOME$ = "Ogro2"soldados.Tinimigo = New Tinimigosoldados\px = 50soldados\py = 140soldados\NOME$ = "Ogro3"Exibe("Ogro2")WaitKeyFunction Exibe(identidade$)For soldados.Tinimigo = Each TinimigoIf soldados\NOME$ = identidade$Text soldados\px, soldados\py, soldados\NOME$EndIf NextEnd Function

O método para obter a identidade depende do tipo de jogo e de qual ação está em curso. As vezes poderá ser apenas uma localização geográfica, no caso de aplicar a IA em soldados inimigos; ou poderá ser detecção de colisões; poderá se o fato de o personagem estar em sua frente em uma situação de diálogo; para abrir uma porta ou ligar um dispositivo. Existem infinitas possibilidades de implementação.

USANDO TYPES PARA DINAMIZAR NOSSO JOGOFizemos algumas adaptações no nosso jogo do F15, tirando os comandos de linhas simples e implementando os nossos protagonistas por meio de types. A princípio isso trouxe uma pequena redução de código,

25

Page 26: Modulo 2

afinal o jogo ainda não é muito complexo, mas a medida que um programa como esse vai crescendo, fica muito difícil implementar o mesmo sem o uso types, pois teríamos que criar milhares de linhas para fazer coisas bem simples.

Exemplo 7Graphics 800, 600SetBuffer BackBuffer()Type TnaveField imgField pxField pyEnd TypeGlobal Nave.Tnave = New TnaveNave\img = LoadImage("midia/f15.bmp")Nave\px = 380Nave\py = 450Type TmeteoroField imgField pxField pyEnd TypeMeteoro.Tmeteoro = New TmeteoroMeteoro\img = LoadImage("midia/pedra1.png")Meteoro\px = Rnd(0,700)Meteoro\py = -Rnd(100,700)Meteoro.Tmeteoro = New TmeteoroMeteoro\img = LoadImage("midia/pedra2.png")Meteoro\px = Rnd(0,700)Meteoro\py = -Rnd(100,700)Meteoro.Tmeteoro = New TmeteoroMeteoro\img = LoadImage("midia/pedra3.png")Meteoro\px = Rnd(0,700)Meteoro\py = -Rnd(100,700)Meteoro.Tmeteoro = New TmeteoroMeteoro\img = LoadImage("midia/pedra1.png")Meteoro\px = Rnd(0,700)Meteoro\py = -Rnd(100,700)Meteoro.Tmeteoro = New TmeteoroMeteoro\img = LoadImage("midia/pedra2.png")Meteoro\px = Rnd(0,700)Meteoro\py = -Rnd(100,700)Global PONTOS = 0While Not KeyHit(1)Controle()Fisica()If Colide() Then GameOver()Logica() Draw() WendEndFunction Controle()If KeyDown(203) nave\px = nave\px - 1

If KeyDown(205) nave\px = nave\px + 1End FunctionFunction Logica()Pontos = Pontos + 1End FunctionFunction Fisica()For This.Tmeteoro = Each TmeteoroThis\py = This\py + 3If This\py > 600This\px = Rnd(0,700)This\py = -Rnd(100,500)End IfNext End FunctionFunction Draw()DrawImage Nave\img, Nave\px, Nave\pyFor This.Tmeteoro = Each TmeteoroDrawImage This\img, This\px, This\py Next Color 255,0,0Text 10, 10, "PONTOS: " + PONTOSFlipClsEnd FunctionFunction Colide()For This.Tmeteoro = Each TmeteoroIf ImagesCollide(nave\img,nave\px,nave\py,0,This\img,This\px,This\py,0) Then Return 1 Next End FunctionFunction GameOver()ClsText 350, 290, "GAME OVER"Text 310, 310, "Você fez " + PONTOS + " Pontos"FlipDelay(3000)End End Function

Criamos dois types, um para controlar a nave e outro para os meteoros.Type TnaveField imgField pxField pyEnd TypeType TmeteoroField imgField pxField pyEnd Type

Eles possuem a mesma estrutura, mas optamos por criar dois types para que eles não sejam objetos da mesma lista, caso contrário a nave seria incluída na lista dos meteoros. Dai teríamos que criar um campo para identidade e criar um comando If para identificar a identidade e dar o comportamento correto para cada tipo de

26

Page 27: Modulo 2

entidade. Como você já percebeu, é bem mais simples separar entidades lógicas diferentes em types diferentes.Veja as linhas abaixoGlobal Nave.Tnave = New TnaveMeteoro.Tmeteoro = New Tmeteoro

A nave é declarada como global e os meteoros não. Isso se dá porque a nave será acessada diretamente da variável dentro de funções.Function Controle()If KeyDown(203) nave\px = nave\px - 1If KeyDown(205) nave\px = nave\px + 1End Function

Mas os objetos meteoros serão acessados sempre via lista, por meio do loop For-Each e as listas tem escopo global.For This.Tmeteoro = Each TmeteoroDrawImage This\img, This\px, This\py Next

Nada impede que a nave seja acessada por um laço For-Each, mas é lógico que estaríamos usando um esforço desnecessário, afinal esse tipo possui apenas um objeto.

INTERAGINDO COM O JOGADORNesse momento vamos fazer um estudo específico sobre os mecanismos de interação entre o jogo e o jogador. Como você sabe, um jogo não é realmente um jogo se não existir a participação de uma pessoa fazendo escolhas, interagindo com o aplicação. Existem três formas principais de um jogador interagir com um jogo: pelo teclado, pelo mouse e por meio de joysticks.

Quando uma tecla for pressionadaPor meio do comando “KeyHit” fazemos a verificação se uma tecla foi pressionada. Perceba o verbo, esse comando verifica se “foi” pressionada e não se “está” pressionada. Isso é, ele só dá uma resposta por cada vez que pressionamos uma tecla. Para ter mais respostas você precisará pressionar mais vezes.Esse tipo de controle é importante para tiros intermitentes. Cada vez que você pressiona a tecla sai apenas um tiro. Veja o exemplo abaixo, onde cada vez que a tecla 57 (barra de espaço) é pressionada é exibida a mensagem “Tiro Disparado” na tela.

Exemplo 8While Not KeyHit(1)Text 50,10, "Tecle Espaço para testar e Esc para sair"If KeyHit(57) Then Print "Tiro Disparado" WendEnd

Vamos dar um exemplo um pouco mais sofisticado, onde controlamos as teclas direcionais, as setas do teclado. Para cada tecla diferente pressionada, temos uma resposta diferente.

Exemplo 9Graphics 640, 480While Not KeyHit(1)Text 200,10, "Esc -> sair, Direcionais -> testar"If KeyHit(200) Then Print "Para cima"If KeyHit(208) Then Print "Para baixo"If KeyHit(203) Then Print "Para esquerda"If KeyHit(205) Then Print "Para direita" Wend

Antigamente, quando eu ainda estava iniciando na programação (isso realmente faz bastante tempo!) os computadores pessoais não tinham um grande poder de processamento e era algo muito comum programadores fazerem jogos com elementos gráficos primitivos como caracteres ASCII.O exemplo abaixo cria uma nave a partir de elementos de textos e usa a tecla barra de espaço para disparar um míssil. Embora esse tipo de jogo seja pobre graficamente, é muito rico em criatividade e vai lhe mostra como se usa o KeyHit para disparar um tiro.

Exemplo 10Graphics 640, 480SetBuffer BackBuffer()y = 454chave = 0While Not KeyHit(1)Text 10,10, "Tecle Esc para sair e Barra de espaço para disparar"Text 320, y, "^"Text 320, y+2, "|"Text 312, 458, "X X"; disparoIf KeyHit(57) chave = 1y = 454

27

Page 28: Modulo 2

EndIfIf chave = 1 Then y = y - 2FlipClsWend

Divertido isso não é? O que espero desse exemplo é que você entenda como funciona ao comando “KeyHit” e esse exemplo é apenas uma ilustração de um dos usos que podemos fazer dele.

Se uma tecla está pressionadaAo contrário do KeyHit o comando KeyDown verifica se uma tecla está pressionada e retorna verdadeiro caso esteja. Assim, enquanto a tecla estiver pressionada ele causa ação. Esse comando é próprio para eventos contínuos como caminhar ou correr, onde ficar apertando a tecla para cada passo é incoerente. Assim, com o uso de KeyDown, enquanto a tecla estiver pressionada o personagem caminha.No exemplo abaixo perceba que basta manter a tecla pressionada para que a mensagem seja exibida de forma ininterrupta.Exemplo 11Print "Esc -> Sair; Space -> Teste"While Not KeyHit(1) If KeyDown(57) Print "esta pressionada!"Print "------------------"EndIfWendEnd

No próximo exemplo, adaptamos o código na nave de textos para que ela possa se locomover lateralmente por meio do uso das setas direcionais esquerda e direita.Exemplo 12Graphics 640, 480SetBuffer BackBuffer()y = 454x = 320chave = 0While Not KeyHit(1)Text 10,10, "Tecle Esc para sair, Space para disparar, setas para mover"Text x, y, "^"Text x, y+2, "|"Text x-8, 458, "X X"; disparoIf KeyHit(57) chave = 1y = 454EndIfIf chave = 1 Then y = y - 2

;moveIf KeyDown(203) Then x = x - 1If KeyDown(205) Then x = x + 1FlipClsWend

Como descobrir os números das teclasOs números das teclas do Blitz3D não têm nenhuma relação com a tabela de caracteres ASCII. Mas não se preocupe, pois ele tem uma ferramenta interna para fornecer o numero de cada tecla.Clique com o mouse na imagem da casinha.

Clique agora em Command Reference.

O passo agora é escolher Scancodes Picker

Pronto! Aparece o teclado abaixo.

Aperte com o mouse em uma tecla e veja em cima qual é o número dela.Inputs de mouseAntes de entrarmos em entradas de dados pelo mouse é interessante que aprendamos um output dele, isso é, uma saída. Provavelmente e seu jogo você irá criar botões e talvez menus. Para usar esses recursos é fundamental saber onde está posicionado o mouse. Podemos obter esses dados da posição x e y do mouse por meio dos comandos MouseX() e MouseY(). O ponteiro do mouse só vai aparecer em modo de janela. Portanto se o seu jogo vai rodar em FullScreen você vai ter que providenciar um ponteiro por meio de uma

28

Page 29: Modulo 2

imagem ou por meio de um caractere. No exemplo abaixo usamos o caractere “+”.Exemplo 13While Not KeyHit(1)x = MouseX()y = MouseY()Text 10, 10, "Posição X: " + x Text 10, 20, "Posição y: " + y Text x, y, "+"FlipCls Wend

Nesse exemplo provavelmente o mouse aparece, pois está em modo de janela. No exemplo abaixo vamos rodar em modo Fullscreen. Se o Debug estiver ligado, desabilite o mesmo pois ele força o programa a ser executado em modo de janela.Exemplo 14Graphics 800, 600While Not KeyHit(1)x = MouseX()y = MouseY()Text 10, 10, "Posição X: " + x Text 10, 20, "Posição y: " + y Text x, y, "+"FlipCls Wend

Usando o mouse para movimentar personagensPodemos usar os dados obtidos do mouse para posicionar um personagem ou então para o movimentar o mesmo. Em um caso, dizemos que a posição de uma nave, por exemplo, é igual à posição x e y do mouse; em outro, mais típicos de rpg, quando o mouse é clicado, pegamos a posição atual do ponteiro e fazemos dela a posição de destino do personagem. Como o segundo caso é mais complexo, vamos estudá-lo em capítulos mais avançado. O programa abaixo demonstra uma das formas de usar o mouse para movimentar um personagem.Exemplo 15Graphics 800,600,32,1nave = LoadImage("midia\f15.bmp")While Not KeyHit(1)x = MouseX()y = MouseY()DrawImage nave, x, yFlipClsWend

Se um botão do mouse foi pressionadoPodemos capturar o pressionar de um botão do mouse por meio do comando MouseHit. Ele retorna verdadeiro apenas uma vez para cada vez que um botão é pressionado. Devemos testar qual botão está sendo pressionado por meio de seu número. Veja os números de cada botão:1 – botão esquerdo.2 – botão direito.3 – botão do meio.O exemplo abaixo mostra como esse comando é utilizado.Exemplo 16While Not KeyHit(1) If MouseHit(1) Then Print "Botão Esquerdo Pressionado!"If MouseHit(2) Then Print "Botão Direito Pressionado!"If MouseHit(3) Then Print "Botão do Meio Pressionado!"Wend

Esse comando tem inúmeras utilidades, desde a construção de interface gráfica (verificar se um botão foi pressionado), como disparar eventos (abrir uma porta, acionar uma alavanca) ou mesmo disparar tiros intermitentes em um jogo.Se um botão do mouse está pressionadoPor meio do comando MouseDown fazemos uma verificação se um botão do mouse está pressionado e ele retornará verdadeiro enquanto este estiver pressionado produzindo ação contínua. Veja o exemplo anterior modificado para esse tipo de evento. Exemplo17While Not KeyHit(1) If MouseDown(1) Then Print "Botão Esquerdo Pressionado!"If MouseDown(2) Then Print "Botão Direito Pressionado!"If MouseDown(3) Then Print "Botão do Meio Pressionado!"Print “-----------------------------“Wend

Usando mouse para interagir com botõesO exemplo abaixo mostra como usar os comandos mousex, mousey e mousehit para construir um sistema de botões que provavelmente você vai usar em seus jogos.Exemplo 18Graphics 800, 600ligado = -1While Not KeyHit(1)

29

Page 30: Modulo 2

x = MouseX()y = MouseY()K = MouseHit(1)Color 180,180,180Rect 350,400, 100, 40,1If (x > 350) And (x < 450)If (y > 400) And (y < 440)Color 120,120,120Rect 350,400, 100, 40,1If K Then ligado = ligado * (-1)EndIfEndIfColor 0,255,0If ligado = 1 Then Text 270, 200, "Botão ativado, aperte denovo!"Text x-5, y-5, "+"FlipClsWend

Explicando...ligado = -1

Criamos uma chave para indicar se o botão está ligado ou desligado. Deixamos seu estado inicial como “-1” ou desligado.x = MouseX()y = MouseY()

Aqui estamos pegando a posição atual do mouse e guardando suas coordenadas nas variáveis x e y.K = MouseHit(1)

Nessa linha verificamos se o botão do mouse foi pressionado e guardamos esse resultado na variável k.Color 180,180,180

Estamos alterando a cor da paleta de desenho para um cinza claro.Rect 350, 400, 100, 40, 1

Estamos desenhando um retângulo que será o nosso botão; isso é feito pelo comando Rect. Ele é desenhado na posição 350,400 e tem 100 pontos de largura e 40 de altura. Seu tipo de preenchimento é 1, isso é cheio.If (x > 350) And (x < 450)

Aqui inicia-se a verificação se o ponteiro do mouse está sobre o botão. O Botão inicia-se em 350 e tem 100 pontos de comprimento. Por isso verificamos se a variável X que contém a posição x atual do mouse está dentro desse limite, isso é, se ele é maior que 350 e menor que 450.If (y > 400) And (y < 440)

Se a posição x do mouse está enquadrada dentro do botão, passamos a verificar a posição y do mesmo, pois para a seta do mouse estar localizada dentro, ambos x e y

devem estar dentro desses limites. Como o botão é desenhado na posição 400 e tem 40 de altura, então ele terá que estar entre 400 e 440, isso é, deve ser maior que 400 e menor que 440.Color 120,120,120Rect 350,400, 100, 40,1

Se as variáveis de localização do ponteiro do mouse passaram no dois testes então desenhamos um novo retângulo sobre o anterior, mas com cor mais escura para indicar que ele está em foco.If K Then ligado = ligado * (-1)

Ainda dentro da verificação, agora é hora de saber se o botão esquerdo do mouse foi pressionado. As condições são: se a coordenada x do mouse estiver dentro do botão, e se a coordenada y do mouse estiver dentro do botão e se o botão esquerdo do mouse for pressionado (If K), então inverta a chave. Isso mesmo, inverta, pois a multiplicação de um número por (-1) da o seu valor inverso. Se era -1 vai se tornar 1 e se é 1 vira -1.Color 0,255,0If ligado = 1 Then Text 270, 200, "Botão ativado, aperte denovo!"Text x-5, y-5, "+"

O que fazemos nessa seqüência é: primeiro mudamos a cor da paleta para que a mensagem e o cursor do mouse tenham cores diferentes do botão. Depois exibimos a mensagem se o botão estiver ligado. E por último exibimos um cursor para o mouse. Perceba que fizemos um pequeno ajuste para coincidir com o meio do caractere.Em um jogo o ideal é que você faça seus botões a partir de imagens como .bmp ou .jpg. Isso vai deixar seus botões muito mais elegantes e são muito mais leves de processar.A velocidade do mousePodemos detectar a velocidade com a qual o mouse se movimenta e usarmos esse dado para dar um controle mais preciso à manipulação de nossos personagens e objetos num jogo. Podemos fazer isso por meio do uso dos comandos MousexSpeed e MouseySpeed.Exemplo 19Graphics 800, 600, 32, 1While Not KeyHit(1)

30

Page 31: Modulo 2

x = MouseX()y = MouseY()Text x, y, "+"vx = MouseXSpeed()vy = MouseYSpeed()Text 10,10, "Velocidade X: " + vxText 10,20, "Velocidade Y: " + vyFlipClsWend

MouseXSpeed se refere a velocidade que o mouse se locomove na sentido horizontal da tela, isso é, direção x. O MouseYSpeed se refere ao movimento vertical. Usando MouseSpeed em jogosEsse comando é fantástico para dar uma perfeita movimentação a personagens de jogos e para gerar efeitos de física, afinal ele captura com que velocidade o jogador manipula o mouse. Isso pode ser usando tanto para velocidade de um personagem quanto para indicar a força com a qual se desfere um golpe.No exemplo abaixo usamos o MouseSpeed para mover a nave.Exemplo 20Graphics 800,600,32,1nave = LoadImage("midia\f15.bmp")px = 370py = 400MoveMouse 400,300While Not KeyHit(1)px = px + MouseXSpeed()py = py + MouseYSpeed()If px > 750 Then px = 750If px < 0 Then px = 0If py > 550 Then py = 550If py < 0 Then py = 0DrawImage nave, px, pyFlipClsWend

Na linha abaixo colocamos o ponteiro do mouse no centro da tela para permitir um melhor controle do mesmo.MoveMouse 400,300

Veja que as posições px e py são dadas por um ajuste da posição atual mais a velocidade atual do mousepx = px + MouseXSpeed()py = py + MouseYSpeed()

Outra coisa que temos que fazer é limitar até onde a nave pode ir. Ela não deve atravessar os limites laterais da tela. Fisemos esse controle nas linhas abaixo.If px > 750 Then px = 750

If px < 0 Then px = 0If py > 550 Then py = 550If py < 0 Then py = 0

Atualizando o Jogo F15 para ser controlado por mouseEstamos fazendo algumas atualizações no nosso jogo. Agora a nave será controlado por mouse. Com essa atualização o controle de nosso avião vai ficar bem mais dinâmico e a quantidade de meteoros se tornará muito pouco, deixando o jogo muito fácil. Para superar esse problema, vamos cria um "motor" de criação e de destruição automática de pedras . Para isso usaremos os Types, pois essa estrutura é a única que permite fazer alocação dinâmica de memória, isso é, podemos criar de destruir entidade na quantidade que desejarmos, e em tempo de execução do jogo. Assim, nosso motor criará entidades de forma aleatória e as destruirá quando não mais as usar, isso é, quando saírem da área visível da tela.

Exemplo 21Graphics 800, 600SetBuffer BackBuffer()Type TnaveField imgField pxField pyEnd TypeType TmeteoroField imgField pxField pyEnd TypeGlobal Nave.Tnave = New TnaveNave\img = LoadImage("midia/f15.bmp")Nave\px = 380

31

Page 32: Modulo 2

Nave\py = 450Global img1 = LoadImage("midia/pedra1.png")Global img2 = LoadImage("midia/pedra2.png")Global img3 = LoadImage("midia/pedra3.png")Global PONTOS = 0MoveMouse 400,300While Not KeyHit(1)CriarPedras()Controle()Fisica()If Colide() Then GameOver()Logica() Draw() WendEndFunction CriarPedras()sorte = Rnd(0,20)If sorte = 1Meteoro.Tmeteoro = New Tmeteoro Meteoro\px = Rnd(0,700)Meteoro\py = -Rnd(100,700)IMG = Rnd(1,3)If IMG = 1 Then Meteoro\img = img1If IMG = 2 Then Meteoro\img = img2If IMG = 3 Then Meteoro\img = img3EndIfEnd FunctionFunction Controle()Nave\px = Nave\px + MouseXSpeed() End FunctionFunction Logica()Pontos = Pontos + 1End FunctionFunction Fisica()For This.Tmeteoro = Each TmeteoroThis\py = This\py + 3If This\py > 600Delete ThisEnd IfNext End FunctionFunction Draw()DrawImage Nave\img, Nave\px, Nave\pyFor This.Tmeteoro = Each TmeteoroDrawImage This\img, This\px, This\py Next Color 255,0,0Text 10, 10, "PONTOS: " + PONTOSFlipClsEnd FunctionFunction Colide()For This.Tmeteoro = Each TmeteoroIf ImagesCollide(nave\img,nave\px,nave\py,0,This\img,This\px,This\py,0) Then Return 1 Next End FunctionFunction GameOver()

ClsText 350, 290, "GAME OVER"Text 310, 310, "Você fez " + PONTOS + " Pontos"FlipDelay(3000)End End Function

A primeira mudança que fizemos foi no conceito de como usar as imagens dos meteoros. Os nossos meteoros agora serão instanciados dinamicamente em tempo de jogo, isso é, não serão carregados como antes, no inicio do jogo, mas serão criados enquanto o jogo estiver em andamento. Por isso não é mais uma boa idéia carregar as imagens direto do arquivo para o type do meteoro. Decidimos estão carregar as imagens em variáveis globais e no objeto meteoro deixaremos apenas uma referência, isso é, o campo do meteoro vai apontar para uma dessas três variáveis e não mais conterá as imagens. Assim, carregaremos apenas 3 imagens e nosso jogo vai fluir de maneira mais suave, sem lags.Global img1 = LoadImage("midia/pedra1.png")Global img2 = LoadImage("midia/pedra2.png")Global img3 = LoadImage("midia/pedra3.png")

Como nosso jogo agora será controlado por mouse, colocamos o mouse no centro da tela.MoveMouse 400,300

Nosso loop do jogo recebeu uma nova função, a função CriarPedras().While Not KeyHit(1)CriarPedras()Controle()Fisica()If Colide() Then GameOver()Logica() Draw() Wend

A principal alteração no nosso jogo se deu na criação de nosso motor construtor de meteoros. Veja o código abaixo.Function CriarPedras()sorte = Rnd(0,20)If sorte = 1Meteoro.Tmeteoro = New Tmeteoro Meteoro\px = Rnd(0,700)Meteoro\py = -Rnd(100,700)IMG = Rnd(1,3)If IMG = 1 Then Meteoro\img = img1

32

Page 33: Modulo 2

If IMG = 2 Then Meteoro\img = img2If IMG = 3 Then Meteoro\img = img3EndIfEnd Function

Como você já percebeu, a função é invocada a cada iteração, isso é, mais ou menos 60 vezes por segundo. Para que não sejam criados milhares de meteoros, condicionamos a sua criação a um sorteio sorte = Rnd(0,20). Caso seja sorteado o número 1, ai será criado um novo meteoro. A posição x do meteoro é sorteada Meteoro\px = Rnd(0,700) assim como também a sua posição y Meteoro\py = -Rnd(100,700). A imagem do meteoro também é dada por sorteio IMG = Rnd(1,3). Dai fazemos uma referência a uma das três variáveis globais que possuem imagem Meteoro\img = img1.Como agora controlamos a nave pelo mouse, a função Controle(), sofreu alterações.Function Controle()Nave\px = Nave\px + MouseXSpeed() End Function

A ultima alteração foi na nossa função de física, que foi usada para destruir meteoros.Function Fisica()For This.Tmeteoro = Each TmeteoroThis\py = This\py + 3If This\py > 600Delete ThisEnd IfNext End Function

Quando um meteoro passa o limite da tela If This\py > 600 ele é destruído com o comando Delete: Delete This.Parabéns!!!!Você acaba de entender um dos mais fantásticos conceitos de desenvolvimento de jogos: motores de criação e destruição dinâmica de entidades. Sem essa estratégia é impossível criar jogos complexos e seria muito difícil gerenciar qualquer tipo de jogo. Com esse algoritmo você pode criar jogos infinitos, com inteligência artificial extremamente complexa, com pouco esforço de trabalho e com ótima economia de processamento.Como você verá a frente, esse recurso também é o ideal para disparar tiros. Os tiros serão criados e destruídos com pouquíssimo esforço e em poucas linhas.Inputs de Joysticks

O Blitz3D também oferece uma série de recursos de suporte a joysticks, os controles típicos de videogames. Esses recursos são muito importantes, pois alguns jogos que necessitam de maior controle são mais agradáveis de serem jogados com joysticks.Estou fazendo esses exemplos para um joystick de oito botões. Mas não se preocupe, pois isso se adapta a qualquer tipo de modelo.Se um botão foi pressionadoPor meio do comando JoyHit podemos fazer uma varedura no joystick para verificamos se um botão foi pressionado. Esse comando só dá um retorno por cada vez que o botão for pressionado. É o comando típico de controle de tiros intermitentes.Exemplo 22While Not KeyHit(1)If JoyHit(1) Print "botão 1 pressionado"If JoyHit(2) Print "botão 2 pressionado"If JoyHit(3) Print "botão 3 pressionado"If JoyHit(4) Print "botão 4 pressionado"If JoyHit(5) Print "botão 5 pressionado"If JoyHit(6) Print "botão 6 pressionado"If JoyHit(7) Print "botão 7 pressionado"If JoyHit(8) Print "botão 8 pressionado"Wend

Se um botão está pressionadoPor meio do comando JoyDown fazemos uma verificação se algum botão do joystick está pressionado, dando retornos contínuos enquanto ele estiver neste estado. Veja o exemplo abaixo.Exemplo 23While Not KeyHit(1)If JoyDown(1) Print "botão 1 pressionado"If JoyDown(2) Print "botão 2 pressionado"If JoyDown(3) Print "botão 3 pressionado"If JoyDown(4) Print "botão 4 pressionado"If JoyDown(5) Print "botão 5 pressionado"If JoyDown(6) Print "botão 6 pressionado"If JoyDown(7) Print "botão 7 pressionado"If JoyDown(8) Print "botão 8 pressionado"Wend

Qual botão foi pressionado?

33

Page 34: Modulo 2

Com o comando GetJoy, em vez de testar um botão específico, podemos fazer um teste genérico onde ele retorna o número do botão pressionado. Ele é muito importante para dar a opção de o jogador escolher qual botão irá usar para qual ação. O exemplo abaixo dá uma idéia de como fazer isso. Não se assuste com o tamanho, pois a única coisa nova ai é o GetJoy().Exemplo 24Graphics 800, 600btn_tiro = 1btn_bomba = 2tiro = 1bomba = 2While Not KeyHit(1)If JoyHit(btn_tiro) Print "Tiro" + tiro + " disparado"tiro = tiro + 1EndIf If JoyHit(btn_bomba) Print "Bomba" + bomba + " disparada"bomba = bomba + 1EndIf Text 250, 10, "pressione space para configurar"Text 250, 30, "Tiro BTN: " + btn_tiroText 250, 45, "Bomba BTN: " + btn_bombaIf KeyHit(57)chave = 0While (chave = 0)Text 250, 10, "Pressione um botão para o tiro e Space para sair"Text 250, 30, "Tiro BTN: " + btn_tiroa = GetJoy() If a > 0 Then btn_tiro = aIf KeyHit(57) Then chave = 1FlipClsWendchave = 0While (chave = 0)Text 250, 10, "Pressione um botão para Bomba e Space para sair"Text 250, 30, "Bomba BTN: " + btn_bombaa = GetJoy() If a > 0 Then btn_bomba = aIf KeyHit(57) Then chave = 1FlipClsWendEndIfWend

Movendo Com joystickOs comandos JoyX() e JoyY() são responsáveis pelo controle dos eixos direcionais do joystic. Eles retornam os valores "1" e " -1" quando esses eixos são

acionados, assim indicam se a posição x e y devem ser incrementadas ou diminuídas. O programa abaixo controla o helicóptero por meio do jopystic.

Exemplo 25Graphics 800, 600px = 400py = 300nave = LoadImage("midia\heli.bmp")While Not KeyHit(1) Cls JX = JoyX() * 2 JY = JoyY() * 2 Px = Px + JXPy = Py + JyDrawImage nave, Px, PyFlipWend

Como esse comando retorna 1 ou -1, estamos multiplicando o mesmo por 2 para que o helicóptero possa se mover mais rápido. JX = JoyX() * 2 JY = JoyY() * 2

Persistência Até agora só aprendemos a colocar dados na memória latente, isso é, na memória ram. Quando criamos uma variável ou um type estamos fazendo uma reserva de uma parte da memória ram para guardar nossos dados. Mas esse tipo de dados são muito voláteis, pois funcionam por meio de dispositivos elétricos que simulam estruturas lógicas. Quando o programa é encerrado ou o computador é desligado esse tipo de dado se perde.Em jogos existem certos tipos de dados que não podem ser perdidos; entre eles estão os placares de melhores partidas ou os arquivos de jogos salvos. Para que possamos

34

Page 35: Modulo 2

recuperar esse tipo de dados temos que usar outra técnica: a persistência.Para gerar dados persistentes devemos colocar esses dados em dispositivos elétrico-magnéticos, como disquetes, discos rígidos e fitas de dados ou em outros tipos de dispositivos que possam por algum meio físico ou químico conservar o estado de dados, mesmo sem o fornecimento constante de energia.O Blitz3D pode gerar arquivos de texto ou binários para criar dados persistentes. Nosso estudo de como o Blitz faz isso vai ser bem objetivo para nos capacitar a criar persistência para índices de escores, arquivos de dados e salvar estados de jogos. Portanto, não iremos abordar todos os recursos que essa ferramenta possui para lidar com dados, pois isso levaria pelo menos uma centena de páginas.Manipulando StringsPor meio dos comandos de manipulação de arquivos podemos gravar uma ou mais variáveis de strings em um arquivo de dados. O exemplo abaixo grava uma string em um arquivo.Exemplo 26arquivo_aberto = WriteFile("meuarquivo.ovo") WriteString(arquivo_aberto, "meu primeiro dado") CloseFile(arquivo_aberto)Print "Gravando..."WaitKey

Vamos à explicação...arquivo_aberto = WriteFile("meuarquivo.ovo")

O que essa linha faz é abrir um arquivo para gravar dados nele. O comando usado para abrir um arquivo para gravação é WriteFile( ). Só podemos escrever dados em um arquivo se ele for aberto com o método de gravação. Existem métodos de abertura de arquivos apenas para leitura, veremos isso depois. Quando usamos o comando WriteFile ele procura pelo arquivo indicado. Se não encontrar ele cria um novo arquivo. Caso encontre um arquivo ele o abre para gravação, portanto todo seu conteúdo é sobrescrito. Se não gravarmos nada, ele fica em branco.

O nome do arquivo "meuarquivo.ovo" é de nossa escolha e podemos dar a extensão que quisermos. Nesse exemplo não colocamos endereçamento do arquivo, isso é, não dissemos em que pasta ele está, assim o Blitz3D compreende que o arquivo está, ou deve ser criado no mesmo local que está gravado o código fonte do programa, isso é, na sua pasta de projetos. Portanto se você criou um arquivo de código novo e ainda não o salvou, isso pode dar problema, pois ele vai entender que o diretório de projeto é o diretório raiz do Blitz3D, onde estão os arquivos desse programa.Podemos gerenciar onde um arquivo será salvo ou de onde será lido por meio das resoluções de endereço absoluto ou relativo, por exemplo “dados\meuarquivo.ovo” ou “C:\projetodoovo\dados\meuarquivo.ovo”. Esses comandos não criam diretórios. Se os diretórios não existem o arquivo não será salvo.Perceba que quando abrimos o arquivo criamos uma variável e atribuímos o resultado da abertura a ela “arquivo_aberto =” isso se dá porque não manipulamos o arquivo por meio de seu nome no disco rígido, mas por meio de uma referência que o Blitz3D cria para ele. Perceba que a partir de agora iremos controlar esse arquivo aberto por meio dessa variável criada para ele.WriteString(arquivo_aberto, "meu primeiro dado")

É nessa linha que estamos gravando os dados de texto no arquivo. O comando WriteString() grava uma string em um arquivo. Sua forma de uso é WriteString(arquivo,string). Veja que o primeiro argumento é a variável criada na abertura do arquivo “arquivo_aberto” ela indica onde vamos gravar a string. O segundo argumento deve ser uma string ou uma variável de string; nesse caso usamos uma string literal, isso é, um texto “meu primeiro dado”.CloseFile(arquivo_aberto)

Agora que já gravamos os dados no arquivo nos resta fechar esse arquivo e é isso que o comando CloseFile faz. Como você poderá ver mais adiante, não importa com qual

35

Page 36: Modulo 2

comando você abre um arquivo, todos serão fechados por CloseFile.Print "Gravando..."

Essa linha não possui função técnica, colocamos ai apenas para informar que a operação foi concluída.Gravando várias stringsPara gravar várias strings basta colocar vários comandos WriteString(). Veja o exemplo abaixo.Exemplo 27arquivo_aberto = WriteFile("meuarquivo.ovo")WriteString(arquivo_aberto, "meu primeiro dado")WriteString(arquivo_aberto, "meu segundo dado")WriteString(arquivo_aberto, "meu terceiro dado")CloseFile(arquivo_aberto)Print "Gravando..."WaitKey

Simples não é?Como ler strings de arquivosPara ler uma string de um arquivo devemos primeiro abrir o arquivo para leitura, isso pode ser feito com o comando ReadFile() e devemos ler string por string por meio do comando ReadString(). Veja o exemplo abaixo onde vamos ler o conteúdo do arquivo criado no programa anterior.Exemplo 28arquivo_aberto = ReadFile("meuarquivo.ovo")texto$ = ReadString(arquivo_aberto)CloseFile(arquivo_aberto)Print "Conteúdo do arquivo: " + texto$WaitKey

Explicando...arquivo_aberto = ReadFile("meuarquivo.ovo")

Aqui usamos o método de abertura para leitura que não altera o conteúdo do arquivo. Perceba que colocamos a referência do arquivo em uma variável.texto$ = ReadString(arquivo_aberto)

Veja que usamos o método ReadString() para pegar a string do arquivo representado por arquivo_aberto. Colocamos essa string dentro de uma variável de strings texto$ para não perdermos o seu conteúdo.CloseFile(arquivo_aberto)

Fechamos o arquivoPrint "Conteúdo do arquivo: " + texto$

Aqui imprimimos a string lida do arquivo.Lendo várias stringsAssim como para escrever várias strings devemos usar vários WriteString, para ler várias strings deveremos usar várias vezes ReadString.Exemplo 29arquivo_aberto = ReadFile("meuarquivo.ovo")texto$ = ReadString(arquivo_aberto)Print "Conteúdo do arquivo: " + texto$texto$ = ReadString(arquivo_aberto)Print "Conteúdo do arquivo: " + texto$texto$ = ReadString(arquivo_aberto)Print "Conteúdo do arquivo: " + texto$CloseFile(arquivo_aberto)WaitKey

Manipulando InteirosPara manipular dados de inteiros em arquivos podemos proceder da mesma forma que fizemos com as strings, mas trocando os comando de gravação e de leitura para WriteInt() e ReadInt(). O exemplo abaixo grava 3 inteiros em um arquivo.Exemplo 30arquivo_aberto = WriteFile("meuarquivo.dovo")WriteInt(arquivo_aberto, 11111)WriteInt(arquivo_aberto, 22222)WriteInt(arquivo_aberto, 33333)CloseFile(arquivo_aberto)Print "Dados gravados..."WaitKey

Esses dados de inteiros podem ser acessados por meio de ReadInt(), veja no exemplo.Exemplo 31arquivo_aberto = ReadFile("meuarquivo.dovo")For x = 0 To 2dado = ReadInt(arquivo_aberto)Print "Numero lido: " + dadoNextCloseFile(arquivo_aberto)Print "Dados gravados..."WaitKey

Veja que nesse exemplo, ai invés de ficar repetindo três vezes os comando ReadInt() e Print, colocamos os mesmos dentro de um laço com três iterações.Criando um sistema de escoresVamos criar agora um exemplo de como fazer um módulo de melhores pontuações. Nesse exemplo vamos fazê-lo para apenas 5 jogadores para que não fique muito grande,

36

Page 37: Modulo 2

mas é muito fácil modificá-lo para a quantidade que você quiser.Esse exemplo será criado com arquitetura genérica, assim poderá ser reutilizado em qualquer projeto. Nessa primeira versão o fizemos com um estilo mais repetitivo, sem coleções para que o mecanismo de funcionamento seja mais fácil de ser compreendido.Exemplo 32Graphics 800, 600Global nome1$Global ponto1Global nome2$Global ponto2Global nome3$Global ponto3Global nome4$Global ponto4Global nome5$Global ponto5If FileType("ovoscores.scr") Then LerScores()While Not KeyHit(1)Text 200, 20, "------------- SORTEIO DE 0 A 1000 -------------"Text 250, 50, "Para jogar Aperte Barra de Espaço"Text 200, 450, "------------------- SCORES --------------------"Text 200, 470, "1." + nome1$ + " " + ponto1Text 200, 485, "2." + nome2$ + " " + ponto2Text 200, 500, "3." + nome3$ + " " + ponto3Text 200, 515, "4." + nome4$ + " " + ponto4Text 200, 530, "5." + nome5$ + " " + ponto5Text 200, 555, "-----------------------------------------------"If KeyHit(57)seuponto = Rnd(1000)Text 200, 200, "Você fez " + seuponto + " pontos"If seuponto > ponto5Text 200, 250, "Você está entre as melhores pontuações!"Locate 200, 300seunome$ = Input$("Digite seu nome: ")GravarScores(seunome$, seuponto)LerScores()Else Text 200, 250, "Aperte space para continuar..."While Not KeyHit(57)Wend

EndIfEndIfFlipClsWend;*********************** METODO DE LEITURA DE DADOS *************************Function LerScores()aberto = ReadFile ("ovoscores.scr")nome1$ = ReadString(aberto)ponto1 = ReadInt(aberto)nome2$ = ReadString(aberto)ponto2 = ReadInt(aberto)nome3$ = ReadString(aberto)ponto3 = ReadInt(aberto)nome4$ = ReadString(aberto)ponto4 = ReadInt(aberto)nome5$ = ReadString(aberto)ponto5 = ReadInt(aberto)CloseFile(aberto)End Function;******************** METODO DE ADIÇÃO DE DADOS **********************Function GravarScores(jogador$, pontos);pegando os dados atuais do arquivoIf FileType("ovoscores.scr")aberto = ReadFile ("ovoscores.scr")play1$ = ReadString(aberto)score1 = ReadInt(aberto)play2$ = ReadString(aberto)score2 = ReadInt(aberto)play3$ = ReadString(aberto)score3 = ReadInt(aberto)play4$ = ReadString(aberto)score4 = ReadInt(aberto)play5$ = ReadString(aberto)score5 = ReadInt(aberto)CloseFile(aberto)EndIf; fasendo seleção de dadosIf pontos > score5score5 = pontosplay5$ = jogador$EndIfIf pontos > score4score5 = score4play5$ = play4$score4 = pontosplay4$ = jogador$EndIfIf pontos > score3score4 = score3play4$ = play3$score3 = pontosplay3$ = jogador$EndIfIf pontos > score2score3 = score2play3$ = play2$score2 = pontosplay2$ = jogador$

37

Page 38: Modulo 2

EndIfIf pontos > score1score2 = score1play2$ = play1$score1 = pontosplay1$ = jogador$EndIf; gravar dodos depois da seleçãoaberto = WriteFile("ovoscores.scr")WriteString(aberto, play1$)WriteInt(aberto, score1)WriteString(aberto, play2$)WriteInt(aberto, score2)WriteString(aberto, play3$)WriteInt(aberto, score3)WriteString(aberto, play4$)WriteInt(aberto, score4)WriteString(aberto, play5$)WriteInt(aberto, score5)CloseFile(aberto)End Function

Explicando tudinho...Global nome1$Global ponto1Global nome2$Global ponto2Global nome3$Global ponto3Global nome4$Global ponto4Global nome5$Global ponto5

Nessas linhas ai declaramos 5 variáveis globais para conter os nomes de melhores jogadores e 5 variáveis para conter suas pontuações.If FileType("ovoscores.scr") Then LerScores()

Essa linha é muito importante, pois não podemos ler um arquivo que não existe, pois do contrário dará erro. O comando FileType retorna 0 se o arquivo não existe, retorna 1 se ele existe e 2 se for um diretório. Assim, essa primeira leitura ai só vai ser feita se o arquivo já foi criado.While Not KeyHit(1)Text 200, 20, "------------- SORTEIO DE 0 A 1000 -------------"Text 250, 50, "Para jogar Aperte Barra de Espaço"Text 200, 450, "------------------- SCORES --------------------"Text 200, 470, "1." + nome1$ + " " + ponto1Text 200, 485, "2." + nome2$ + " " + ponto2Text 200, 500, "3." + nome3$ + " " + ponto3

Text 200, 515, "4." + nome4$ + " " + ponto4Text 200, 530, "5." + nome5$ + " " + ponto5Text 200, 555, "-----------------------------------------------"

Nesse trecho não há segredos. Estamos iniciando o loop do jogo e exibindo informações na tela.If KeyHit(57)seuponto = Rnd(1000)Text 200, 200, "Você fez " + seuponto + " pontos"

Tudo começa com o apertar da tecla 57, a barra de espaços. Ai usamos o gerador de números aleatórios Rnd() para fazer o sorteio de pontos de 0 a 1000. Depois informamos qual foi à pontuação alcançada.If seuponto > ponto5Text 200, 250, "Você está entre as melhores pontuações!"Locate 200, 300seunome$ = Input$("Digite seu nome: ")GravarScores(seunome$, seuponto)LerScores()

Se a pontuação é maior que a menor pontuação dos melhores escores significa que você vai entrar para o Hall da fama... Então a primeira coisa a fazer é dizer que você está dentro. O que fazemos depois é pegar o nome do sortudo por meio do comando “Input”. Perceba que usamos o comando “Locate” para dizer onde queremos que o input apareça.Depois de obter o nome, passamos o nome e a pontuação para a função de gravação de scores “GravarScores(seunome$, seuponto) “. O ultimo passo é atualizar o nosso escore exibido na tela. Para isso chamamos a função ”LerScores()”.Else Text 200, 250, "Aperte space para continuar..."While Not KeyHit(57)Wend EndIf

Esse trecho ai de código só executa se sua pontuação não estiver entre as melhores. O que ele faz é simplesmente criar um mecanismo de retardo para dar tempo de ver a pontuação mesmo que ela não seja boa. Para passar por isso basta pressionar a barra de espaços.EndIf

38

Page 39: Modulo 2

FlipClsWend

Bom, isso foi o fim do nosso código principal. Faltam agora as funções.Function LerScores()aberto = ReadFile ("ovoscores.scr")nome1$ = ReadString(aberto)ponto1 = ReadInt(aberto)nome2$ = ReadString(aberto)ponto2 = ReadInt(aberto)nome3$ = ReadString(aberto)ponto3 = ReadInt(aberto)nome4$ = ReadString(aberto)ponto4 = ReadInt(aberto)nome5$ = ReadString(aberto)ponto5 = ReadInt(aberto)CloseFile(aberto)End Function

Essa função de leitura é muito simples. A única coisa que você deve ficar atento aqui é que, em primeiro lugar, montamos uma estrutura do tipo ler uma string e ler um int; então essa estrutura deve ser repetida na função de gravação de dados, isso é, gravar uma string, gravar um int... Se as duas funções não tiverem a mesma estrutura vai ocorrer um erro lógico.Perceba também que estamos carregando os dados diretamente para as variáveis globais definidas para manipular os escores do jogo. Isso significa que se você quiser usar essas funções em um projeto seu, também terá que declarar aquelas variáveis globais lá.Function GravarScores(jogador$, pontos)

Perceba que nossa função de gravar escores aceita dois parâmetros. Um deve conter o nome do jogador e o outro a sua pontuação.;pegando os dados atuais do arquivoIf FileType("ovoscores.scr")

Como você se lembra, o método de abertura de arquivos para gravação WriteFile apaga o conteúdo de um arquivo antes de gravar algo nele, por isso precisamos colher todos dos dados que existem nele antes de gravar algo. Mas perceba que se esse arquivo não existe, não poderemos e nem precisaremos fazer isso. Por isso nessa linha acima testamos para ver se ele existe, caso contrário poderia ocorrer um erro.play1$ = ReadString(aberto)score1 = ReadInt(aberto)play2$ = ReadString(aberto)score2 = ReadInt(aberto)play3$ = ReadString(aberto)

score3 = ReadInt(aberto)play4$ = ReadString(aberto)score4 = ReadInt(aberto)play5$ = ReadString(aberto)score5 = ReadInt(aberto)

Veja que usamos variáveis locais para conter temporariamente os dados lidos do arquivo. Fizemos isso para não poluir os dados globais de escores. Essas variáveis serão destruídas quanto a função chegar em EndFunction.; fazendo seleção de dadosIf pontos > score5score5 = pontosplay5$ = jogador$EndIf

Aqui começamos a fazer a seleção dos escores. Devemos comparar um por um, a partir do ultimo, para colocar o novo escore no local correto. Perceba que verificamos se a nova marca é maior que o ultimo colocado do Ranking ”If pontos > score5“, se essa marca realmente for maior então gravamos ela da ultima posição ”score5 = pontos” e o passo seguinte é colocar o nome do pontuador lá também “play5$ = jogador$”. Lembre-se que as variáveis pontos e jogador$ são aquelas que recebemos como argumento no cabeçalho da função, isso é, os dados do ultimo jogador que deverá entrar nos melhores escores.If pontos > score4score5 = score4play5$ = play4$score4 = pontosplay4$ = jogador$EndIf

Veja que a partir do penúltimo a coisa fica um pouco mais complicada, pois se o novo jogador tiver uma marca maior que a dele, não basta colocar no lugar, o antigo ocupante daquela colocação deverá ser rebaixado para a posição de baixo. De novo, a primeira coisa a se saber é se a nova pontuação é maior que a do quarto colocado do ranking “pontos > score4”. Se isso for verdadeiro devemos passar o antigo ocupante para a colocação abaixo, senão os dados dele serão perdidos. Primeiro passamos os pontos “score5 = score4” o passo seguinte foi passar o nome dele para baixo “play5$ = play4$”.

39

Page 40: Modulo 2

Agora sim poderemos colocar o novo jogador na posição 4 do ranking. Primeiro colocamos os pontos “score4 = pontos” e depois o nome “play4$ = jogador$”.If pontos > score3score4 = score3play4$ = play3$score3 = pontosplay3$ = jogador$EndIfIf pontos > score2score3 = score2play3$ = play2$score2 = pontosplay2$ = jogador$EndIfIf pontos > score1score2 = score1play2$ = play1$score1 = pontosplay1$ = jogador$EndIf

As linhas acima continuam o processo de colocar o jogador e seus pontos no local correto do ranking e rebaixando os antigos ocupantes. Elas possuem a mesma lógica do processo explicado acima e por isso você já sabe como funciona.; gravar dodos depois da seleçãoaberto = WriteFile("ovoscores.scr")WriteString(aberto, play1$)WriteInt(aberto, score1)WriteString(aberto, play2$)WriteInt(aberto, score2)WriteString(aberto, play3$)WriteInt(aberto, score3)WriteString(aberto, play4$)WriteInt(aberto, score4)WriteString(aberto, play5$)WriteInt(aberto, score5)CloseFile(aberto)End Function

Por fim, no momento final, depois de ter ajustado todos os pontuadores no local correto, basta gravar esses dados ajustados no arquivo de dados e isso você também já sabe como se faz.Note com cuidado que a forma de gravar tenha a mesma estrutura da forma de ler o arquivo, isso é:StringIntStringIntStringIntString

IntStringInt

Inclusão de arquivosFora raras exceções de profunda genialidade, bons jogos são programas bem grandes e, sendo assim, gerenciar um código fonte desses é algo bem complexo. A melhor maneira de fazer isso sem se perder no emaranhado das linhas é a técnica de “dividir para conquistar” isso é, saber criar módulos de código que efetuem tarefas específicas e que sejam separados não só logicamente (funções), mas fisicamente também (arquivos).Se nossos jogos tiverem apenas um arquivo e tiver milhares de linhas teremos pelo menos 2 inconvenientes:1. só uma pessoa pode trabalhar por vez.2. passaremos muitas horas do projeto navegando e procurando os módulos dentro do código fonte.Para solucionar isso as linguagens de programação mais recentes permitem quem sejam criados arquivos externos de código fonte que podem ser incluídos no projeto. As principais vantagens dessa técnica são.1. Melhor arquitetura do projeto.2. Modularização do software.3. Maior facilidade para corrigir erros e fazer modificações4. Possibilidade de trabalhar em equipes5. Software desacoplado6. Reuso de softwarePerceba que só existem vantagens em se trabalhar assim.Como incluir arquivosPara incluir arquivos devemos primeiro possuir um arquivo principal no projeto, que normalmente terá o nome do jogo ou então se chamar “game.bb”, ou “principal.bb” ou algum outro nome que o identifique como o arquivo principal, e dentro dele darmos o comando de inclusão Include “arquivo.bb”. Lembre-se que o programa só poderá ser executado e compilado a partir do arquivo principal senão não funcionará.Exemplo práticoEm nosso exemplo prático vamos criar um simples jogo de dados com primitivas gráficas 2D. Nosso jogo possuirá 2 arquivos:

40

Page 41: Modulo 2

o principal que será o arquivo “game.bb” e o arquivo auxiliar que será o arquivo “dado.bb”.A estrutura de controle do jogo em si ficará no arquivo principal e o arquivo a ser incluído será responsável por gerenciar o dado. Isso é, ele será construído segundo o princípio da coesão, pelo qual ele só cuidará de uma coisa específica: o dado, suas propriedades e seu comportamento. Essa é a chave do sucesso... enquanto o arquivo principal cuida da lógica geral do jogo, como um maestro, os arquivos incluídos são específicos, cada um cuida de um objeto ou um responsabilidade determinada.Mas vamos a parte prática.

Arquivo “Game.bb”Graphics 640, 480SetBuffer BackBuffer()Include "Dado.bb"While Not KeyHit(1)Shows()If KeyHit(57) Then Dado_Jogar()Dado_Exibir()FlipClsWendEndFunction Shows()Text 50,10, "**************************************************************"Text 50,22, "************************* DADOS ****************************"Text 50,34, "**************************************************************"

Text 50,450,"**************** PRESSIONE SPACE PARA JOGAR ******************"End Function

Perceba que logo depois de configurar do modo gráfico do jogoGraphics 640, 480SetBuffer BackBuffer()

Fizemos a inclusão do arquivo que contem a estrutura e os métodos do dado:Include "Dado.bb"

O arquivo principal sempre terá o loop do jogo, onde tudo acontece:While Not KeyHit(1)

E também conterá as funções de controle do jogo dentro desse loop:Shows()If KeyHit(57) Then Dado_Jogar()Dado_Exibir()

Veja acima que estamos invocado duas funções que foram declaradas no arquivo “Dado.bb” e uma do próprio arquivo principal.Arquivo “Dado.bb”Global Dado_dado = 6Function Dado_Jogar()Dado_dado = Rnd(1,6) End FunctionFunction Dado_Exibir()Color 100, 100, 100Rect 216, 136, 208, 208Color 160, 160, 160Rect 217, 137, 206, 206Color 220, 220, 220Rect 218, 138, 204, 204Color 255, 255, 240Rect 220, 140, 200, 200Color 0,0,200If Dado_dado = 1 Oval 300, 220, 40, 40EndIfIf Dado_dado = 2Oval 240, 160, 40, 40Oval 360, 280, 40, 40EndIfIf Dado_dado = 3Oval 240, 160, 40, 40Oval 300, 220, 40, 40Oval 360, 280, 40, 40 EndIfIf Dado_dado = 4Oval 240, 160, 40, 40Oval 360, 160, 40, 40Oval 240, 280, 40, 40Oval 360, 280, 40, 40 EndIfIf Dado_dado = 5Oval 240, 160, 40, 40Oval 360, 160, 40, 40Oval 300, 220, 40, 40Oval 240, 280, 40, 40

41

Page 42: Modulo 2

Oval 360, 280, 40, 40 EndIfIf Dado_dado = 6Oval 240, 160, 40, 40Oval 240, 220, 40, 40Oval 240, 280, 40, 40Oval 360, 160, 40, 40 Oval 360, 220, 40, 40Oval 360, 280, 40, 40 EndIfColor 255,255,255End Function

Embora esse arquivo seja um pouco maior é até mais simples que o anterior.Global Dado_dado = 6

Veja que declaramos uma variável global para dizer qual é o valor atual do dado, assim o ultimo dado sorteado sempre aparecerá na tela. Embora seja desaconselhável trabalhar com variáveis globais, nesse caso não estamos utilizando essa fora da estrutura desse arquivo, então isso é menos grave, pois não gera dependências externas.Perceba que adotamos a seguinte nomenclatura para as variáveis: “arquivo” + “_” + “nome”. Isso é muito importante por dois motivos: sempre saberemos a localização física de uma variável e não corremos o risco de redefinir ou alterar uma variável em outro arquivo.Function Dado_Jogar()Dado_dado = Rnd(1,6) End Function

Nossa primeira função é responsável por fazer o lançamento do dado. Veja que ela faz isso por meio de um gerador de números aleatórios. O comando Rnd() funciona com a seguinte configuração Rnd(numero inicial, numero final). Assim, determinamos que ele deverá fazer sorteios de números que se enquadrem entre 1 até 6.Perceba que também usamos o mesmo sistema de nomenclatura “arquivo” + “_” + “nome” para identificar as funções. Isso nos dará uma enorme garantia de eficiência para localizar e saber o que cada método faz e para qual objeto ele faz.Function Dado_Exibir()

Essa função tem a responsabilidade de exibir o dado sorteado na tela. Embora seja um pouco grande, ela toda trabalha com apenas 3 conceitos: mudar a cor da paleta de desenho por meio do comando Color red, green, blue , desenhar quadrados por meio

do comando Rect posição x, posição y, largura, altura e desenhar círculos por meio do comando Oval posição x, posição y, largura, altura.Color 100, 100, 100Rect 216, 136, 208, 208Color 160, 160, 160Rect 217, 137, 206, 206Color 220, 220, 220Rect 218, 138, 204, 204Color 255, 255, 240Rect 220, 140, 200, 200

Nesse trecho de código desenhamos o dado, os quadrados. Veja que na verdade fizemos quatro quadrados com cores diferentes, de um cinza mais escuro para uma cor mais clara. Fizemos isso para dar uma sensação de profundidade no objeto por meio do efeito de sombra.If Dado_dado = 1 Oval 300, 220, 40, 40EndIf

Nessa parte do código estamos verificando qual é o valor do dado sorteado e desenhando a quantidade de bolinhas correspondentes nele. Acima vemos o teste para saber se o dado sorteado tem o valor 1. Se isso é verdadeiro, desenha apenas uma bolinha no centro da tela. Veja outra parte do código abaixo.If Dado_dado = 5Oval 240, 160, 40, 40Oval 360, 160, 40, 40Oval 300, 220, 40, 40Oval 240, 280, 40, 40Oval 360, 280, 40, 40EndIf

Nessa parte o teste é para ver se o valor sorteado foi 5 e se assim o for, desenhamos 5 bolinhas no dado.Color 255,255,255End Function

Veja que no final do código colocamos a paleta de desenho em sua cor padrão, isso é, branco, senão as mensagens na tela teriam sua cor alterada.Ultimo Upgrade no jogo F15Como ultima adaptação no nosso jogo, vamos reeditar o arquivo do exemplo 32 e fazer dele um componente de software. Vamos salvá-lo como Scores.bb e vamos incluir o mesmo no nosso arquivo do jogo, que vai se chamar agora F15.bb.

42

Page 43: Modulo 2

Vamos primeiro ás alterações do Arquivo principal, que agora se chama "F15.bb"Arquivo "F15.bb"Graphics 800, 600SetBuffer BackBuffer()Include "Scores.bb"Type TnaveField imgField pxField pyEnd TypeGlobal Nave.Tnave = New TnaveNave\img = LoadImage("midia/f15.bmp")Nave\px = 380Nave\py = 450Type TmeteoroField imgField pxField pyEnd TypeGlobal img1 = LoadImage("midia/pedra1.png")Global img2 = LoadImage("midia/pedra2.png")Global img3 = LoadImage("midia/pedra3.png")Global PONTOS = 0MoveMouse 400,300While Not KeyHit(1)CriarPedras()Controle()Fisica()If Colide() Then GameOver()Logica() Draw() WendEndFunction CriarPedras()sorte = Rnd(0,20)If sorte = 1Meteoro.Tmeteoro = New Tmeteoro Meteoro\px = Rnd(0,700)Meteoro\py = -Rnd(100,700)IMG = Rnd(1,3)If IMG = 1 Then Meteoro\img = img1If IMG = 2 Then Meteoro\img = img2If IMG = 3 Then Meteoro\img = img3EndIfEnd FunctionFunction Controle()Nave\px = Nave\px + MouseXSpeed() End FunctionFunction Logica()Pontos = Pontos + 1End FunctionFunction Fisica()For This.Tmeteoro = Each TmeteoroThis\py = This\py + 3If This\py > 600Delete ThisEnd If

Next End FunctionFunction Draw()DrawImage Nave\img, Nave\px, Nave\pyFor This.Tmeteoro = Each TmeteoroDrawImage This\img, This\px, This\py Next Color 255,0,0Text 10, 10, "PONTOS: " + PONTOSFlipClsEnd FunctionFunction Colide()For This.Tmeteoro = Each TmeteoroIf ImagesCollide(nave\img,nave\px,nave\py,0,This\img,This\px,This\py,0) Then Return 1 Next End FunctionFunction GameOver()ClsText 350, 290, "GAME OVER"Text 310, 310, "Você fez " + PONTOS + " Pontos"FlipDelay(3000)ClsFlipAddScore(PONTOS)ExibeScores()End End Function

Veja que foram poucas as alterações sofridas no arquivo principal, isso porque ajustamos o arquivo incluído para funcionar apenas com duas interfaces. A primeira coisa que obrigatoriamente temos que fazer é incluir o arquivo de scores.Include "Scores.bb"

A partir disso, as alterações só ocorreram na função GameOver(). Primeiro invocamos a interface de adição de escores. Esse método tem a responsabilidade de verificar se a pontuação vai entrar nos escores e se isso for verdadeiro, pegar o nome do jogador e inserir nome e pontos no ranking. Para isso, ele só precisa receber os pontos atuais do jogador.AddScore(PONTOS)

O Segundo método invocado é o de exibir a pontuação.ExibeScores()

Essas foram todas as alterações que tivemos que fazer em nosso arquivo principal do jogo. Simples não é? Vamos agora as alterações no arquivo "Scores.bb".Arquivo "Scores.bb"

43

Page 44: Modulo 2

Global nome1$Global ponto1Global nome2$Global ponto2Global nome3$Global ponto3Global nome4$Global ponto4Global nome5$Global ponto5;******************************* INTERFACE *************************************Function AddScore(pontos)LerScores() If pontos > ponto5Text 200, 250, "Você está entre as melhores pontuações!"Locate 200, 300seunome$ = Input$("Digite seu nome: ")GravarScores(seunome$, pontos)EndIfClsFlipEnd FunctionFunction ExibeScores()LerScores() ClsFlipColor 0,255,0Text 250, 220, "********* MELHORES PONTUAÇÕES *********"Color 255,255,255If ponto1 > 0 Then Text 250,250, nome1$ + " " + ponto1If ponto2 > 0 Then Text 250,280, nome2$ + " " + ponto2If ponto3 > 0 Then Text 250,310, nome3$ + " " + ponto3If ponto4 > 0 Then Text 250,340, nome4$ + " " + ponto4If ponto5 > 0 Then Text 250,370, nome5$ + " " + ponto5Color 0,255,0Text 250, 410, "***************************************"FlipDelay(5000)End Function ;******************************* PRIVADOS *************************************Function LerScores()If FileType("f15.data")aberto = ReadFile ("f15.data")nome1$ = ReadString(aberto)ponto1 = ReadInt(aberto)nome2$ = ReadString(aberto)ponto2 = ReadInt(aberto)nome3$ = ReadString(aberto)ponto3 = ReadInt(aberto)

nome4$ = ReadString(aberto)ponto4 = ReadInt(aberto)nome5$ = ReadString(aberto)ponto5 = ReadInt(aberto)CloseFile(aberto)EndIfEnd FunctionFunction GravarScores(jogador$, pontos);pegando os dados atuais do arquivoIf FileType("f15.data")aberto = ReadFile ("f15.data")play1$ = ReadString(aberto)score1 = ReadInt(aberto)play2$ = ReadString(aberto)score2 = ReadInt(aberto)play3$ = ReadString(aberto)score3 = ReadInt(aberto)play4$ = ReadString(aberto)score4 = ReadInt(aberto)play5$ = ReadString(aberto)score5 = ReadInt(aberto)CloseFile(aberto)EndIf; fasendo seleção de dadosIf pontos > score5score5 = pontosplay5$ = jogador$EndIfIf pontos > score4score5 = score4play5$ = play4$score4 = pontosplay4$ = jogador$EndIfIf pontos > score3score4 = score3play4$ = play3$score3 = pontosplay3$ = jogador$EndIfIf pontos > score2score3 = score2play3$ = play2$score2 = pontosplay2$ = jogador$EndIfIf pontos > score1score2 = score1play2$ = play1$score1 = pontosplay1$ = jogador$EndIf; gravar dodos depois da seleçãoaberto = WriteFile("f15.data")WriteString(aberto, play1$)WriteInt(aberto, score1)WriteString(aberto, play2$)WriteInt(aberto, score2)WriteString(aberto, play3$)WriteInt(aberto, score3)WriteString(aberto, play4$)WriteInt(aberto, score4)

44

Page 45: Modulo 2

WriteString(aberto, play5$)WriteInt(aberto, score5)CloseFile(aberto)End Function

A primeira coisa que tivemos que fazer para criar o nosso componente de escores foi criar uma interface para adicionar escores.Function AddScore(pontos)LerScores()

Veja que essa função recebe apenas a pontuação do jogador atual. A primeira coisa que ela faz é carregar os dados do arquivo de escores por meio da função LerScores(); esses dados serão armazenados nas variáveis globais.If pontos > ponto5Text 200, 250, "Você está entre as melhores pontuações!"Locate 200, 300seunome$ = Input$("Digite seu nome: ")GravarScores(seunome$, pontos)EndIf

Logo em seguida, é feita a verificação se a pontuação do jogador atual é superior a pontuação do ultimo colocado do ranking de escores pontos > ponto5, pois essa é a condição para que ele entre nesse ranking. Caso isso seja verdadeiro, exibe-se uma mensagem de aviso e pede para que ele entre com o seu nome. Para coletar o nome usamos o comando Input$ e o nome fornecido ficará armazenado na variável seunome$. Assim, enviamos nome e pontos para serem gravados GravarScores(seunome$, pontos).A outra alteração no nosso componente de controle de escores foi a criação de uma interface para exibir os escores.Function ExibeScores()LerScores() ClsFlipColor 0,255,0Text 250, 220, "********* MELHORES PONTUAÇÕES *********"Color 255,255,255If ponto1 > 0 Then Text 250,250, nome1$ + " " + ponto1If ponto2 > 0 Then Text 250,280, nome2$ + " " + ponto2If ponto3 > 0 Then Text 250,310, nome3$ + " " + ponto3If ponto4 > 0 Then Text 250,340, nome4$ + " " + ponto4

If ponto5 > 0 Then Text 250,370, nome5$ + " " + ponto5Color 0,255,0Text 250, 410, "***************************************"FlipDelay(5000)

Veja que ela é bem simples, apenas usa a função LerScores() para carregar os dados atuais de escores para as variáveis globais e em seguida exibe esses dados na tela. Ela também usa o metodo Delay() para fazer com que o programa de uma pausa de 5 segundos para dar tempo de lermos os escores.As funções LerScores() e GravarScores() não sofreram alterações por isso não iremos comentar. Caso tenha alguma dúvida, consulte a explicação do exemplo 32.Pronto, agora o seu jogo está completo! E além disso você aprendeu a fazer na prática reuso de software, criação de componente e inclusão de arquivo. Além de se tornar um desenvolvedor de jogos, você vai acabar virando um grande arquiteto de software.

ALELUIA!!!!

Finalmente chegamos ao fim da teoria de programação. Você já deve estar cansado de ficar exibindo mensagens na tela. Infelizmente essa maneira é a mais fácil e rápida de ensinar o básico da programação. Se para cada comando novo tivesse que criar um jogo... A partir do próximo bloco trataremos apenas sobre jogos, fundamentos e práticas.Se você chegou até aqui cheio de dúvidas, não se preocupe isso é sinal de que você é humano e terá muitas maneiras de solucionar esses problemas. Se você conseguiu compreender tudo, parabéns você é um gênio e provavelmente será um dos próximos integrantes da EA GAMES ou então fundador da próxima empresa que irá revolucionar o mundo dos jogos.

45

Page 46: Modulo 2

Anotações__________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________

46