Na segunda aula do curso a idéia é começar a colocar interatividade nos objetos do game. Mas colocar interatividade nesses elementos a partir do código gerado na primeira aula, seria o fim do mundo, porque ficaria uma ‘zona’ o nosso código; Então decidi colocar tudo bonitinho em classes e seus objetos
…sim seria possível colocar tal interatividade sem uso de objetos, mas além de ficar uma ‘zona’, eu pergunto, não parece natural criar objetos para um jogo, uma vez que em um jogo, os objetos possuem suas ações? Para mim, a resposta é sim!
Um cara chamado Mark Guzdial, escreveu o seguinte (sobre POO): “Objetos possuem duas coisas: Objetos sabem coisas; Objetos podem fazer coisas”, isso me parece cair como uma luva para jogos
Dessa forma começaremos a organizar nosso código como classes para nossos objetos do jogo.
Iniciantes: (caso você conheça orientação a objetos pule este trecho)
Você iniciante, que pode estar com medo de POO, digo que não se preocupe, a coisa não é tão feia, na verdade é muito mais simples do que se pode imaginar, e para ajudar você eu não usarei termos técnicos que não sejam extremamente necessários, e quando necessários farei de forma que vc os ache legal… espero
Para você entender isso melhor é simples, basta pensar naturalmente. Por exemplo, um cachorro late quando houve um barulho incomum, certo!? Então, para transformar isso em programação, nós temos que criar um tipo de objeto que ao ‘ouvir’ um evento incomum execute uma ação de latir. E essas ações nós iremos escrever em uma ‘classe’ que vai conter todas essas ‘instruções’, e só depois quando criamos o objeto é que elas irão acontecer… lembre-se disso e observe o que faremos adiante.
Mão na massa: (todos…
)
Nós podemos começar com base no código da primeira aula. Que está assim:
arena = loader.loadModel(‘../res/eggs/plataformaBase’)
arena.setScale(4)
arena.reparentTo(render)
arena.setPos(20,30,5)
Nossa idéia é transformar os elementos do jogo em objetos, certo? Bem, a arena é um elemento importantíssimo do jogo, então vamos transformá-la em um objeto, para tal, teremos que criar uma classe, que terá apenas uma função, a de carregar a nossa arena no ‘render’ do Panda, e definir algumas propriedades. Vamos lá:
class Arena
():
def __init__(self):
self.
carregaModelo()
def carregaModelo(self):
self.arena = loader.loadModel(‘../res/eggs/plataformaBase’)
self.arena.setScale(4)
self.arena.reparentTo(render)
self.arena.setPos(20,30,5)
Pronto! Já temos a nossa classe pronta. Mas vamos dar uma olhada no que fizemos:
1- Primeiro criamos a classe com nome Arena
2- Definimos a iniciação da classe (ou também chamado método construtor), ele é sempre obrigatório quando trabalhamos com classes:
3- No __init__ nós chamamos a função que irá fazer o trabalho de carregar os modelos 3D e setar as propriedade que queremos:
4- Criamos a função que fará o trabalho:
5- O conteúdo da função você já conhece, com a exceção do uso do ’self’:
self.arena = loader.loadModel(‘../res/eggs/plataformaBase’)
self.arena.setScale(4)
self.arena.reparentTo(render)
self.arena.setPos(20,30,5)
O ’self’ é sempre algo que permite muitas conversas e debates, mas de forma sucinta: o ’self’ serve para que o objeto, busque as informações relativas, nele mesmo.
Agora resta instanciar o objeto, ou seja, criar o objeto:
e então:
obs.: Não esqueça dos ‘imports’, são os mesmos da primeira aula!
Execute o programa, e você terá a arena na tela.

Ainda na classe Arena, nós podemos carregar o papel de parede, veja só:
class Arena
(DirectObject
):
def __init__(self):
self.
carregaModelo()
def carregaModelo(self):
self.arena = loader.loadModel(‘../res/eggs/plataformaBase’)
self.arena.setScale(4)
self.arena.reparentTo(render)
self.arena.setPos(20,30,5)
self.parede = loader.loadModel(‘../res/eggs/papelParede’)
self.parede.reparentTo(render)
self.parede.setScale(1)
self.parede.setPos(-5, 60, -2)
Isso vai colocar um céu no nosso jogo
Agora vamos pensar no objeto Avatar. Para esse pequeno demo, o avatar vai ter poucas ações mas o suficiente para aprender coisas importantes, eu montei uma relação de ações e propriedades (coisas que ele vai fazer, e coisas que ele vai saber) para esse avatar; Embora não vamos fazer tudo ao mesmo tempo, é bom termos uma panorâmica do todo:
| Ações |
X |
Propriedades |
| - se move para direita e esquerda
- pula
- colide com coisas(plataforma, Energia, obstáculos)
- sensível aos eventos de teclado
|
|
- saúde
- velocidade
- altura do pulo |
Além é claro de coisas básicas que são invisíveis ao jogador, tipo, carregar o próprio modelo. Então vamos começar por coisas básicas… vamos fazer nossa classe para o futuro ‘objetoAvatar’, começando por fazê-la carregar o modelo 3D:
class Avatar
(DirectObject
):
def __init__(self):
”‘Metodo construtor da classe Avatar.
- Define os estados do Avatar: self.estados
‘”
self.
estados =
{‘direita’:
0,
‘esquerda’:
0,
‘pulo’:
0}
self.carregaAtor()
def carregaAtor(self):
”‘Carrega o modelo personagem.egg no NodePath ‘persona‘
‘”
self.persona = render.attachNewNode(‘persona’)
self.personaActor = Actor(‘../res/eggs/personagem.egg’,
{‘idle’:‘../res/eggs/personagem-parado’,
‘run’ :‘../res/eggs/personagem-correr’,
‘jump’:‘../res/eggs/personagem-pular’}
)
self.personaActor.setScale(.3)
self.personaActor.loop(‘idle’)
self.personaActor.reparentTo(self.persona)
self.personaActor.setPos(0,0,0)
self.persona.setPos(0,30,0)
self.persona.setH(90)
Resumindo, 1: criamos uma classe chamada Avatar; 2: criamos um método de inicialização, onde chamamos a função que carrega o nosso modelo 3D; 3: criamos a função que carrega o modelo 3D.
Se você reparou bem na função carregaAtor(), reparou uma pequena mudança no procedimento que fizemos na aula 1. Na aula 1, nós criamos um nó chamado ‘persona’ ao grafo de cena (ligado ao render), e nesse nó estava no nosso modelo 3D, vejo o esquema abaixo:

Mas agora a coisa mudou um pouquinho, criamos um nó chamado ‘persona’ ao render, e ao nó persona criamos um nó ‘personaActor’, que esse sim guarda o modelo 3D. Isso irá nos permitir maior flexibilidade no futuro, e uma estrutura mais organizada. Ah, aquela variável self.estados, que na verdade é um dicionário, servirá para fazermos nosso avatar se mover.
Outra pequena mudança: class Avatar(DirectObject): reparou nisso? o que é esse DirectObject? Bem esse DirectObject é o que permitirá nosso avatar receber os inputs de teclado, e outros eventos!
mas que por hora não está funcionando… mas, lembre-se dele.
Agora é criar o objeto baseado na classe Avatar, e testar; Então após o término da classe, e antes do comando run():

Eventos e Tarefas (Task)
Agora vamos trabalhar muito na classe Avatar, implementando as funcionalidades principais.
A primeira das funcionalidades serão os inputs de teclado, e movimentação, e para tornar isso possível iremos falar sobre duas coisas super importantes no Panda3D, os Eventos e as tarefas (Tasks).
Eventos podem ser entendidos como mensagens que ‘circulam’ pelo sistema, e alguns objetos, ao receberem esses eventos podem reagir a ele, com alguma ação. Esses eventos podem ser gerados de diversas formas, via input de teclado, colisões, ou serem criados ao gosto do desenvolvedor.
Para o Panda3D, os inputs de teclado serão sempre eventos, sempre! É um padrão do sistema, sempre que uma tecla é pressionada gera um evento, basta que um objeto esteja apto a tratar esse evento. É exatamente o que temos que fazer para, por exemplo, tornar possível a movimentação do nosso personagem. Está lembrado do DirectObject? class Avatar(DirectObject): lembra que isso é o que permitirá a uma classe tratar eventos.
Tarefas por sua vez são funções que serão executadas todos os frames. Por exemplo, se um elemento do jogo tem que se mover constantemente, deve haver uma função que faça com que ele se mova, e tal função deve ser definida como tarefa, para que ela executada a cada frame.
A lógica da movimentação do nosso avatar será feita em duas partes, primeira: haverá uma tarefa que irá mover nosso personagem, dependendo dos valores do dicionário self.estados; segunda: faremos com que a classe Avatar ao receber um evento das teclas direcionais do teclado, modifique os valores do dicionário self.estados.

Para colocar isso em prática temos que criar algumas funções, uma para capturar os eventos, uma para alterar o self.estados, uma para mover o avatar:
class Avatar
(DirectObject
):
def __init__(self):
”‘Metodo construtor da classe Avatar.
- Define os estados do Avatar: self.estados
‘”
self.estados = {‘direita’:0, ‘esquerda’:0, ‘pulo’:0}
self.carregaAtor()
self.capturaEventos()
taskMgr.add( self.mover, ‘moverPersona’ )
def carregaAtor(self):
”‘Carrega o modelo personagem.egg no NodePath ‘persona‘
‘”
self.persona = render.attachNewNode(‘persona’)
self.personaActor = Actor(‘../res/eggs/personagem.egg’,
{‘idle’:‘../res/eggs/personagem-parado’,
‘run’ :‘../res/eggs/personagem-correr’,
‘jump’:‘../res/eggs/personagem-pular’}
)
self.personaActor.setScale(.3)
self.personaActor.loop(‘idle’)
self.personaActor.reparentTo(self.persona)
self.personaActor.setPos(0,0,0)
self.persona.setPos(0,30,0)
self.persona.setH(90)
def capturaEventos(self):
”‘Tratamento de eventos INPUT
arrow_right = direita
arrow_left = esquerda
‘”
self.accept( ‘arrow_right’, self.alteraEstado, [‘direita’,1] )
self.accept( ‘arrow_left’, self.alteraEstado, [‘esquerda’,1] )
self.accept( ‘arrow_right-up’, self.alteraEstado, [‘direita’,0] )
self.accept( ‘arrow_left-up’, self.alteraEstado, [‘esquerda’,0] )
def alteraEstado(self, chave, valor):
”‘Modifica os estados de acao, de acordo com os valores recebidos
acao ->; estados: direita, esquerda
valor ->; valor que sera atribuido a acao
‘”
self.estados[chave] = valor
def mover(self, task):
”‘tarefa responsavel pela movimentacao do personagem
‘”
#DIREITA
if (self.estados[‘direita’] != 0):
self.persona.setX( self.persona.getX()+0.1 )
#ESQUERDA
if (self.estados[‘esquerda’] !=0):
self.persona.setX( self.persona.getX()-0.1 )
return Task.cont
Vamos aos detalhes. Primeiro vamos observar aquela primeira parte da lógica, onde uma função (tipo task) fará nosso personagem se mover de acordo com valores do ‘estados’. Essa é a função mover()! Observe a chamada dessa função lá no __init__:
taskMgr.add( self.mover, ‘moverPersona’ )
O taskMgr, é um objeto ‘global’ no sistema Panda3D, que irá gerenciar todas as tarefas do jogo, afinal cada elemento do jogo pode ter sua tarefa… o que nós fazemos aqui foi dizer a esse gerenciador (taskMgr) para executar a função self.mover (do avatar) todos os frames. Agora na função mover(), propriamente dita, repare que ela está recebendo uma variável task -> def mover(self, task): <- ISSO É OBRIGATÓRIO! Sempre que uma função seja definida como tarefa, ela deve receber a variável de tarefa (no caso task), isso se explica, por exemplo na última linha da função -> return Task.cont <- isso quer dizer que essa tarefa deve ser novamente executada no próximo frame (significa: tarefa.continue :) ). Sim, a execução dessas tarefas podem ser largamente personalizadas, dê uma olhada aqui.
Bem, a lógica interna da função mover(), está bem simples. Os valores do self.estado, começam todos com 0(zero), e dentro da função se o estado direita for diferente de 0(zero), ele altera a coordenada X em 0.1 positivo, o que dará a sensação do personagem se mover para a direita. E logo depois a mesma lógica é aplicada para o mover para a esquerda.
Agora a segunda parte da lógica, onde o evento do teclado será capturado, e acionará uma alteração nos valores do dicionário. A captura dos eventos, é feita na função capturaEventos() … a estrutura para isso é muito simples, é o comando accept():
self.accept( ‘nome_do_evento’, funçao_que_trata_Evento, [parametros] )
No caso selecionamos os eventos ‘arrow_left’, ‘arrow_right’ que são disparados quando as teclas são pressionadas, e os eventos ‘arrow_left-up’ e ‘arrow_right-up’ que acontecem quando as mesmas teclas são ’soltas’.
self.accept( ‘arrow_right’, self.alteraEstado, [‘direita’, 1] )
Então o que fizemos foi, quando ocorrem o evento ‘arrow_right’, disparamos a função alteraEstado() para trabalhar com os parâmetros ‘direita’, e 1.
A função alteraEstado(), apenas tem o trabalho de alterar os valores do dicionário (ou…), recebendo a informação de qual chave alterar, e o valor a ser atualizado, só.
Simples não!? Agora você pode testar o programa e ver ele funcionar com o nosso avatar se movendo para os lados.
Na próxima aula vamos falar de colisões, e de como tratar os eventos gerados por elas.
Aqui está o arquivo final desta aula.
Games, Panda3D
Games, Panda3D, tutorial