Você está na página 1de 34

9. HELL: PASSO A PASSO.

Em nossa ltima parte do curso vamos explicar passo a passo o funcionamento de um jogo completo no estilo FPS (First Person Shorter), isso , tiro em primeira pessoa. Esse jogo ser completo, isso , conter todos os elementos que normalmente existem em um jogo. Lembramos que os arquivos fonte e o executvel do jogo se encontram da pasta de arquivos de nosso ltimo mdulo. Agradecimentos especiais ao Zizaco por ter criado o cenrio e as mdias da interface grfica do jogo e a UniDev por disponibilizar algumas horas de trabalho desse incrvel artista, bem como por ter cedido os modelos 3d para o mesmo. O objetivo desse projeto final no ensinar como um jogo deve ser feito, afinal obvio que a construo de um jogo completo, explicado passo a passo, gastaria mais de mil pginas de tutoriais. Os conceitos abordados at esse momento j do base para a construo da maioria dos jogos comerciais existentes, mas ainda falta compreender como um jogo 3d deve ser estruturado. Assim, o objetivo desse projeto : 1. Aprender como integrar os conceitos adquiridos no curso. 2. Compreender a estrutura de um jogo 3d. 3. Adquirir noes de projeto de jogos. 4. Ver na prtica como modularizar um projeto (dividir para conquistar). 5. Aprender tcnicas de implementao de jogos.

munio e chaves. Tambm aprenderemos a controlar portas trancadas com chaves. Existem trs tipos de inimigos: zumbis, fantasmas e mutantes. Veja abaixo uma screen shot do jogo.

Desempenho da Aplicao O Blitz3D uma ferramenta to poderosa que um projeto desse porte no necessitaria de nenhum tipo de tcnica especial para rodar em qualquer computador de baixa performance. Mas como sabemos das dificuldades financeiras do nosso sofrido povo brasileiro, resolvemos portar esse jogo para computadores bem antigos. Para melhorar o desempenho geral da aplicao usamos a tcnica Hide/Show, que consiste em s exibir entidades 3d complexas a partir de uma certa distncia da cmera. Essa tcnica se fundamenta no conceito de que no precisamos gastar esforo de processamento com entidades que no esto interagindo no jogo. Assim, os personagens s sero exibidos a partir de uma certa distncia do personagem principal. Muitos programadores gostam de usar a tcnica de range de cmera aliada a fog. Essa tcnica consiste em limitar a renderizao de objetos a partir de uma certa distncia da cmera e colocar uma simulao de neblina para encobrir o vazio que ficar devido a nada existir depois dessa distncia do range de cmera. A nica vantagem dessa tcnica que ela fcil, pois automtica. Assim muito til para quem no gosta de programar. Eu,

Nesse nosso projeto de integrao tambm iremos abordar na prtica as funcionalidades mais corriqueiras dos jogos de mercado: itens, armas, tiros, inimigos, portas trancadas, controle de munio, controle de vitalidade. Para no deixar o projeto muito grande e tornar extremamente tediosa a tarefa de acompanhar a explicao passo a passo do mesmo, iremos limitar o tamanho do jogo e da quantidade de elementos presentes. Teremos apenas uma arma, pois quem sabe configurar uma, sabe fazer o mesmo com vrias. Iremos interagir com apenas 2 itens:

particularmente, no gosto dela, pois nos priva da viso da paisagem do cenrio. S conseguimos visualizar uma pequena parte da paisagem, e a partir disso s vemos neblina. Por meio da tcnica manual de Hide/Show ns podemos controlar cada entidade individualmente ou por colees de famlias de objetos. Assim, podemos, por exemplo, configurar o cenrio para sempre ser completamente exibido; personagens para serem mostrados sempre a partir de 500 metros e plantas a partir de 100 metros; e assim em diante. Tambm podemos dar a possibilidade de o jogador escolher nas configuraes grficas do jogo a partir de que distncia as entidades sero processadas, assim ele poder ajustar esses detalhes conforme a potncia de seu computador.

Da maneira que estruturamos esse projeto ele consegui um respeitvel desempenho, confira abaixo: Computador testado: Processador: mhz Sistema Operacional: Memria Ran: Placa de Vdeo: Resoluo 640 x 480 1024 x 768 Pentium II 400

Windows 98 96 mb TNT2 Riva 32mb Frame Rate 35 fps 23 fps

A estrutura do jogo

Explicando o cdigo A partir de agora vamos abrir todo o cdigo fonte do jogo explicando todas as suas funcionalidade. Lembramos que nesse cdigo no existe nada de novo, pois tudo que existe aqui j foi discutido durante o curso. Estamos aqui apenas juntando todas as peas do quebra cabeas para criar uma grande imagem final. ARQUIVO GAME.BB
Include Include Include Include Include Include Include Include Include Include Include "Info.bb" "Menu.bb" "Jogar.bb" "Cenario.bb" "Porta.bb" "Item.bb" "Audio.bb" "Heroy.bb" "zumbi.bb" "mutante.bb" "fantasma.bb"

Veja que o loop principal da aplicao roda enquanto o valor dessa varivel for igual a zero.
While Not(SAIR)

O cdigo do nosso loop principal bem simples, veja abaixo.


ACAO = MENU() If ACAO = 1 Then JOGAR() If ACAO = 0 Then SAIR = 1 Temos uma varivel ACAO

que recebe do menu o seu valor. Assim, se no menu apertarmos o boto jogar, a varivel recebe o valor 1 e executa o jogo. Se receber do menu o valor 0, por apertar o boto sair, ento liga a varivel SAIR e finaliza o programa. Por fim, antes de encerrar a aplicao, apresentamos os crditos do jogo, que tambm s aparecem uma vez, ao sair da aplicao.
Info_Creditos()

Info_Entrada() SAIR = 0 ACAO = 0 While Not(SAIR) ACAO = MENU() If ACAO = 1 Then JOGAR() If ACAO = 0 Then SAIR = 1 Wend Info_Creditos() End

ARQUIVO INFO.BB
Function Info_Entrada() Graphics 800,600 SetBuffer BackBuffer() entrada1 LoadImage("midia/util/udco.jpg") DrawImage entrada1,140,190 Flip Info_Espere(3000) Cls entrada2 LoadImage("midia/util/Hell.jpg") DrawImage entrada2,200,190 Flip Info_Espere(3000) End Function =

Explicando Esse o nosso arquivo principal. a partir dele que todo o projeto executado, por isso fizemos a partir dele todas as incluses de arquivos auxiliares por meio do comando Include. Como voc j percebeu, a apresentao do jogo e da produtora aparece apenas uma vez no jogo, antes do menu do jogo. Fizemos isso nessa linha abaixo, invocando um servio do arquivo Info.bb que o nosso arquivo responsvel por apresentao e crditos.
Info_Entrada()

Function Info_Espere(tempo) t1 = MilliSecs() While (t1 + tempo > MilliSecs()) Wend End Function

Os jogos normalmente iniciam a partir de um menu e esse menu normalmente um loop de controle. Em nossa arquitetura optamos por ter um loop de controle em nosso arquivo principal e outro no menu. Declaramos a varivel abaixo como controle do loop do nosso cdigo principal, ela a varivel de escape.
SAIR = 0

Function Info_Creditos() Graphics 800,600 SetBuffer BackBuffer() wrm LoadImage("midia/util/credito.jpg") DrawImage wrm, 150, 280 Flip Info_Espere(3000) =

Cls End Function

Por fim, nossa ultima funo apresenta os crditos do jogo.


wrm LoadImage("midia/util/credito.jpg") DrawImage wrm, 150, 280 Flip Info_Espere(3000) =

Explicando Esse arquivo responsvel por apresentaes e crditos do jogo. Veja que a primeira funo dele Info_Entrada() faz as apresentaes do projeto.
entrada1 LoadImage("midia/util/udco.jpg") DrawImage entrada1,140,190 Flip Info_Espere(3000) =

ARQUIVO MENU.BB
Function Menu() Graphics 800, 600 SetBuffer BackBuffer() btnJ LoadImage("midia/util/btn_jogar.jpg") btnS LoadImage("midia/util/btn_sair.jpg") btnR1 LoadImage("midia/util/btn_res1.jpg") btnR2 LoadImage("midia/util/btn_res2.jpg") btnR3 LoadImage("midia/util/btn_res3.jpg") btnR4 LoadImage("midia/util/btn_res4.jpg") mouse LoadImage("midia/util/mouse.png") opt = -1 While (opt = -1) Color 0,0,0 mx = MouseX() my = MouseY() mk = MouseHit(1) DrawImage btnJ, 325, 220 If JOGO\Tela_X btnR1, 325, 280 If JOGO\Tela_X btnR2, 325, 280 If JOGO\Tela_X btnR3, 325, 280 If JOGO\Tela_X btnR4, 325, 280 = = = = 640 800 1024 1280 Then Then Then Then DrawImage DrawImage DrawImage DrawImage = = = = = = =

Aqui ele carrega uma imagem de apresentao e a exibe. Veja que usamos o mtodo Info_Espere(), que desse componente mesmo, para fazer com que seja feita uma pausa de 3 segundos para dar tempo de lermos a apresentao.
entrada2 LoadImage("mdia/util/Hell.jpg") DrawImage entrada2,200,190 Flip Info_Espere(3000) =

Aqui exibimos a segunda imagem da apresentao, o nome do jogo. A nossa segunda funo o mtodo criado para fazer as pausas de apresentao. Veja abaixo a estrutura dela.
Function Info_Espere(tempo) t1 = MilliSecs() While (t1 + tempo > MilliSecs()) Wend End Function

Ela recebe um parmetro de quem o invoca: um inteiro que ser o tempo em milsimos de segundo que dever esperar. Assim que se inicia a sua execuo, a primeira coisa que faz pegar o tempo inicial.
t1 = MilliSecs()

a partir desse tempo inicial do sistema que vamos esperar at que atinja o tempo solicitado.
While (t1 + tempo > MilliSecs())

DrawImage btnS, 325, 340

Veja que, para isso, ficamos testando o tempo do sistema a todo momento, at que atinja o valor esperado, isso , o tempo atual, menos o tempo inicial deve ser maior que o tempo solicitado. Nesse caso, fizemos a seguinte equao de verificao Enquanto o tempo inicial, mais o tempo de espera for maior que o tempo atual....

If mx > 325 And mx < 475 If my > 220 And my < 269 Rect 325,220,150,49,0 If mk Then opt = 1 EndIf EndIf

If mx > 325 And mx < 475 If my > 280 And my < 329 Rect 325,280,150,49,0

If mk Select JOGO\Tela_X Case 640 JOGO\Tela_X JOGO\Tela_Y Case 800 JOGO\Tela_X JOGO\Tela_Y Case 1024 JOGO\Tela_X JOGO\Tela_Y Case 1280 JOGO\Tela_X JOGO\Tela_Y End Select EndIf EndIf EndIf

= 800 = 600 = 1024 = 768 = 1280 = 1024 = 640 = 480

btnS LoadImage("midia/util/btn_sair.jpg") btnR1 LoadImage("midia/util/btn_res1.jpg") btnR2 LoadImage("midia/util/btn_res2.jpg") btnR3 LoadImage("midia/util/btn_res3.jpg") btnR4 LoadImage("midia/util/btn_res4.jpg") mouse LoadImage("midia/util/mouse.png")

= = = = = =

If mx > 325 And mx < 475 If my > 340 And my < 389 Rect 325,340,150,49,0 If mk Then opt = 0 EndIf EndIf DrawImage mouse, mx, my Flip Cls Color 255,255,255 Wend Return opt End Function

Criamos uma varivel para gerenciar os eventos do menu. Ela ser iniciada com o valor de (-1) e enquanto tiver esse valor o loop de menu ser executado. Essa varivel ser alterada se apertarmos os botes SAIR ou JOGAR do menu, assim sairemos do loop do menu e voltaremos ao loop da arquivo GAME.BB, que o loop da aplicao.
opt = -1 While (opt = -1)

A linha seguinte apenas configura a cor da paleta do sistema para a cor preta, pois usaremos essa cor para gerar um efeito especial nos botes quando o mouse estiver sobre eles.
Color 0,0,0

Devemos pegar os estados do mouse, sua posio e se o boto foi pressionado, para podermos saber se o cursor do mouse est sobre algum boto ou se o boto foi pressionado.
mx = MouseX() my = MouseY() mk = MouseHit(1)

Explicando Esse o arquivo do menu da aplicao. Veja que ele possui apenas uma funo e ela contm um loop para gerenciar os eventos do menu. Isso uma regra geral, se temos que gerenciar eventos, devemos criar um loop para captar e tratar esses eventos. No inicio dessa funo configuramos o modo grfico, afinal, o menu pode ter sido invocado depois do fim do jogo. O jogo 3d e o menu 2d, por isso devemos sempre redefinir as configuraes do modo grfico.
Graphics 800, 600 SetBuffer BackBuffer()

Nessas linhas seguintes exibimos os botes. Como decidimos exibir a resoluo atual da tela diretamente no boto, tivemos que carregar um boto para cada resoluo e exibir o boto de acordo com a resoluo atual. Lembramos que a resoluo da tela fica guardada nas variveis tela do objeto Jogo.
DrawImage btnJ, 325, 220 If JOGO\Tela_X btnR1, 325, 280 If JOGO\Tela_X btnR2, 325, 280 If JOGO\Tela_X btnR3, 325, 280 If JOGO\Tela_X btnR4, 325, 280 = = = = 640 800 1024 1280 Then Then Then Then DrawImage DrawImage DrawImage DrawImage

A segunda coisa que fizemos foi carregar as imagens que sero usadas no menu. Veja que tambm carregamos uma imagem para o ponteiro do mouse.
btnJ LoadImage("midia/util/btn_jogar.jpg") =

DrawImage btnS, 325, 340

As linhas abaixo fazem duas verificaes e provocam dois eventos.


If mx > 325 And mx < 475 If my > 220 And my < 269 Rect 325,220,150,49,0 If mk Then opt = 1 EndIf EndIf

JOGO\Tela_Y = 480 End Select EndIf

A primeira verificao se o cursor do mouse est sobre o boto JOGAR do menu. Para isso ele testa as variveis mx e my, que contm a posio atual do mouse, para ver se esto na rea onde o boto exibido. Como voc pode ver acima, o boto foi exibido na posio 325, 220. Como esse boto possui um tamanho de 150 pixels de largura, primeiro verificamos se a posio x do mouse (mx) est dentro dessa rea x, isso , desde 325 at 475. Se passar nesse teste, vamos verificar se est dentro da coordenada y (my); como o boto tem 49 pixels de altura, a verificao se o mouse est na rea entre 220 e 269. Se os dois testes de posicionamento foram positivos, ento o cursor do mouse est sobre o boto. Ento vamos desenhar um retngulo em volta dele com o comando Rect. Isso causa um efeito de foco. Se o cursor estiver dentro da rea do boto tambm verificamos se o boto foi pressionado. A varivel mk possui essa informao, por isso fizemos a verificao If mk Then opt = 1. Aqui, se o boto foi pressionado configuramos opt como igual a 1. Assim, se pressionamos o boto jogar, ento sairemos do loop do menu e o menu retornar esse valor para que o loop de GAME.BB possa iniciar o jogo. O prximo controle de boto bem mais complexo. Vamos pular a parte de verificao de posio, pois isso ns j sabemos como fazer. A complexidade est na parte de configurar o evento caso o boto seja pressionado.
If mk Select JOGO\Tela_X Case 640 JOGO\Tela_X JOGO\Tela_Y Case 800 JOGO\Tela_X JOGO\Tela_Y Case 1024 JOGO\Tela_X JOGO\Tela_Y Case 1280 JOGO\Tela_X

Veja que estamos usando o comando condicional Select Case, pois iremos fazer uma comparao complexa e esse comando o mais recomendado. Lembramos que esse bloco de cdigo s executado se o boto do mouse foi pressionado.
If mk

Embora seja um pouco grande, o que essa estrutura faz simplesmente verificar qual a resoluo atual do monitor.
Case 640

E configurar a resoluo para a prxima disponvel.


JOGO\Tela_X = 800 JOGO\Tela_Y = 600

Se tiver dvidas de como o comando Select Case funciona, verifique os primeiros captulos do curso, pois l demos uma explicao completa. Abaixo o teste do ltimo boto.
If mx > 325 And mx < 475 If my > 340 And my < 389 Rect 325,340,150,49,0 If mk Then opt = 0 EndIf EndIf

Esse boto o boto SAIR, se ele foi pressionado, o valor de opt ser 0. Assim o loop de GAME ir finalizar a aplicao. Abaixo temos algumas linhas do final da aplicao.
DrawImage mouse, mx, my Flip Cls Color 255,255,255

Veja que o cursor do mouse a ultima coisa a ser exibida, afinal ele deve ficar acima de todas as coisas. O Cls deve sempre estar depois de Flip, pois devemos limpar a tela as ultimas imagens exibidas. Por fim, o comando abaixo o responsvel por dar o retorno das escolhas do menu, retornando o valor atual de opt.
Return opt

= 800 = 600 = 1024 = 768 = 1280 = 1024 = 640

ARQUIVO JOGAR.BB
Type Tjogo Field Luz Field COLI_HER Field COLI_CEN Field COLI_INI Field COLI_ITEN Field SAIR Field Tela_X Field Tela_Y

Field Show Field mira Field Numeros End Type

JOGO\COLI_ITEN = 4 JOGO\SAIR = 0 Jogo\Show = LoadImage("midia/util/show.png") MaskImage jogo\show, 255, 255, 255 Jogo\mira = LoadImage("midia/util/mira.bmp") Jogo\Numeros = LoadAnimImage("mdia/util/numeros.bmp", 10,10,0,10) Cenario_Carregar() Heroy_Carregar() Porta_Carregar() Item_Carregar() Audio_Carregar() zumbi_Carregar() mutante_Carregar() fantasma_Carregar() Collisions JOGO\COLI_HER, JOGO\COLI_CEN, 2, 3 Collisions JOGO\COLI_INI, JOGO\COLI_CEN, 2, 3 Collisions JOGO\COLI_HER, JOGO\COLI_INI, 2, 3 Collisions JOGO\COLI_HER, JOGO\COLI_ITEN, 1, 3 Collisions JOGO\COLI_INI, JOGO\COLI_INI, 3, 3 ;==================== GAME LOOP ==================== While Not (JOGO\SAIR) t1 = MilliSecs() Jogar_Controle() Jogar_Logica() UpdateWorld RenderWorld Jogar_Show()

Global JOGO.Tjogo = New Tjogo JOGO\Tela_X = 800 JOGO\TELA_Y = 600 Function Jogar() ;INTRODUO Graphics 800, 600 SetBuffer BackBuffer() Cls Flip fato = LoadImage("midia/util/fato.jpg") miss = LoadImage("midia/util/missao.jpg") help = LoadImage("midia/util/help.jpg") out = 0 t1 = MilliSecs() While Not(out) t2 = MilliSecs() TEMPO = t2 - t1 If TEMPO < 15000 DrawImage fato, 100,100 Else If TEMPO < 30000 DrawImage miss, 100,100 Else DrawImage help, 150,100 EndIf EndIf Flip Cls If TEMPO > 40000 Then out = 1 If GetKey() Then out = 1 If GetMouse() Then out = 1 Wend Cls Flip FreeImage fato FreeImage miss FreeImage help ;-----------------------------O JOGO----------------------------Graphics3D JOGO\Tela_X , JOGO\TELA_Y, 32, 1 SetBuffer BackBuffer() Jogo\Luz = CreateLight() RotateEntity Jogo\Luz , 90,0,0 JOGO\COLI_HER = 1 JOGO\COLI_CEN = 2 JOGO\COLI_INI = 3

Flip 0 ;FPS 30 While (MilliSecs() - t1) < 33 Wend Wend ;======================================= ============ Cenario_DesCarregar() Heroy_DesCarregar() Item_Descarregar() Porta_Descarregar() Audio_Descarregar() Zumbi_Descarregar() Mutante_Descarregar() Fantasma_Descarregar() Jogar_GameOver() End Function

Function Jogar_Controle() Heroy_Controle() Cenario_Controle() If KeyHit(1) Then JOGO\SAIR = 1

MoveMouse 300,300 End Function

life = heroy_getvida() Color 255,0,0 Rect 56,7, life, 7 tiros = Heroy_gettiro() tiro$ = Str$(tiros) tam = Len(tiro$) For x=1 To tam n$ = Mid$(tiro$, x, 1) v = n$ px = 56 + ((x * 10) - 10) DrawImage JOGO\numeros, px, 27, v Next DrawImage Jogo\mira, (Jogo\tela_x/2) - 14, (Jogo\tela_y/2) 14 End Function

Function Jogar_Logica() Zumbi_IA() Zumbi_Logica() Mutante_IA() Mutante_Logica() Fantasma_IA() Fantasma_Logica() Porta_Logica() Item_Logica() ;SE MATOU TODOS, GANHA If (Not Zumbi_Get()) If (Not Mutante_Get()) If (Not Fantasma_Get()) Jogar_Venceu() EndIf EndIf EndIf ;SE PERDER A VIDA, MORRE If Heroy_GetVida() < Jogar_Morreu() End Function 1 Then

Function Jogar_Morreu() morreu = LoadImage("midia/util/morreu.png") DrawImage morreu, JOGO\Tela_X/2 150, JOGO\Tela_y/2 - 25 Flip Info_Espere(2000) JOGO\SAIR = 1 End Function Function Jogar_Venceu() venceu = LoadImage("midia/util/venceu.png") DrawImage venceu, JOGO\Tela_X/2 150, JOGO\Tela_y/2 - 25 Flip Info_Espere(2000) JOGO\SAIR = 1 End Function Function Jogar_GameOver() Cls fim LoadImage("midia/util/gameover.png") DrawImage fim, JOGO\Tela_X/2 150, JOGO\Tela_y/2 - 25 Flip Info_Espere(2000) End Function Function Jogar_Show() DrawImage jogo\show,0,0

Explicando Esse o arquivo de controle do jogo, onde existe o loop do jogo. Ele controla todos os eventos e objetos da aplicao. Iniciamos o arquivo declarando o Type. Nele temos todos os dados gerais relevantes do jogo.
Type Tjogo Field Luz Field COLI_HER Field COLI_CEN Field COLI_INI Field COLI_ITEN Field SAIR Field Tela_X Field Tela_Y Field Show Field mira Field Numeros End Type

= -

O campo Luz possuir a referncia para a luz criada para o ambiente. Os campos com incio COLI so responsveis por definir os tipos de objetos de coliso; veja que temos objetos para o heri, cenrio, inimigos e itens. O campo Sair responsvel pelo loop do jogo. Os campos Tela_X e Tela_Y so os que controlam a resoluo do jogo e so afetados pelo boto de resoluo do menu. O campo Show contm a imagem base para indicar o nvel de vida e quantidade de munio. O campo mira guarda a imagem da mira da arma. Por fim, o campo Numeros contm o arquivo animado de imagens dos nmeros que so usados para indicar a quantidade de tiros.

Global JOGO.Tjogo = New Tjogo JOGO\Tela_X = 800 JOGO\TELA_Y = 600

Veja que estamos instanciando o objeto de jogo como global antes da funo, isso significa que ele iniciado desde o primeiro instante que o programa comea a rodar. por isso que os campos que definem o tamanho da tela podem ser acessados a partir do menu. A nossa funo dever exibir algumas informaes como a introduo do jogo, os objetivos da misso e como jogar. Por isso iniciamos em modo grfico 2d e carregamos imagens.
Function Jogar() ;INTRODUO Graphics 800, 600 SetBuffer BackBuffer() Cls Flip fato = LoadImage("midia/util/fato.jpg") miss = LoadImage("midia/util/missao.jpg") help = LoadImage("midia/util/help.jpg")

Como podemos ver, existem trs escapes do loop: por tempo, se alguma tecla for pressionada e se algum boto do mouse for pressionado.
If TEMPO > 40000 Then out = 1 If GetKey() Then out = 1 If GetMouse() Then out = 1

Quando sair desse loop, o programa vai limpar a tela e liberar as imagens da memria.
Cls Flip FreeImage fato FreeImage miss FreeImage help

A partir desse momento vamos nos preparar para iniciar o jogo. Vamos definir o modo grfico como 3d e vamos criar a iluminao.
;-----------------------------O JOGO----------------------------Graphics3D JOGO\Tela_X , JOGO\TELA_Y SetBuffer BackBuffer() Jogo\Luz = CreateLight() RotateEntity Jogo\Luz , 90,0,0

Tambm temos que inicializar todas as variveis do jogo.


JOGO\COLI_HER = 1 JOGO\COLI_CEN = 2 JOGO\COLI_INI = 3 JOGO\COLI_ITEN = 4 JOGO\SAIR = 0 Jogo\Show = LoadImage("midia/util/show.png") MaskImage jogo\show, 255, 255, 255 Jogo\mira = LoadImage("midia/util/mira.bmp") Jogo\Numeros = LoadAnimImage("mdia/util/numeros.bmp", 10,10,0,10)

As imagens possuem um tempo de exibio, vencido esse tempo, muda para a prxima. Como tambm damos a opo de sair dessa introduo via teclado ou mouse, tivemos que criar um loop para gerenciar esses eventos. Esse loop controlado pela varivel out e por tempo.
out = 0 t1 = MilliSecs() While Not(out) t2 = MilliSecs() TEMPO = t2 - t1

Veja que o tempo decorrido dentro do loop colocado dentro da varivel TEMPO. Como pode ser visto no cdigo abaixo, cada imagem tem um tempo de exibio. A imagem fato exibida desde o incio at o 14 segundo. A imagem miss tambm exibida por 15 segundos, desde o 15 at o 29. Por fim a imagem help ser exibida durante o resto do tempo que a aplicao permanecer em loop.
If TEMPO < 15000 DrawImage fato, 100,100 Else If TEMPO < 30000 DrawImage miss, 100,100 Else DrawImage help, 150,100 EndIf EndIf

Tambm vamos carregar todos os objetos e mdias do jogo, isso , vamos inicializar todos os elementos do jogo por meio da funo Carregar de cada um dos objetos.
Cenario_Carregar() Heroy_Carregar() Porta_Carregar() Item_Carregar() Audio_Carregar() zumbi_Carregar() mutante_Carregar() fantasma_Carregar()

A ltima coisa que fazemos ante de iniciar o jogo ligar todas as colises. Estamos acionando colises heri x cenrio para que o nosso heri no atravesse as paredes e nem atravesse o cho com o efeito da gravidade. Pelo mesmo motivo acionamos colises inimigo x cenrio. As colises de heri x inimigos se devem para impedir que seus corpos atravessem uns aos outros. As

colises do heri x itens so necessrias para pegarmos itens por toque. Inimigos x inimigos so para evitar que seus corpos se sobreponham ou atravessem uns aos outros.
Collisions JOGO\COLI_HER, 2, 3 Collisions JOGO\COLI_INI, 2, 3 Collisions JOGO\COLI_HER, 2, 3 Collisions JOGO\COLI_ITEN, 1, 3 Collisions JOGO\COLI_INI, 3, 3 JOGO\COLI_CEN, JOGO\COLI_CEN, JOGO\COLI_INI, JOGO\COLI_HER, JOGO\COLI_INI,

de atualizao de cerca de 30 quadros por segundo.


;FPS 30 While (MilliSecs() - t1) < 33 Wend

A funo se encerra com invocaes das funes de descarregar objetos e a funo game over. Dessa forma, descarregamos todos os contedos do jogo ante de irmos para a tela de menu. Esse trecho s ser executado se o jogo for encerado.
Cenario_DesCarregar() Heroy_DesCarregar() Item_Descarregar() Porta_Descarregar() Audio_Descarregar() Zumbi_Descarregar() Mutante_Descarregar() Fantasma_Descarregar() Jogar_GameOver() End Function

Agora chegamos ao loop do jogo. Veja que iniciamos cada iterao pegando o tempo inicial da iterao. Isso se d porque vamos implementar um sistema de limitao de velocidade de atualizao da tela. Afinal, queremos que jogo possua a mesma velocidade em todos os computadores.
;==================== ==================== While Not (JOGO\SAIR) t1 = MilliSecs() GAME LOOP

A seguir invocamos as funes de controle do jogo.


Jogar_Controle() Jogar_Logica()

Depois disso atualizamos as entidades 3d e renderizamos o cenrio.


UpdateWorld RenderWorld

Vamos agora estudar os cdigos das funes auxiliares do arquivo jogar. Nossa primeira funo a de controle. Veja que nela invocamos as funes de controle do heri e do cenrio. Tambm verificamos se a tecla de escape pressionada para sairmos do jogo. E por fim, alinhamos o mouse em um ponto central da tela, pois estamos controlando o personagem com o mouse, por isso ele sempre deve estar dentro da rea do monitor, seno os comandos dele no respondero.
Function Jogar_Controle() Heroy_Controle() Cenario_Controle() If KeyHit(1) Then JOGO\SAIR = 1 MoveMouse 300,300 End Function

A partir disso podemos exibir os objetos 2d. No caso, a barra de status do jogador.
Jogar_Show()

Agora podemos enviar a imagem do buffer de desenho para a tela com o comando Flip. Veja que passamos o parmetro 0, pois ns mesmos vamos implementar o nosso sistema de velocidade de atualizao da tela.
Flip 0

Esse o nosso sistema de controle de velocidade de atualizao da tela, o controle de frame rate. Como voc se lembra, no incio da iterao do loop sempre pegamos o tempo do sistema e guardamos na varivel t1. Nesse momento j foi realizado todo o processamento pesado que atualizao do cenrio e renderizao do mesmo. A partir disso verificamos qual o tempo atual do sistema e obrigamos que o programa espere at que a diferena entre o tempo inicial e o tempo atual chegue a 33 milsimos de segundos, para depois iniciar outro ciclo do loop. Dessa forma teremos uma velocidade

Nossa segunda funo a ser estudada a de lgica. Essa funo faz referncias a funes de lgica e ia de cinco outras entidades do jogo. Ela tambm verifica se todos os inimigos foram mortos; se isso verdadeiro, executa a funo responsvel pela vitria do jogador Jogar_Venceu(). Outra responsabilidade verificar se o jogador possui vida, caso seja falso executa a funo de perder o jogo Jogar_Morreu().
Function Jogar_Logica() Zumbi_IA() Zumbi_Logica() Mutante_IA() Mutante_Logica() Fantasma_IA() Fantasma_Logica() Porta_Logica() Item_Logica() ;SE MATOU TODOS, GANHA

If (Not Zumbi_Get()) If (Not Mutante_Get()) If (Not Fantasma_Get()) Jogar_Venceu() EndIf EndIf EndIf ;SE PERDER A VIDA, MORRE If Heroy_GetVida() < Jogar_Morreu() End Function 1 Then

DrawImage jogo\show,0,0 life = heroy_getvida() Color 255,0,0 Rect 56,7, life, 7 tiros = Heroy_gettiro() tiro$ = Str$(tiros) tam = Len(tiro$) For x=1 To tam n$ = Mid$(tiro$, x, 1) v = n$ px = 56 + ((x * 10) - 10) DrawImage JOGO\numeros, px, 27, v Next End Function

Abaixo a funo executada quando perdemos o jogo, isso , quando morremos. Ela apenas carrega uma imagem e a exibe como aviso por 2 segundos. Ela tambm responsvel por configura a chave do loop para que o jogo pare de executar. Veja que o local de exibio da imagem depende do tamanho da tela. Fizemos isso para que o aviso sempre aparece no centro da tela, independente da resoluo na qual o jogo esteja rodando.
Function Jogar_Morreu() morreu LoadImage("midia/util/morreu.png") DrawImage morreu, JOGO\Tela_X/2 JOGO\Tela_y/2 - 25 Flip Info_Espere(2000) JOGO\SAIR = 1 End Function = 150,

Veja que ela inicia com a exibio da imagem de fundo desses dados.
DrawImage jogo\show,0,0

Logo a segui usamos uma interface para descobrir a vida atual do heri.
life = heroy_getvida()

E exibimos um retngulo com tamanho exato da vida. Como a vida vai de 0 a 100 esse tambm ser o tamanho do retngulo que a representa, pois a varivel life agora possui a quantidade de vida dele.
Color 255,0,0 Rect 56,7, life, 7

Agora nossa funo de vitria. Ela faz as mesmas coisas da anterior, s muda o contedo da mensagem.
Function Jogar_Venceu() venceu LoadImage("midia/util/venceu.png") DrawImage venceu, JOGO\Tela_X/2 JOGO\Tela_y/2 - 25 Flip Info_Espere(2000) JOGO\SAIR = 1 End Function = 150,

O cdigo a seguir responsvel por exibir a quantidade de balas que o heri possui. Veja que estamos usando uma imagem animada para fazer isso.
tiros = Heroy_gettiro() tiro$ = Str$(tiros) tam = Len(tiro$) For x=1 To tam n$ = Mid$(tiro$, x, 1) v = n$ px = 56 + ((x * 10) - 10) DrawImage JOGO\numeros, px, 27, v Next

A primeira coisa que fizemos foi descobrir quantos tiros o heri tem.
tiros = Heroy_gettiro()

Como sabemos, a funo GameOver a ltima a ser executada no jogo pois est na ultima linha da funo antes dela retornar. Ela semelhante s anteriores, carrega uma imagem e a exibe por um breve perodo de tempo.
Function Jogar_GameOver() Cls fim = LoadImage("midia/util/gameover.png") DrawImage fim, JOGO\Tela_X/2 - 150, JOGO\Tela_y/2 - 25 Flip Info_Espere(2000) End Function

Vamos ter que usar esse dado como texto, por isso vamos usar a funo Str$() do Blitz3d para converter nmeros para texto.
tiro$ = Str$(tiros)

Agora temos que saber quantos dgitos tem esse nmero. Para isso basta achar o tamanho da string gerada com a funo Len().
tam = Len(tiro$)

Agora deveremos exibir um dgito de cada vez. Para isso iremos construir um lao ForNext com o numero de iteraes igual quantidade de dgitos.
For x=1 To tam

Para finalizar, a funo que exibe os estados atuais do heri.


Function Jogar_Show()

Agora iremos pegar dgito por dgito da string para desenh-lo na tela como imagem. O dgito colocado na varivel n$.
n$ = Mid$(tiro$, x, 1)

chave\modelo = LoadMesh("midia/util/chave.3ds") chave\id = 1 PositionEntity chave\modelo, -112, 4, 110 ScaleEntity chave\modelo, .05, .05, .05 RotateEntity chave\modelo, 0, 90, 0 EntityType chave\modelo, JOGO\COLI_ITEN chave.Titem = New Titem chave\modelo = LoadMesh("midia/util/chave.3ds") chave\id = 2 PositionEntity chave\modelo, 60, 3, -82 ScaleEntity chave\modelo, .05, .05, .05 TurnEntity chave\modelo, 0, -90, 0 EntityType chave\modelo, JOGO\COLI_ITEN End Function Function Item_Logica() HERO = Heroy_get3d() COLY = EntityCollided(hero, JOGO\COLI_ITEN) For this.Titem = Each Titem If COLY = this\modelo ; se munio If this\id = 0 Audio_PlayItem() Heroy_SetTiro(this\qtd) FreeEntity this\modelo Delete this Else ;se a chave 1 If this\id = 1 Audio_PlayItem() Heroy_SetChave1() FreeEntity this\modelo Delete this Else ;se a chave 2 Audio_PlayItem() Heroy_SetChave2() FreeEntity this\modelo Delete this

Devemos saber o valor numrico desse dgito, por isso criamos uma nova varivel do tipo int que receber o mesmo e o converter de letra para nmero.
v = n$

Para que os dgitos no sejam sobrepostos fizemos uma funo para calcular automaticamente onde eles devem aparecer.
px = 56 + ((x * 10) - 10)

Agora s exibir. Veja que o frame da imagem igual ao valor do nmero.


DrawImage JOGO\numeros, px, 27, v

ARQUIVO ITEM.BB
Type Titem Field modelo Field id Field qtd End Type Function Item_Carregar() obj = LoadMesh("midia/util/municao.3ds") ;-------------------------------------------caixa.Titem = New Titem caixa\modelo = obj ScaleMesh obj, 50,50,50 caixa\id = 0 caixa\qtd = 25 PositionEntity caixa\modelo, -112, 3, 113 TurnEntity caixa\modelo, 0, -90, 0 ScaleEntity caixa\modelo, .2, .2, .2 EntityType caixa\modelo, JOGO\COLI_ITEN ;-------------------------------------------caixa.Titem = New Titem caixa\modelo = CopyMesh(obj) caixa\id = 0 caixa\qtd = 15 ;-------------------------------------------. . . ;-------------------------------------------;AS CHAVES chave.Titem = New Titem

EndIf EndIf EndIf Next End Function Function Item_Descarregar() For this.Titem = Each Titem FreeEntity this\modelo Delete this Next End Function

EntityType caixa\modelo, JOGO\COLI_ITEN

Veja que a partir da segunda caixa no podemos mais fazer referncia direta ao modelo. Temos que agora usar o comando CopyMesh(). Essa caixa possui menos munio.
caixa.Titem = New Titem caixa\modelo = CopyMesh(obj) caixa\id = 0 caixa\qtd = 15

Existem vrias caixas distribudas pelo cenrio. No colocamos o cdigo que se repete sistematicamente. Colocamos os trs pontinho para dizer que havia mais cdigo nas linhas subseqentes.
;-------------------------------------------. . .

Explicando Aqui controlamos os item do jogo. Nesse jogo temos apenas dois tipos de itens: munio e chave. Mas se voc quiser colocar 100 tipos de itens a nica coisa que muda o tamanho do cdigo e a adio do evento para aquele item, afinal cada item provoca um resultado. Veja que no incio do arquivo estamos declarando o Type para os itens.
Type Titem Field modelo Field id Field qtd End Type

Como temos apenas duas chaves, carregaremos os modelos direto do arquivo. A tcnica de usar CopyMesh para evitar demoras com carregamento de vrio arquivos iguais. Veja que a id dessa chave 1.
chave.Titem = New Titem chave\modelo = LoadMesh("midia/util/chave.3ds") chave\id = 1

Abaixo a segunda chave. Sua id 2, pois cada chave abre uma porta especfica. Carregando o modelo 3d da caixa de munies.
obj = LoadMesh("midia/util/municao.3ds") chave.Titem = New Titem chave\modelo = LoadMesh("midia/util/chave.3ds") chave\id = 2

Aqui estamos criando a primeira instncia de item. Veja que como o primeiro a ser criado, esse primeiro objeto faz referncia direta ao modelo 3d carregado, assim no iremos ter que sumir com ele. Veja que definimos a id das caixas de munio para 0. Esse campo de id muito importante quando temos diferentes objetos sendo construdos com o mesmo type. Cada caixa tem uma quantidade de munio, essa possui 25. Os outros comandos apenas ajuntam o modelo em tamanho e posio e dizem qual tipo de objeto de coliso ele .
caixa.Titem = New Titem caixa\modelo = obj ScaleMesh obj, 50,50,50 caixa\id = 0 caixa\qtd = 25 PositionEntity caixa\modelo, -112, 3, 113 TurnEntity caixa\modelo, 0, -90, 0 ScaleEntity caixa\modelo, .2, .2, .2

Vamos iniciar nossa funo de lgica. Veja que pegamos a referncia do modelo 3d que representa o heri e colocamos na varivel local HERO.
Function Item_Logica() HERO = Heroy_get3d()

Nessa prxima linha verificamos as colises entre heri e itens. Lembramos que essa funo retorna a referncia do objeto que sofreu a coliso. Colocamos a referencia desse objeto na varivel COLY.
COLY = JOGO\COLI_ITEN) EntityCollided(hero,

Agora iniciamos um loop do tipo For-Each para que possamos testar todos os objetos do Tipo Titem. Veja que verificamos um por um todos os objetos do tipo para ver se foi com ele que houve a coliso.
For this.Titem = Each Titem If COLY = this\modelo

Caso tenha ocorrido uma coliso com um objeto desse tipo, ento verificamos se esse objeto uma caixa de munio, isso , se ele do tipo 0. Caso seja verdadeiro, executamos o efeito sonoro de pegar itens Audio_PlayItem(), passamos a quantidade de munio que existe na caixa para a funo de configurar tiros Heroy_SetTiro(this\qtd), e depois disso, liberamos o modelo 3d FreeEntity this\modelo. Por fim, deletamos o objeto do Type Delete this.
; se munio If this\id = 0 Audio_PlayItem() Heroy_SetTiro(this\qtd) FreeEntity this\modelo Delete this

Field cont End Type Function Porta_Carregar() porta.Tporta = New Tporta porta\modelo = LoadMesh("midia/mapa/portao.3ds") PositionEntity porta\modelo, 255.5, .8, -71 RotateEntity porta\modelo, 0, 90, 0 EntityType porta\modelo, JOGO\COLI_CEN porta\id = 2 porta\estado = 0 porta\cont = 0 porta.Tporta = New Tporta

A prxima parte verifica se o objeto colidido a chave 1. Caso seja verdadeiro, aciona o som, configura o heri como possuidor da chave 1, libera o modelo 3d e deleta o objeto do tipo.
;se a chave 1 If this\id = 1 Audio_PlayItem() Heroy_SetChave1() FreeEntity this\modelo Delete this

porta\modelo = LoadMesh("midia/mapa/portao.3ds") PositionEntity porta\modelo, 85, .8, -109.5 EntityType porta\modelo, JOGO\COLI_CEN porta\id = 1 porta\estado = 0 porta\cont = 0 End Function Function Porta_Logica() hero = Heroy_Get3d() If MouseHit(2) For this.Tporta = Each Tporta distancia = EntityDistance(hero, this\modelo) If distancia < 20 If this\id = 1 And Heroy_GetChave1() If this\estado = 0 this\estado = 1 Else If this\estado = 2 this\estado = 3 EndIf

O bloco seguinte de cdigo faz a mesma coisa que o acima, mas para a chave 2.
Else ;se a chave 2 Audio_PlayItem() Heroy_SetChave2() FreeEntity this\modelo Delete this

Nossa ultima funo a de descarregar os objetos no final do jogo.


Function Item_Descarregar() For this.Titem = Each Titem FreeEntity this\modelo Delete this Next End Function

Ela usa um loop For-Each para liberar os modelos e para deletar as instncias de itens. Logo aps, libera o som tambm. ARQUIVO PORTA.BB
Type Tporta Field modelo Field Id Field estado

EndIf EndIf If this\id = 2 And Heroy_GetChave2() If this\estado = 0 this\estado = 1 Else If this\estado = 2 this\estado = 3 EndIf EndIf EndIf EndIf Next EndIf For this.Tporta = Each Tporta If this\estado = 1 this\cont = this\cont + 1 If this\cont = 120 this\cont = 0 this\estado = 2 EndIf MoveEntity this\modelo, 0.1,0,0 EndIf If this\estado = 3 this\cont = this\cont + 1 If this\cont = 120 this\cont = 0 this\estado = 0 EndIf MoveEntity this\modelo, -0.1,0,0 EndIf Next End Function Function Porta_Descarregar() End Function For this.Tporta = Each Tporta FreeEntity this\modelo Delete this Next

Explicando Esse arquivo possui os cdigos de carregamento, controle e liberao das portas. Temos apenas duas portas nesse jogo e elas iniciam o jogo trancadas. Para que voc possa ter acesso s reas protegidas por elas, voc precisa encontrar as chaves no cenrio para liberar as mesmas. O cdigo se inicia com a declarao do Type prottipo do objeto porta. Temos um campo para o modelo 3d e um para a identidade da porta, afinal deveremos reconhecer qual cada porta para que ela se abra s se tivermos a chave correta. Tambm h um campo para dizer qual estado atual de cada porta, elas possuem 4 estados: fechado, abrindo, aberta e fechando. O ultimo campo uma varivel para um contador, para controlarmos o movimento de abertura e fechamento da porta.
Type Tporta Field modelo Field Id Field estado Field cont End Type

Logo a segui aparece a nossa funo construtora de portas. Como temos apenas duas portas, estamos carregando o modelo 3d diretamente do arquivo para as duas portas. Posicionamos a porta no local correto com o comando PositionEntity e configuramos seu grupo de colises. Atribumos porta uma identidade e zeramos os estados iniciais dela.
Function Porta_Carregar() porta.Tporta = New Tporta porta\modelo = LoadMesh("midia/mapa/portao.3ds") PositionEntity porta\modelo, 255.5, .8, -71 RotateEntity porta\modelo, 0, 90, 0 EntityType porta\modelo, JOGO\COLI_CEN porta\id = 2 porta\estado = 0

porta\cont = 0

Veja que fizemos o mesmo tipo de procedimento para a outra porta, s muda a posio e a id dela.
porta.Tporta = New Tporta porta\modelo = LoadMesh("midia/mapa/portao.3ds") PositionEntity porta\modelo, 85, .8, -109.5 EntityType porta\modelo, JOGO\COLI_CEN porta\id = 1 porta\estado = 0 porta\cont = 0

this\estado = 1 Else If this\estado = 2 this\estado = 3 EndIf EndIf EndIf

Veja que o cdigo abaixo semelhante ao cdigo de cima, mas aplicado para a segunda porta.
If this\id = 2 And Heroy_GetChave2() If this\estado = 0 this\estado = 1 Else If this\estado = 2 this\estado = 3 EndIf EndIf EndIf

A nossa funo de lgica precisa estabelecer relaes com o modelo 3d de nosso heri, para saber se ele est prximo da porta. Por isso, logo de incio j pegamos a sua referncia.
Function Porta_Logica() hero = Heroy_Get3d()

Iniciamos o controle de eventos das portas com o loop For-Each para ter acesso s duas portas.
For this.Tporta = Each Tporta

Os eventos para a porta se daro se pressionarmos o boto direito do mouse. Quando isso acontecer, iremos entrar em um loop For-Each para ter acesso a todas as portas.
If MouseHit(2) For this.Tporta = Each Tporta

Para que o evento (abrir ou fechar) ocorra, o personagem deve estar a uma distncia mnima da porta. Definimos isso como 20. Por isso, primeiro testamos a distncia entre o personagem e cada porta, se estiver dentro do estabelecido o evento poder ocorrer.
distancia = this\modelo) If distancia < 20 EntityDistance(hero,

Os eventos da porta, fechar ou abrir, dependem da varivel de estados da porta. Por isso nossa primeira verificao qual o estado atual. Se o estado 1 ento estamos abrindo a porta. Para abrir a porta criamos um evento paralelo ao processamento do jogo. Cada vez que a execuo passa por essa parte do cdigo verifica se o estado de abrir (1) est acionado. Se est, incrementa o contador de aes em uma unidade this\cont=this\cont + 1 e move a porta em um dcimo de unidade de distncia para a sua lateral (eixo x). Quando o contador atingir o valor 100, ir mudar o estado da porta para Fechada e ir reiniciar seu valor.
If this\estado = 1 this\cont = this\cont + 1 If this\cont = 120 this\cont = 0 this\estado = 2 EndIf MoveEntity this\modelo, EndIf

Abaixo temos o algoritmo de controle da porta. Primeiro testamos qual a porta que estamos aplicando o evento If this\id = 1, e se j pegamos a chave dela Heroy_GetChave1(). Nesse caso, estamos verificando se a porta 1 e se o jogador j pegou a chave dessa porta. Caso isso seja verdadeiro, temos que verificar se a porta est aberta ou fecha, pois se tiver fechada If this\estado = 0 ento deveremos acionar o estado de abrindo que o estado 1 this\estado = 1. Caso esteja aberta If this\estado = 2 deveremos acionar o estado de fechando this\estado = 3.
If this\id = 1 And Heroy_GetChave1() If this\estado = 0

0.1,0,0

Fizemos o mesmo tipo de lgica para o estado 3, mas s a mudamos os valores das frags e a direo de movimento que ao contrrio para poder fechar a porta.
If this\estado = 3 this\cont = this\cont + 1 If this\cont = 120 this\cont = 0 this\estado = 0 EndIf MoveEntity this\modelo, -0.1,0,0 EndIf

Nossa ultima funo descarrega os objetos porta da memria. Primeiro liberamos o modelo 3d FreeEntity this\modelo, depois deletamos as instncias Delete this.
Function Porta_Descarregar() For this.Tporta = Each Tporta FreeEntity this\modelo Delete this Next End Function

For this.Tcenario = Each Tcenario FreeEntity this\ob Delete this Next End Function

Explicando Vamos a explicao do cdigo do cenrio. De incio declaramos o type para ele.
Type Tcenario Field ob Field id End Type

ARQUIVO CENARIO.BB
Type Tcenario Field ob Field id End Type Function Cenario_Carregar() ;CENARIO BASE Cenario.Tcenario = New Tcenario Cenario\ob = LoadMesh( "midia/mapa/mapa.b3d" ) ScaleEntity Cenario\ob, 0.3, 0.3, 0.3 Cenario\id = 1 EntityType Cenario\ob, JOGO\COLI_CEN ;SKY DOME Cenario.Tcenario = New Tcenario Cenario\ob = CreateSphere(8) ScaleEntity Cenario\ob,500,300,500 sky_tex=LoadTexture("midia/mapa/sk y.bmp") ScaleTexture sky_tex,0.25,0.5 ;Dimenciona a Textura EntityTexture Cenario\ob,sky_tex FlipMesh Cenario\ob Cenario\id = 3 End Function Function Cenario_Controle() Hero = Heroy_get3d() px# = EntityX(hero) pz# = EntityZ(hero) For this.Tcenario = Each Tcenario If this\id = 3 PositionEntity this\ob, px#, 0, pz# TurnEntity this\ob, 0, 0.01, 0 EndIf Next End Function

Logo a segui carregamos o cenrio, demos uma identidade a ele e configuramos seu grupo de colises.
Cenario.Tcenario = New Tcenario Cenario\ob = LoadMesh( "midia/mapa/mapa.b3d" ) ScaleEntity Cenario\ob, 0.3, 0.3, 0.3 Cenario\id = 1 EntityType Cenario\ob, JOGO\COLI_CEN

Criamos um skydome (ou skySphere). Um skydome uma grande esfera que usada para representar o cu do ambiente. Criamos ele com o comando CreteSphere() do Blitz3d, depois o redimensionamos para um tamanho um pouco menor que o alcance da cmera e o texturizamos com uma textura de cu. Como a textura aplicada sempre do lado de fora dos objetos, tivemos que usar o comando FlipMesh para deixar o objeto no avesso, isso , inverter suas faces para dentro, seno ele seria invisvel.
;SKY DOME Cenario.Tcenario = New Tcenario Cenario\ob = CreateSphere(8) ScaleEntity Cenario\ob,500,300,500 sky_tex=LoadTexture("midia/mapa/sky.bmp" ) ScaleTexture sky_tex,0.25,0.5 EntityTexture Cenario\ob,sky_tex FlipMesh Cenario\ob Cenario\id = 3

A funo de controle do cenrio muito simples, pois ela s controla o skydome. Como ele deve acompanhar o heri, tivemos que pegar a referncia do modelo 3d dele Heroy_get3d(). Depois pegamos a posio x e y desse modelo para passar para o skydome. Para achar o objeto correto, procuramos pela id de cada uma das entidades do cenrio If this\id = 3. Se achamos o skydome, isso , a id de valor 3, ento passamos a ela a posio do heri e aplicamos uma leve rotao ao mesmo.
Function Cenario_Controle()

Function Cenario_DesCarregar()

Hero = Heroy_get3d() px# = EntityX(hero) pz# = EntityZ(hero) For this.Tcenario = Each Tcenario If this\id = 3 PositionEntity this\ob, px#, 0, pz# TurnEntity this\ob, 0, 0.01, 0 EndIf Next End Function

ExtractAnimSeq(Heroy\arma, 62,76) Animate Heroy\arma Heroy\anima = 0 heroy\vida = 100 heroy\tiro = 0 End Function Function Heroy_Get3D() For this.Thero = Each Thero Return this\esfera Next End Function Function Heroy_GetCamera() For this.Thero = Each Thero Return this\Camera Next End Function

Nossa ltima funo se limita a descarregar os modelos e as instncias do type.


Function Cenario_DesCarregar() For this.Tcenario = Each Tcenario FreeEntity this\ob Delete this Next End Function

ARQUIVO HEROY.BB
Type Thero Field Field Field Field Field Field Field Field Field Field End Type ESFERA CAMERA arma anima vida tiro chave1 chave2 turn_x# turn_y#

Function Heroy_Controle() For this.Thero = Each Thero If KeyDown(200) Then MoveEntity this\ESFERA, 0, 0, 1 If KeyDown(208) Then MoveEntity this\ESFERA, 0, 0, -1 If KeyDown(203) Then MoveEntity this\ESFERA, -1, 0, 0 If KeyDown(205) Then MoveEntity this\ESFERA, 1, 0, 0 ty# = (MouseXSpeed() * (-1)) / 2 tx# = MouseYSpeed() / 2 this\turn_x# = this\turn_x# + tx# this\turn_y# = this\turn_y# + ty# If this\turn_x# = If this\turn_x# = this\turn_x# < -10 Then -10 this\turn_x# > 20 Then 20

Function Heroy_Carregar() Heroy.Thero = New Thero Heroy\ESFERA = CreateSphere() ScaleMesh Heroy\ESFERA, 1, 4, 1 PositionEntity Heroy\ESFERA, 72,7,100 EntityType Heroy\ESFERA, JOGO\COLI_HER EntityRadius Heroy\ESFERA, 2, 4 EntityAlpha Heroy\ESFERA, 0 Heroy\CAMERA=CreateCamera(Heroy\ES FERA) MoveEntity Heroy\CAMERA, 0, 3, -1 CameraRange Heroy\CAMERA, .5, 1000 Heroy\arma = LoadAnimMesh("midia/atores/hud.x",Heroy\ Esfera) RotateEntity Heroy\arma, 0, 180, 1 ScaleEntity Heroy\arma, .13, .13, .13 MoveEntity Heroy\arma, .5, 2, 1 ExtractAnimSeq(Heroy\arma, 46,62)

RotateEntity this\esfera, this\turn_x#, this\turn_y#, 0 If MouseHit(1) If this\tiro > 0 HEROY_Disparar() this\tiro = this\tiro - 1 Audio_PlayTiro() Else Audio_PlayTiroSeco() EndIf EndIf ;gravidade TranslateEntity this\ESFERA, 0, -.6, 0

;animaes time = AnimTime(this\arma) If this\anima = 0 If time > 45 Animate this\arma,1,.2,1 this\anima = 1 EndIf EndIf If this\anima = 2 If time > 13 Animate this\arma,1,.2,1 this\anima = 1 EndIf EndIf Next End Function Function HEROY_Disparar() For this.Thero = Each Thero ;animao Animate this\arma,1,.5,2 this\anima = 2 ;Pega entidade no alvo ENTITY=CameraPick( THIS\CAMERA, JOGO\Tela_X/2 , JOGO\TELA_Y/2) ;Verifica se algum dos inimigos If ENTITY Then Zumbi_Tiro(ENTITY) If ENTITY Then Mutante_Tiro(ENTITY) If ENTITY Then Fantasma_Tiro(ENTITY) Next End Function Function Heroy_DesCarregar() For this.Thero = Each Thero FreeEntity this\esfera Delete this Next End Function Function Heroy_SetVida(valor) For this.Thero = Each Thero this\vida = this\vida + valor Next End Function Function Heroy_GetVida() For this.Thero = Each Thero Return this\vida Next

End Function Function Heroy_SetTiro(valor) For this.Thero = Each Thero this\tiro = this\tiro + valor Next End Function Function Heroy_GetTiro() For this.Thero = Each Thero Return this\tiro Next End Function Function Heroy_SetChave1() For this.Thero = Each Thero this\chave1 = 1 Next End Function Function Heroy_GetChave1() For this.Thero = Each Thero Return this\chave1 Next End Function Function Heroy_SetChave2() For this.Thero = Each Thero this\chave2 = 1 Next End Function Function Heroy_GetChave2() For this.Thero = Each Thero Return this\chave2 Next End Function Function Heroy_GetTurn_x#() For this.Thero = Each Thero Return this\turn_x# Next End Function

Explicando Como podemos ver, nosso heri tem uma estrutura bem complexa:
Type Thero Field Field Field Field ESFERA CAMERA arma anima

Field Field Field Field Field Field End Type

vida tiro chave1 chave2 turn_x# turn_y#

O passo seguinte foi dizer que essa entidade dever se comportar como uma entidade de coliso:
EntityType JOGO\COLI_HER Heroy\ESFERA,

Como nosso modelo 3d ser um hud, o campo esfera ser usado para conter um objeto para ser usado como referncia para colises. A cmera dever sempre acompanhar o personagem, bem por isso ele deve ser uma entidade pertencente ao nosso heri. O campo arma dever receber o modelo 3d, o hud. J o campo anima ser nossa varivel de controle de animao. A varivel vida conter o status de vida atual do personagem enquanto que tiro ter a quantidade de munio que ele possui. Os campos chave1 e chave2 servem como um inventrio do jogador, pois indicam quais objetos o mesmo recolheu no cenrio. Fizemos assim pois esse jogo contm poucos itens a serem recolhidos, mas em um jogo mais complexo, como um RPG, voc deve considerar a possibilidade de usar alguma coleo para armazenar uma grande quantidade de objetos. Por fim, os campos turn_x# e turn_y# contm os ngulos atuais do nosso personagem.
Function Heroy_Carregar() Heroy.Thero = New Thero Heroy\ESFERA = CreateSphere() ScaleMesh Heroy\ESFERA, 1, 4, 1 PositionEntity Heroy\ESFERA, 72,7,100 EntityType Heroy\ESFERA, JOGO\COLI_HER EntityRadius Heroy\ESFERA, 2, 4 EntityAlpha Heroy\ESFERA, 0

Embora tenhamos criado uma esfera para usar como referncia para colises, essa mesma receber um sistema de coliso do tipo esfera e por isso tambm temos que configurar o raio da esfera do sistema de colises com o comando EntityRadius.
EntityRadius Heroy\ESFERA, 2, 4

No confunda o objeto esfera com a entidade de coliso esfera. Tivemos que criar uma esfera para servir de base para a entidade de coliso esfera, pois do contrrio no teramos como ajustar o hud, isso se faz necessrio pois o centro lgico do modelo 3d do hud est deslocado. A criao de um objeto de referncia facilita as coisas, pois podemos fazer o que quisermos com o objeto visvel (o hud), pois ele no ser referncia para colidir. Assim, colocamos o hud no lugar que ele deve aparecer e deixamos a esfera no lugar correto para colidir.
EntityAlpha Heroy\ESFERA, 0

Por fim, deixamos a esfera invisvel, pois ela apenas uma entidade auxiliar de fsica.

Vamos agora s configuraes da cmera:


Heroy\CAMERA=CreateCamera(Heroy\ESFERA) MoveEntity Heroy\CAMERA, 0, 3, -1 CameraRange Heroy\CAMERA, .5, 1000

Nesse trecho do cdigo estamos criando a esfera de coliso do personagem. Veja que usamos um comando de escalonamento para deixar a mesma mais fina e alta, para ter um formato semelhante ao espao ocupado por uma pessoa:
ScaleMesh Heroy\ESFERA, 1, 4, 1

Veja que ela criada como filha da entidade esfera. Como dissemos, a esfera, apesar de ser invisvel, a nossa entidade de referncia para toda a fsica, por isso a cmera filha dela. Assim, a cmera receber por herana todo do comportamento que a esfera tiver. Se a esfera rotacionar, a cmera tambm far o mesmo. Se a esfera se deslocar, a cmera a seguir. Usamos o comando MoveEntity para deixar a cmera um pouco atrs da esfera, para podemos visualizar o hud e ajustamos o

alcance da viso da cmera, para podermos visualizar corretamente o hud sem cortes.

movimento de recuo provocado pela fora do tiro.


Animate Heroy\arma Heroy\anima = 0

Abaixo o cdigo para carregar e configurar o hud. Perceba que ele tambm um objeto filho da esfera de referncia de fsica, assim tambm herdando seu comportamento. Tambm foram necessrios ajustes de tamanho, ngulo e posicionamento do modelo 3d para que ele se ajustasse perfeitamente a esse jogo.
Heroy\arma = LoadAnimMesh("midia/atores/hud.x",Heroy\ Esfera) RotateEntity Heroy\arma, 0, 180, 1 ScaleEntity Heroy\arma, .13, .13, .13 MoveEntity Heroy\arma, .5, 2, 1

Como voc pode ver acima, j disparamos a primeira animao, que o movimento de sacar a arma. A nossa varivel de controle tambm ajustada para poder saber qual animao est rodando, no caso a animao 0, a de sacar a arma. Essa varivel foi uma Flag que ns criamos para facilitar o controle de animaes.
heroy\vida = 100 heroy\tiro = 0 End Function

A seguir fizemos as configuraes das animaes do modelo 3d:


ExtractAnimSeq(Heroy\arma, 46,62) ExtractAnimSeq(Heroy\arma, 62,76) Animate Heroy\arma,1,.2 Heroy\anima = 0

No final da funo temos a configurao da quantidade de vida do personagem e a munio inicial do mesmo.

O modelo comercial, por isso no veio com as animaes sobre encomendas. Assim temos que extrair as animaes que nos interessa. No jogo usamos 3 animaes: Sacar Segurar Disparar

Abaixo temos uma funo utilitria do jogo, um servio que o heroy presta para outras entidades da aplicao. Algumas entidades necessitaro ter informaes sobre o heri para poderem funcionar. Essa funo retorna a referncia da entidade fsica, a esfera, para quem a invocar.
Function Heroy_Get3D() For this.Thero = Each Thero Return this\esfera Next End Function

A animao se sacar ser usada apenas uma vez, no inicio do jogo o personagem saca a arma. Como ela est no incio da animao da entidade, no ser necessrio extrair a mesma.
ExtractAnimSeq(Heroy\arma, 46,62)

Essa segunda funo utilitria retorna a cmera para quem a invocar.


Function Heroy_GetCamera() For this.Thero = Each Thero Return this\Camera Next End Function

A animao de segurar a arma importante, pois d mais realismo ao jogo. Quando seguramos uma arma ela no fica sempre imvel, natural que ela se mexa um pouco, principalmente quando andamos.
ExtractAnimSeq(Heroy\arma, 62,76)

Agora entramos na funo mais importante da estrutura do heri, nossa funo de controle:
Function Heroy_Controle() For this.Thero = Each Thero If KeyDown(200) Then MoveEntity this\ESFERA, 0, 0, 1

Disparar o tiro com certeza a animao de maior destaque, afinal h um forte

If KeyDown(208) Then MoveEntity this\ESFERA, 0, 0, -1 If KeyDown(203) Then MoveEntity this\ESFERA, -1, 0, 0 If KeyDown(205) Then MoveEntity this\ESFERA, 1, 0, 0

RotateEntity this\esfera, this\turn_x#, this\turn_y#, 0

Agora nosso controle de eventos de disparar tiro:


If MouseHit(1) If this\tiro > 0 HEROY_Disparar() this\tiro = this\tiro - 1 Audio_PlayTiro() Else Audio_PlayTiroSeco() EndIf EndIf

O trecho acima da funo responsvel pelo controle de movimento do personagem. Captamos os eventos de pressionar as teclas de seta do teclado e movemos a entidade conforme a tecla pressionada.
ty# = (MouseXSpeed() * (-1)) / 2 tx# = MouseYSpeed() / 2 this\turn_x# = this\turn_x# + tx# this\turn_y# = this\turn_y# + ty# If this\turn_x# = If this\turn_x# = this\turn_x# < -10 Then -10 this\turn_x# > 20 Then 20

O bloco de cdigo acima s invoca a funo de disparar tiros, mas no o dispara de fato. Assim sendo uma estrutura de controle de eventos. Veja que o tiro s ser disparado se o boto esquerdo do mouse for clicado e a quantidade de tiros disponveis for maior que zero:
If MouseHit(1) If this\tiro > 0

RotateEntity this\esfera, this\turn_x#, this\turn_y#, 0

Essa parte a responsvel por fazer o mouse controlar para onde o heri est olhando. Para no deixar o movimento muito brusco, reduzimos a velocidade do MouseSpeed dividindo o mesmo por 2:
ty# = (MouseXSpeed() * (-1)) / 2 tx# = MouseYSpeed() / 2

Quando isso for verdadeiro, disparamos o tiro, debitamos 1 tiro da caixa de munio e executamos o udio de tiro:
HEROY_Disparar() this\tiro = this\tiro - 1 Audio_PlayTiro()

Colocamos o resultado disso em duas variveis que sero usadas para ajustar o angulo atual do heri:
this\turn_x# = this\turn_x# + tx# this\turn_y# = this\turn_y# + ty#

Caso tenhamos clicado o boto esquerdo do mouse, mas no tenhamos munio disponvel, ser executado o som de tiro seco.
Audio_PlayTiroSeco()

Tambm temos que limitar at onde ele pode virar no eixo x, isso , limitamos o olhar para cima e olhar para baixo:
If this\turn_x# = If this\turn_x# = this\turn_x# < -10 Then -10 this\turn_x# > 20 Then 20

A seguir nosso efeito de gravidade para deixar o heri no cho. Como o jogo no possui eventos fsicos complexos, um simples TranslateEntity resolve o problema:
;gravidade TranslateEntity this\ESFERA, 0, -.6, 0

Agora s atualizar esses dados no heroi:

Vamos agora ao controle de animaes...


;animaes time = AnimTime(this\arma)

If this\anima = 0 If time > 45 Animate this\arma,1,.2,1 this\anima = 1 EndIf EndIf

;Pega entidade no alvo ENTITY=CameraPick( THIS\CAMERA, JOGO\Tela_X/2 , JOGO\TELA_Y/2) ;Verifica se algum dos inimigos If ENTITY Then Zumbi_Tiro(ENTITY) If ENTITY Then Mutante_Tiro(ENTITY) If ENTITY Then Fantasma_Tiro(ENTITY) Next End Function

Primeiro pegamos o frame atual da animao e colocamos na varivel time:


time = AnimTime(this\arma)

O passo seguinte verificar se a animao atual a animao 0, aquela de sacar a arma:


If this\anima = 0

Primeiro rodamos a animao de disparar tiros do modelo 3d e configuramos a varivel de controle de animao:
Animate this\arma,1,.5,2 this\anima = 2

Se a animao que estiver rodando for essa, ento temos que ver se ela chegou ao seu final, isso , se ela chegou ao frame 45:
If time > 45

O passo seguinte disparar o evento Pick da camera para a entidade que estiver na mira:
ENTITY=CameraPick( THIS\CAMERA, JOGO\Tela_X/2 , JOGO\TELA_Y/2)

Se isso verdadeiro tambm, temo que mudar a animao para a animao 1, a de segurar a arma, caso contrrio o heri vai ficar sacando e guardando a arma o tempo todo, paralelo a isso tambm alteramos a varivel de controle para dizer que agora a animao 1 que est rodando:
Animate this\arma,1,.2,1 this\anima = 1

A seguir, basta enviar a entidade detectada para as funes de verificao de tiro de cada personagem pois essas funes devero verificar qual personagem sofreu o tiro e tirar sua vida:
If ENTITY Then Zumbi_Tiro(ENTITY) If ENTITY Then Mutante_Tiro(ENTITY) If ENTITY Fantasma_Tiro(ENTITY)

Nosso controle de animao no para por ai. Tambm devemos fazer a mesma coisa com a animao de disparar tiro. Quando essa chegar ao seu fim, tambm deve retornar para a animao de segurar arma:
If this\anima = 2 If time > 13 Animate this\arma,1,.2,1 this\anima = 1 EndIf EndIf

Then

Abaixo a funo de descarregar para a entidade heroi:


Function Heroy_DesCarregar() For this.Thero = Each Thero FreeEntity this\esfera Delete this Next End Function

Vamos agora a nossa ltima funo complexa desse personagem, a de disparar tiros.
Function HEROY_Disparar() For this.Thero = Each Thero ;animao Animate this\arma,1,.5,2 this\anima = 2

A nossa funo de controle de vida do heri. Ela ser usada quando um inimigo atacar o mesmo. Tambm poderia ser usada para pegar itens de vida, caso o jogo tivesse esse tipo de item.

Function Heroy_SetVida(valor) For this.Thero = Each Thero this\vida = this\vida + valor Next End Function

For this.Thero = Each Thero this\chave2 = 1 Next End Function

A funo abaixo informa a vida atual do heri. usada para saber se ele morreu e para exibir o status atual de vida na tela:
Function Heroy_GetVida() For this.Thero = Each Thero Return this\vida Next End Function

A funo de informao se a chave 2 foi coletada ou no:


Function Heroy_GetChave2() For this.Thero = Each Thero Return this\chave2 Next End Function

E por fim, a ltima funo desse arquivo, retorna o ngulo atual do heri: Nossa funo de controle de munio usada quando disparamos uma tiro e quando pegamos itens da caixa de munio:
Function Heroy_SetTiro(valor) For this.Thero = Each Thero this\tiro = this\tiro + valor Next End Function Function Heroy_GetTurn_x#() For this.Thero = Each Thero Return this\turn_x# Next End Function

A funo de informao de quantidade de tiros disponveis:


Function Heroy_GetTiro() For this.Thero = Each Thero Return this\tiro Next End Function

OS INIMIGOS Nesse jogo temo 3 entidades inimigas: zumbis, fantasmas e mutantes. Eles possuem a mesma estrutura lgica, assim sendo, poderiam at estar todos no mesmo arquivo, como membros de uma mesma entidade. Assim s precisaramos criar um campo de ID para diferenciar cada um deles e tratar os mesmos de acordo com suas peculiariedades. Preferimos colocar cada entidade em um arquivo lgico diferente. Isso deixa o nosso cdigo fonte bem maior, mais que o dobro de cdigo para fazer a mesma coisa. Mas fizemos isso para deixar o cdigo mais fcil de ser compreendido. Se fossemos tratar as trs entidades no mesmo cdigo, teramos vrias excees a serem tratadas e assim nosso cdigo ficaria cheio de IFS e ELSES e isso no bom para iniciantes... Assim, privilegiamos o aspecto pedaggico em detrimento ao tcnico aqui. Portanto saiba que essa heresia de retrabalho devido ao no reuso de cdigo foi proposital para facilitar a vida de quem iniciante. Por outro lado, como a estrutura lgica a mesma, e s h diferena em alguns detalhes, no vamos perder tempo aqui explicando 3 vezes a mesma coisa. Assim

Nossa funo se pegar a chave 1:


Function Heroy_SetChave1() For this.Thero = Each Thero this\chave1 = 1 Next End Function

A funo de informao se a chave 1 foi pega:


Function Heroy_GetChave1() For this.Thero = Each Thero Return this\chave1 Next End Function

Nossa funo para coletar a chave 2:


Function Heroy_SetChave2()

iremos abrir o cdigo de apenas um desses elementos, pois o que muda de um para outro so apenas coisas como o tamanho da animao, a distncia, o tamanho e a posio da esfera de colises, etc. ARQUIVO ZUMBI.BB
Type Tzumbi Field fisico Field vida Field anima Field anima_para Field anima_anda Field anima_ataca Field anima_morre End Type Function Zumbi_Carregar() ;ZUMBI 1 inimigo.Tzumbi = New Tzumbi inimigo\vida = 5 inimigo\anima = 0 inimigo\fisico = CreateSphere() ScaleMesh inimigo\fisico, 1.3, 3.7, 1.3 PositionEntity inimigo\fisico, 190,8,80 EntityType inimigo\fisico, JOGO\COLI_INI EntityRadius inimigo\fisico, 1.5, 4.5 EntityAlpha inimigo\fisico, 0 EntityPickMode inimigo\fisico, 2 inimigo\anima_para = LoadAnimMesh("midia/atores/ZumbiParado.3 ds", inimigo\fisico) inimigo\anima_anda = LoadAnimMesh("midia/atores/ZumbiMove.3ds ", inimigo\fisico) ExtractAnimSeq(inimigo\anima_anda, 4, 25) inimigo\anima_ataca= LoadAnimMesh("midia/atores/ZumbiAtaca.3d s", inimigo\fisico ) inimigo\anima_morre= LoadAnimMesh("midia/atores/ZumbiMorre.3d s", inimigo\fisico ) ScaleEntity inimigo\anima_para, 4, 4, 4 ScaleEntity inimigo\anima_anda, 4, 4, 4 ScaleEntity inimigo\anima_ataca,4, 4, 4 ScaleEntity inimigo\anima_morre,4, 4, 4 MoveEntity inimigo\anima_para, 0, 3, 0

MoveEntity inimigo\anima_anda, 0, 3, 0 MoveEntity inimigo\anima_ataca,0, 3, 0 MoveEntity inimigo\anima_morre,0, 3, 0 Animate inimigo\anima_para, 1, .5 HideEntity inimigo\anima_anda HideEntity inimigo\anima_ataca HideEntity inimigo\anima_morre inimigo\anima = 0 ;--------------------------------------. . . ;-------------------------------------End Function

Function Zumbi_Get() For this.Tzumbi = Each Tzumbi qtd = qtd + 1 Next Return qtd End Function Function Zumbi_IA() heroy = Heroy_Get3D() For this.Tzumbi = Each Tzumbi ;gravidade TranslateEntity this\fisico, 0, -.1, 0 DISTANCIA# = EntityDistance(this\fisico, heroy) PointEntity (this\fisico, heroy) ANGULO = EntityYaw(this\fisico) If this\tipo = 1 RotateEntity this\fisico, 0, ANGULO, 0 Else RotateEntity this\fisico, 0, ANGULO-180, 0 EndIf If DISTANCIA# < 70 And this\vida > 0 If DISTANCIA# < 5 And DISTANCIA# > 2 If this\anima_ataca, 1, .8 this\anima <> 2 Animate

ShowEntity this\anima_ataca HideEntity this\anima_anda HideEntity this\anima_para HideEntity this\anima_morre Audio_PlayAtaca() EndIf this\anima = 2 anima = AnimTime(this\anima_ataca) If anima = 15 Then Heroy_setvida(-10) Else MoveEntity this\fisico, 0, 0, -.4 this\anima <> 1 Animate this\anima_anda, 1, .8, 1 ShowEntity this\anima_anda HideEntity this\anima_ataca HideEntity this\anima_para HideEntity this\anima_morre EndIf this\anima = 1 EndIf Else If this\vida > 0 this\anima <> 0 Animate this\anima_para, 1, .5 ShowEntity this\anima_para HideEntity this\anima_ataca HideEntity this\anima_anda HideEntity this\anima_morre EndIf this\anima = 0 End If Next End Function ;Funo de Logica Function Zumbi_Logica() For this.Tzumbi = Each Tzumbi If this\vida < 1 If this\anima <> 3 If If

Animate this\anima_morre, 3, .6 ShowEntity this\anima_morre HideEntity this\anima_ataca HideEntity this\anima_para HideEntity this\anima_anda EndIf this\anima = 3 anima = AnimTime(this\anima_morre) If anima > 24 FreeEntity this\fisico Delete this EndIf EndIf Next End Function ;Funo de deteco de tiros Function Zumbi_Tiro(ENTIDADE) For this.Tzumbi = Each Tzumbi If (ENTIDADE = this\fisico) Then this\vida = this\vida - 1 Next End Function ;Funo para descarregar objetos Function Zumbi_DesCarregar() For this.Tzumbi = Each Tzumbi FreeEntity this\fisico Delete this Next End Function

Explicando... Abaixo a estrutura da entidade zumbi, que a mesma das outras entidades inimigas:
Type Tzumbi Field fisico Field vida Field anima Field anima_para Field anima_anda Field anima_ataca Field anima_morre End Type

O campo fisico dever conter a esfera de referncia de fsica. O campo vida tem a quantidade de vida da entidade. Anima a nossa varivel de controle de animao. Os

demais campos devero receber 4 modelos 3d, cada um conter uma animao diferente. Veja que nesse projeto estamos usando uma tcnica diferente para animao. Existem duas formas de se trabalhar com animaes: ou voc deixa tudo em um nico modelo 3d ou ento voc cria vrios modelos (arquivos), cada um com animaes diferentes. Os modelos 3d que estamos usando para esse projeto (zumbi, mutante e fantasma) foram criados de forma que cada arquivo possui apenas uma animao. Assim, como vamos usar 4 animaes (parado, andando, atacando e morrendo) temos que carregar 4 modelos, um para cada animao.

Depois posicionamos o mesmo no seu lugar de origem no mapa:


PositionEntity inimigo\fisico, 190,8,80

Configuramos a esfera para se comportar como uma entidade de coliso de tipo inimigo:
EntityType inimigo\fisico, JOGO\COLI_INI

Ajustamos o raio da entidade de coliso:


EntityRadius inimigo\fisico, 1.5, 4.5

Deixamos a esfera auxiliar de fsica invisvel:


EntityAlpha inimigo\fisico, 0

Vamos iniciar a nossa carregamento de entidades:


Function Zumbi_Carregar()

funo

de

E ligamos o pickmode para a mesma. Como a fsica ser toda controlada pela esfera, tambm o pickmode deve ser feito com a esfera para detectarmos os tiros.
EntityPickMode inimigo\fisico, 2

;ZUMBI 1 inimigo.Tzumbi = New Tzumbi inimigo\vida = 5 inimigo\anima = 0

Instanciamos um objeto do tipo zumbi, configuramos a vida e configuramos a varivel de controle de animao para a animao inicial, isso , parado.
inimigo\fisico = CreateSphere() ScaleMesh inimigo\fisico, 1.3, 3.7, 1.3 PositionEntity inimigo\fisico, 190,8,80 EntityType inimigo\fisico, JOGO\COLI_INI EntityRadius inimigo\fisico, 1.5, 4.5 EntityAlpha inimigo\fisico, 0 EntityPickMode inimigo\fisico, 2

Abaixo carregamos os modelos 3d e ajustamos algumas animaes para se comportarem corretamente. Ao carreg-los, j o fazemos informando que sero filhos da etidade de fsica, assim esses modelos 3d recebero o mesmo comportamento por herana. Veja que para a animao de andar tnhamos alguns frames sobrando e fizemos um ajuste:
inimigo\anima_para = LoadAnimMesh("midia/atores/ZumbiParado.3 ds", inimigo\fisico) inimigo\anima_anda = LoadAnimMesh("midia/atores/ZumbiMove.3ds ", inimigo\fisico) ExtractAnimSeq(inimigo\anima_anda, 4, 25) inimigo\anima_ataca = LoadAnimMesh("midia/atores/ZumbiAtaca.3d s", inimigo\fisico ) inimigo\anima_morre = LoadAnimMesh("midia/atores/ZumbiMorre.3d s", inimigo\fisico )

As linhas acima se dedicam exclusivamente a configurar nossa esfera de suporte a fsica. Na primeira linha criamos a esfera:
inimigo\fisico = CreateSphere()

Nossa segunda linha ajusta o tamanho dela para ficar semelhante ao modelo 3d do inimigo:
ScaleMesh inimigo\fisico, 1.3, 3.7, 1.3

Abaixo ajustamos o tamanho das entidades para ficarem mais realistas:


ScaleEntity inimigo\anima_para, 4, 4, 4 ScaleEntity inimigo\anima_anda, 4, 4, 4

ScaleEntity inimigo\anima_ataca,4, 4, 4 ScaleEntity inimigo\anima_morre,4, 4, 4

A seguir, ajustamos os modelos 3d para que apaream no lugar correto da tela. Como o centro lgico deles est nos ps, tivemos que mov-los um pouco mais baixo; do contrrio eles pareceriam estar flutuando.
MoveEntity 0 MoveEntity 0 MoveEntity 0 MoveEntity 0 inimigo\anima_para, 0, -3.4, inimigo\anima_anda, 0, -3.4, inimigo\anima_ataca,0, -3.4, inimigo\anima_morre,0, -3.4,

Como existem vrias entidades inimigas no cenrio, os seus controles devem estar dentro de um loop do tipo For-Each.
For this.Tzumbi = Each Tzumbi

Abaixo aplicamos o efeito de gravidade para eles:


;gravidade TranslateEntity this\fisico, 0, -.1, 0

Agora vamos descobrir a que distncia cada entidade zumbi est do heri:
DISTANCIA# = EntityDistance(this\fisico, heroy)

Agora configuramos a animao para iniciar o jogo. Ligamos a animao inicial, a de estar parado e escondemos os demais modelos. Lembre-se que carregamos 4 modelos e s podemos exibir um de cada vez; assim, quando mostrarmos um, devemos esconder os outros com o comando HideEntity.
Animate inimigo\anima_para, 1, .5 HideEntity inimigo\anima_anda HideEntity inimigo\anima_ataca HideEntity inimigo\anima_morre inimigo\anima = 0

Abaixo fazemos com que os inimigos sempre olhem para o heri, isso , eles sempre apontaro para ele. Como a frente dele est invertida em relao ao modelo de referncia geomtrica do blitz3d, tivemos que usar o comando TurnEntiity para corrigir isso:
PointEntity (this\fisico, heroy) TurnEntity this\fisico, 0, 180, 0

Agora vamos a nossa funo de verificao de quantos inimigos esto vivos. por meio dessa funo que sabemos a quantidade de inimigos ativos no jogo e se eles forem igual a 0 (zumbis, mutantes e fantasmas), ento a misso est completa.
Function Zumbi_Get() For this.Tzumbi = Each Tzumbi qtd = qtd + 1 Next Return qtd End Function

Agora vamos a ia propriamente dita. O comportamento dos inimigos s ser ativado se eles estiverem a uma distncia menor que 70 e se ele possurem vida. Essa questo da vida importante, pois mesmo sem vida eles ainda ficam presentes no cenrio por alguns instantes, rodando a animao de morrer. Se no colocarmos essa segunda condio, a animao de morrer jamais funcionar.
If DISTANCIA# < 70 And this\vida > 0

Agora nossa segunda condio, se a distncia for menor que 5, ele ataca o heri.
If DISTANCIA# < 5 And DISTANCIA# > 2 If this\anima <> 2 Animate this\anima_ataca, 1, .8 ShowEntity this\anima_ataca HideEntity this\anima_anda HideEntity this\anima_para HideEntity this\anima_morre Audio_PlayAtaca() EndIf this\anima = 2 anima = AnimTime(this\anima_ataca) anima = 15 Then Heroy_setvida(-10)

A nossa funo de IA se inicia com a obteno da entidade de fsica do nosso heri. Isso necessrio, pois o comportamento dos inimigos depende da distncia que eles esto dessa entidade.
Function Zumbi_IA() heroy = Heroy_Get3D()

If

Else

Aqui entra o papel da varivel de controle de animao. Veja que s rodamos a animao de ataque se ela ainda no estiver rodando, isso , se a animao atual for diferente de 2 ento a animao atual no a de ataque, assim devemos rodar essa animao:
If this\anima <> 2

MoveEntity this\fisico, 0, 0, -.4 If 1 ShowEntity HideEntity HideEntity HideEntity EndIf this\anima = 1 this\anima_anda this\anima_ataca this\anima_para this\anima_morre this\anima <> 1 Animate this\anima_anda, 1, .8,

Se voc no fizer isso a animao no funciona, pois reiniciaramos a animao a todo instante e isso faz com que ela nunca saia do frame 1, ficando parada. Veja abaixo o procedimento para dispara uma animao quando se tem vrios modelos 3d.
Animate this\anima_ataca, 1, .8 ShowEntity this\anima_ataca HideEntity this\anima_anda HideEntity this\anima_para HideEntity this\anima_morre Audio_PlayAtaca()

Se estamos caminhando, a entidade deve se mover, por isso usamos o comando MoveEntity aqui. Como voc se lembra, o centro lgico desse modelo est invertido, por isso ao invz de mandarmos ele andar para frente (valor positivo) mandamos ele andar para traz (valor negativo do eixo z). Vale lembrar que basta mover nossa esfera de fsica para que os modelos 3d e a cmera a sigam automaticamente, pois esses so filhos dela.
MoveEntity this\fisico, 0, 0, -.4

Primeiro disparamos a animao de ataque, depois mandamos que seja exibido o modelo 3d que possui essa animao com o comando ShowEntity. Por fim, devemos esconder todos os outros modelos que no esto sendo usados com HideEntity. No devemos nos esquecer de rodar o udio de ataque dos monstros para dar um efeito sonoro especial.
this\anima = 2 anima = AnimTime(this\anima_ataca) anima = 15 Then Heroy_setvida(-10)

A lgica a mesma do bloco anterior, uma animao deve ser disparada apenas uma nica vez. Assim, a animao de caminhar s ser disparada se ela j no estiver ativa, isso , se a animao atual no for a animao 1:
If this\anima <> 1

If

A parte abaixo do cdigo s roda se a distncia for maior ou igual a 70:


Else If this\vida > 0 this\anima <> 0 Animate this\anima_para, 1, .5 ShowEntity this\anima_para HideEntity this\anima_ataca HideEntity this\anima_anda HideEntity this\anima_morre EndIf this\anima = 0 End If If

Depois de ativada a animao, devemos configura a varivel de controle de animao para que ela indique que a animao atual a animao 2, a de ataque. Para tirar a vida do heri fizemos por um sistema lgico. Descobrimos qual o frame atual da animao com o comando AnimTime() e se o frame for o frame 15, ento tiramos a vida dele. Escolhemos esse frame por ser o momento da animao que o ataque est no seu alge.

A parte abaixo roda se a distcia for menor que 70 e superior a 5. Dessa forma disparamos os eventos e a animao de caminhar em direo ao heri.

A finalidade desse cdigo colocar as entidades em estado de parado. Quando a entidade ficar a distncia limite do heroi ela deve mudar para a animao de parado e ter essa animao visvel.

Abaixo a funo de lgica:

;Funo de Logica Function Zumbi_Logica() For this.Tzumbi = Each Tzumbi If this\vida < 1 this\anima <> 3 Animate this\anima_morre, 3, .6 ShowEntity this\anima_morre HideEntity this\anima_ataca HideEntity this\anima_para HideEntity this\anima_anda EndIf this\anima = 3 anima = AnimTime(this\anima_morre) If anima > 24 FreeEntity this\fisico Delete this EndIf EndIf Next End Function If

isso seja verdadeiro, devemos dar um fim na entidade:


anima = AnimTime(this\anima_morre) If anima > 24 FreeEntity this\fisico Delete this EndIf

Aqui um servio desse componente. Invocado pela funo de tiro do heri, deve descobrir se alguma das suas entidade levou chumbo. Caso seja verdadeiro, deve tirar um ponto de vida.
;Funo de deteco de tiros Function Zumbi_Tiro(ENTIDADE) For this.Tzumbi = Each Tzumbi If (ENTIDADE = this\fisico) Then this\vida = this\vida - 1 Next End Function

Por fim, finalmente, a descarregar as entidades:

funo

para

Se a vida da entidade for menor que 1 a entidade deve morrer:


If this\vida < 1

;Funo para descarregar objetos Function Zumbi_DesCarregar() For this.Tzumbi = Each Tzumbi FreeEntity this\fisico Delete this Next End Function

Para morrer, ela deve ativar a animao de morrer, mas s se isso ainda no foi feito:
If this\anima <> 3 Animate this\anima_morre, 3, .6 ShowEntity this\anima_morre HideEntity this\anima_ataca HideEntity this\anima_para HideEntity this\anima_anda EndIf

TURBINADO A APLICAO COM HIDE/SHOW Como voc percebeu, usamos por diversas vezes os comando HideEntity e ShowEntity no jogo apresentado. A importncia desse comando se deve ao fato que uma entidade que est oculta com o comando Hide no recebe processamento lgico e nem grfico; isso , ela no renderizada e nem processada fisicamente. Isso no ocorre com o comando EntityAlpha, que como voc viu, nossa esfera auxiliar de fsica possua propriedades lgicas e fsicas.

Devemos sempre informar que a anima ativa a animao morrer:


this\anima = 3

Agora devemos descobrir se a animao de morrer chegou ao seu final (frame 23). Caso

Enquanto uma entidade configurada com EntityAlpha processada tanto graficamente como fisicamente, as entidades definidas como HideEntity s existem na memria ram. Isso , so carregadas, mas no processadas. Como sabemos, acessar dados do disco rgido uma tarefa crtica, extremamente lenta, e deve ser evitado usar isso em demasia. Assim, carregar entidades de um jogo em tempo de execuo uma tarefa bem complicada. Alguns jogos criam portais para disfarar esse garregamento, assim, enquanto estamos passando por um portal, com aqueles efeitos visuais, por traz disso a aplicao est carregando entidades do disco rgido. Existem dois motivos pelos quais voc deveria considerar a possibilidade de querer carregar mdias em tempo de execuo: devido a necessidade de economizar memria ram em jogos muito grandes, ou por necessidade de economizar esforo de processamento com entidades no utilizadas. Se o seu problema memria ram, voc realmente vai ter que dar uma jeito de carregar esses dados a partir do disco rgido em tempo de execuo, mas de o problema economizar processamento, existem outras formas mais fceis de lidar com isso.

1 4 7

2 3 5 6 8 9

Se o heri estiver no setor 1, s sero visveis, no mximo, entidades dos seguintes setores:

(1) 2 4 5
Assim, 50% do cenrio no precisa ser processado e nem ser verificado, pois s precisamos exibir o setor atual e os adjacentes. Mas isso no seria verdadeiro se estivermos no setor 5, pois todos seriam prximos.

1 4 7

2 3 (5) 6 8 9

Assim, essa tcnica bem interessante para cenrios complexos, pois quanto maior o cenrio, mais divises sero feitas e maior ser a economia de processamento. Veja abaixo: a f k p u b g l q v c h m r x d i n s y e j o t w

Como Criar Cenrios Turbinados Pela minha esperincia prtica, as entidades com maior demanda de consumo de processamento so as seguintes por ordem: 1. Personagens (altssima) 2. Vegetao (alta) 3. Objetos (mdia) 4. Prdios (mdia) 5. Terreno (baixa) Assim sendo, fora raras excees, voc pode criar um nico terreno para seu jogo, mas dever deixar as demais entidades separadas, para serem tratadas individualmente, ou por grupos, ou por setores. Veja a tabela abaixo que representa o mapa de um jogo:

Personagem no setor a:

(a) f

b g

Personagem no setor m:

g h i l (m) n

q r

. Acima estamos salientando apenas a parte que sofreu alteraes. Veja que nas trs ltimas linhas alteramos os aquivos que sero includos; agora so as verses 2.0 dos arquivos mutante, zumbi e fantasma, que so optimizadas.

Para exercer esse controle por setores faa o seguinte procedimento: 1. Carregue todo o piso, ou terremo em apenas uma entidade (um modelo 3d em apenas 1 arquivo). 2. Carregue os prdios individualmente ou por setores (um arquivo por prdio ou por setor). 3. Carregue objetos e vegetao individualmente ou por setores. 4. Carregue personagens individualmente. 5. Os types para prdios, objetos, vegetao e presonagens devem possuir um campo chamado SETOR, sendo assim possvel controlar os mesmos por setores. 6. Crie uma funcionalidade na funo Controle que Exiba e Esconda as entidades conforme seus setores e conforme o setor atual do heroy.

ZUMBI.BB
Type Tzumbi Field fisico Field vida Field anima Field anima_para Field anima_anda Field anima_ataca Field anima_morre Field ativo End Type

Aqui adicionamos um campo para informar se a entidade esta visvel ou no:


Field ativo

Controle por distncia O modo mais prtico de exercer um controle sobre as entidades por meio da distncia delas em relao ao personagem principal. Abaixo demonstramos como fazer isso alterando o projeto Hell para ser turbinado por meio dessa tcnica. Passo 1: crie 4 novos arquivos: Game2.bb Zumbi2.bb Mutante2.bb Fantasma2.bb

Veja que esse campo sempre iniciar como ativo. Dessa forma, se estiver fora do campo de viso, poderemos esconder o mesmo.
Function Zumbi_Carregar() ;ZUMBI 1 inimigo.Tzumbi = New Tzumbi inimigo\vida = 5 inimigo\anima = 0 inimigo\ativo = 1

Agora a parte fucional do sistema: GAME2.BB


Include Include Include Include Include Include Include Include Include Include Include "Info.bb" "Menu.bb" "Jogar.bb" "Cenario.bb" "Porta.bb" "Item.bb" "Audio.bb" "Heroy.bb" "zumbi2.bb" "mutante2.bb" "fantasma2.bb" If DISTANCIA# > 130 ;Se estiver a mais que 130 e estiver ativo If this\ativo = 1 HideEntity this\anima_para this\ativo = 0 EndIf Else If this\ativo = 0 ShowEntity this\anima_para this\ativo = 1 EndIf EndIf

. .

Veja que o sistema consiste em verificar se a entidade est a mais de 130 de distncia e verificar se est ativa ou no. Se estiver a mais que 130 e est ativa, ento temos que dar um Hide na entidade. Se estiver a menos que 131 (Else) e ainda est inativa (this\ativo=0), devemos dar um Show na entidade.

em rede, engine isomtrica, arquiteturas multiplay, TCP/IP, UDP, etc. Acredite! Tudo isso bem fcil e est ao seu alcance. Com sua fora de vontade e nossos cursos, no haver limites para voc!

Wanderlei Roberto Marchesi

PROLOGO Chegamos ao fim desse curso. Esperamos sinceramente que tenha sido muito proveitoso para voc. Sei que esse nosso ltimo captulo pode ter sido um tanto o quanto indigesto logo de cara, mas, a longo prazo, ele vai ser uma tima referncia para voc. Poderia ter escolhido algo bem mais simples para fechar esse curso, mas algo simples traria muito pouco proveito. Como voc pode ver, fizemos esse material de forma mais democrtica possvel, possibilitando a qualquer pessoa ter acesso a esses conhecimentos, afinal partimos do nvel zero (lgica de programao) e chegamos bem longe. Nossa escolha em portar esse material para toda e qualquer pessoa interessada em entrar para o ramo de desenvolvimento de jogos nos levou a criar um contedo bastante grande e completo. Assim, seria impossvel abordar todos os contedos envolvidos em apenas um nico curso sem deixar o mesmo cansativo demais. Decidimos ento dividir tudo em 2 cursos: um iniciante/intermedirio e outro avanado. Voc acaba de cursar o nvel iniciante/intermedirio. Fique atento ao lanamento do nosso curso de nvel avanado, pois agora que voc j conhece o bsico sobre desenvolvimento de jogos, vamos poder trabalhar com conceitos muito mais avanado, como, por exemplo, criao de dll, integrao com bibliotecas externas, engines de fsica, programao web, jogos