Arquivos tutorial - Paulo Barbeiro
Curso Blender Game Engine – aula 6
Curso sobre Blender Game Engine.
O principal tema abordado nessa aula é a máquina de estados para o game engine. Outros temas abordados são:
– Sensor Near
– Mudanças de state
– Actuator State
Publicado em 1 de novembro de 2011 21 de julho de 2016 Categorias TutorialTags Blender, Game Engine, gamedev, python, tutorialDeixe um comentário em Curso Blender Game Engine – aula 6
Agora precisamos definir colisões para nosso personagem!
Colisões entre objetos em jogos, são um ponto absurdamente importante. Importante por se tratar de algo fundamental para a mecânica do jogo, e também por ser um tópico delicado em termos computacionais. E esse fator técnico nos obriga a algumas reflexões.
Calcular colisões entre objetos, com eficiência, envolve uma série de algoritmos complexos – os quais não vou entrar em detalhes – e que consomem um processamento considerável do computador, e para permitir que essas colisões sejam possíveis em tempo real, é necessário realizar algumas simplificações na descrição dos nossos elementos. Por exemplo: Se temos um modelo de uma cabeça, colisões não serão calculadas para cada depressão ou volume facial, mas simplificamos o formato da cabeça para uma esfera, ou um tubo, uma forma geométrica mais simples. Dessa forma as colisões conseguem ser calculadas de forma a não travar o bom desempenho da renderização.
Essas formas geométricas simples (esferas, cubos, tubos) são tratadas nas game engines como sólidos de colisão, pois serão esses objetos usados para cálculos de colisão.
Cada game engine possui seus próprios sólidos de colisão, e precisamos usá-los para criar uma descrição simplificada de nossos objetos.
No caso de nosso avatar, fiz a escolha de utilizar uma séria de esferas para suas colisões. Veja abaixo:
Porém, essa imagem acima é meramente ilustrativa, no nosso caso, não precisaremos de tanta definição, e vamos colocar esses sólidos de colisão na medida que forem necessários, começaremos apenas com uma esfera aos pés do avatar, para detectar colisões com a plataforma.
Criar um sólido de colisão, não é tudo. É necessário configurar os cálculos de colisões. No Panda3D, temos que adicionar outros dois objetos, um "Collision Traverser", e um "collision handler". Vamos adicionar esses objetos todos, e nos preocupar com os problemas depois 🙂 Sim teremos problemas, mas todos solucionáveis. Primeiro vamos criar o "Collision Traverser" e o "collision handler", no arquivo Panda-tut-00.py:
– logo no inicio da função __init__(self), temo a criação dos elementos traverser e do base.pusher, que são respectivamente, o "Collision traverser" e o "collision handler"
– logo abaixo da construção do Avatar, temos a linha: avatar.setCollision(traverser). Aqui é o ponto onde criamos no avatar as configurações necessárias para que o mesmo colida com outros objetos.
Vamos a classe Avatar:
O que há de novo nessa classe:
– logo nas primeiras linhas: from pandac.PandaModules import * . Nesta linha importamos alguns módulos necessários para realizar as configurações de colisão.
– abaixo da função __init__ e antes da função movement, inserimos a função setCollision.
– na função setCollision criamos dois sub nós ao no persona.
Após essas alterações será fácil perceber que o o avatar irá de deslocar de maneira estranha pela plataforma, porém ficará claro que ele está "esbarrando" na plataforma. Esse é o comportamento normal do Collision Handler Pusher. Ele impede que os objetos atravessem uns aos outros.
Outro detalhe importantíssimo! ESPECIALMENTE QUANDO VOCÊ FOR UTILIZAR SEUS PRÓPRIOS MODELOS 3D!!!
Até aqui, foi configurado os objetos traverser e handler, e também a configuração de colisão para o avatar. NÃO FOI FEITA NENHUMA CONFIGURAÇÃO DE COLISÃO PARA A PLATAFORMA!!
Na verdade verdadeira, o arquivo da plataforma já está configurado para colisões, por isso não foi necessário fazê-lo. Vou explicar como foi feito, para que este tutorial não fique incompleto, e que quando você for utilizar seus próprios modelos 3D, possa fazer o mesmo.
Para a plataforma, poder-se-ia (rsrsrsrs) usar sólidos de colisão, mas optei por usar Collision Mesh. Collision Mesh, é utilizar o próprio modelo 3D como sólido de colisão. A limitação é: Seu modelo deve ser simples, e não côncavo!
Quando você converte seus modelos 3D para o formato do Panda3D( .egg), o arquivo final, é essencialmente um arquivo de texto, que você pode abrir em qualquer editor de texto simples, e modificá-lo. Para fazer com que um modelo, simples e não côncavo, seja sensível à colisão, precisamos adicionar uma linha a cada "group": <Collide>{Polyset keep descend}
Os arquivos .egg são um texto estruturado tal como um XML. Inicialmente, no .egg, é configurado os materiais existentes na geometria. Então iniciam-se os grupos. Um "Group", é iniciado com sua matriz (informação de posicionamento, e rotação); Logo após, inicia-se uma lista (VertexPool) com informações de todos os vértices que compõe aquela geometria; Finalmente, temos uma lista com informações dos polígonos que compõe a geometria.
Logo, a linha <Collide>{Polyset keep descend}, deve ser colocada no inicio de cada Group. Abra o arquivo plataforma.egg e veja como ele funciona.
Antes da implementação, vamos entender do que se trata esse tal de CollisionHandlerPusher, e como as colisões serão encaixadas no nosso jogo.
O CollisionHandlerPusher, é um "tratador" de colisões no Panda3D, que não permite que os objetos atravessem uns os outros. Ele desloca o objeto que está se movendo para trás, ou para o lado, desviando-o da intersecção dos objetos.
Outra possibilidade do Collision Handler Pusher, é gerar eventos! Ou seja, quando objetos colidem, ele pode gerar eventos notificando o sistema da colisão. Essa é uma funcionalidade do Collision Handler Event, donde o Collision Handler Pusher é derivado. Logo ele herda essa funcionalidade do Handler Event. A geração de eventos do Collision Handler Pusher, é habilitada nas linhas 20 e 21, do arquivo Panda-tut-00.py.
Implementação —————
Agora que temos nossa colisão básica configurada, falta trabalhar nos detalhes de implementação.
A primeira colisão que temos nessa cena, é quando o avatar cai na plataforma. Até o presente momento, nós fazemos esse cálculo baseado em uma posição no eixo Z. Mas para deixar nosso jogo mais correto, vamos fazer isso com colisão, entre a esfera e a plataforma.
Antes de realizar a implementação
Para começar, vamos apagar as linhas 49 e 50:
A partir de agora o que irá mudar o state['flying'] para False, será a colisão entre a esfera e a plataforma. Vamos criar um novo accept no nosso avatar, para capturar o evento gerado pela colisão ('ball0CN-into-plataforma'):
Nesta linha acima, definimos que a colisão entre a esfera e a plataforma, será tratada pela mesma função self.changeState. Observe com detalhe o nome do evento: 'ball0CN-into-plataforma'. Observe a linha 20 do Panda-tut-00.py, base.pusher.addInPattern("%fn-into-%in").
Quando configuramos a geração de eventos pelo pusher, e usamos essa combinação "%fn-into-%in", o pusher irá gerar eventos com os nomes dos colliders, no nosso caso, a "bolla0CN", e a "plataforma".
Porém temos um problema. Toda função que trata uma colisão, obrigatoriamente, tem que receber as informações da colisão. Essa informação é transmitida à função como um parâmetro, logo a função tem que estar preparada para recebê-lo. O que não acontece com nossa função self.changeState. Bem, você pode pensar que: "ah… blz, vamos colocar uma variável ali nos argumentos da função, que fica tudo certo!". Não… Isso não é tão simples… e como fica a chamada da função nos eventos de teclado, que não enviam um terceiro parâmetro? Esqueceu disso? Se uma função for chamada, com mais ou menos parâmetros os quais ela possui, acontece um erro, e o programa é encerrado.
Felizmente, tem uma manhã em python para lidar com isso. Inserimos um parâmetro 'col' (diminutivo de 'colisão' rsrsrs) e dizemos que ele é igual a 'None' (nulo em python), caso a função não receba esse argumento!! Engenhoso não? Veja:
Ao final desse processo, teremos nosso avatar, parando de cair ao tocar a plataforma, porém ainda teremos um problema, veja que ele parece patinar sobre a plataforma! Isso acontece pq o pusher quer repelir o avatar da plataforma! Podemos solucionar esse problema, forçando o personagem na posição X e Y que queremos! Veja a solução no código completo abaixo:
Basicamente, na função movement, checamos se a posição Y do avatar não for igual a 10, então ela é corrigida para 10. Isso resolve a "patinação" do avatar para frente e para tráz. Mas ele ainda patina para os lados. Para resolver isso, criamos uma variável chamada lastXpos. Essa variável irá fazer o trabalho de posicionar o avatar sempre no ponto X correto, e não permitir de ele patine para os lados. Observe na função movement, que quando o avatar estiver tocando na plataforma, e não estiver se movendo para a direita ou esquerda, ele será posicionado sempre na posição lastXpos. Essa variável tem seu valor atualizado, sempre que o avatar para de se mover: Veja na função self.changeState, sempre que o 'value' for igual a zero, atualizamos a variável lastXpos.
Publicado em 28 de março de 2011 2 de agosto de 2016 Categorias TutorialTags desenvolvimento, Game development, Game Engine, gamedev, Panda3D, python, tutorial4 comentários em Panda3D gamedev tutorial 3
Neste segundo tutorial criaremos nosso avatar, e habilitaremos algumas funcionalidades de interação do Panda3D: tasks e inputs de usuário.
Em termos de código python, veremos como trabalhar com módulos, condicionais e dicionários.
Porque precisamos desses módulos python? Bem, nosso código começará a ganhar tamanho, e manter nossas classes em arquivos diferentes, é altamente recomendável para facilitar nosso desenvolvimento. Essa coisa de módulos é super simples: basta criar um novo arquivo de código na nossa pasta Panda-project/src e nameá-lo avatar.py, e teremos nele o código abaixo:
Vejamos as coisas novas:
::> Essa é a classe do Panda3D que lida com modelos "riggados". Que é o caso do nosso modelo.
::> Aqui um novo 'nó' é criado, que irá abrigar o 'nó' para o modelo 3D. Precisaremos fazer isso, porque teremos outros nós a serem criados, e logo, devemos manter esses nós organizados.
::> Nessa linha criamos o Actor, baseados no arquivo do personagem.egg.
::> Nessas linhas informamos ao Actor, quais animações queremos no nosso personagem. O que vemos aqui é um dicionário python. Podemos notar que 'idle' é a chave, para o valor './assets/eggs/personagem-parado', ou seja a animação 'idle', será a animação do arquivo "personagem-parado.egg". Esse pequeno processo se repete para todas as animações que quisermos.
::> Este comando rotaciona o nó persona. Tente comentar essa linha e veja o que vai acontecer.
Para facilitar a compreenção do que foi feito, veja no gráfico abaixo, como que está organizado esse node relativo ao Avatar:
No diagrama acima, temos um esquema ilustrando que o nó do personaActor, nó que guarda nossa geometria, está linkado ao nó persona. E está posicionado na posição (0,0,4.6) relativo ao nó persona, ou seja, levemente acima.
Neste próximo diagrama, temos a demonstração de como vai funcionar nosso grafo a partir de agora:
Agora, temos a segunda parte… importar esse módulo no arquivo principal Panda-tut-00.py, onde precisamos importar a classe Avatar do módulo avatar:
#classe para o jogo
#carregar o modelo da plataforma
self.parede = loader.loadModel('../assets/eggs/papelParede')
self.parede.reparentTo(render)
self.parede.setScale(1)
self.parede.setPos(-5, 30, -2)
Ao final teremos algo como essa imagem ao lado.
De agora em diante, todos os comandos relativos ao Avatar serão escritos nesse arquivo Avatar.py. Vamos iniciar com uma lógica bem simples, imperfeita para nossas finalidades, mas boa para introduzir quem esta começando. Vamos fazer com que nosso personagem caia até a plataforma, e então possa se deslocar lateralmente sobre a mesma.
Façamos nesta ordem:
– avatar cai, se não estiver tocando a plataforma;
– quando tocar a plataforma para de cair;
– quando estiver na plataforma, ele pode se deslocar para os lados;
– quando o usuário pressionar as setas para direita ou esquerda, o avatar se desloca;
Para essa pequena lógica, vamos criar uma função que analise os 'estados' do avatar, para então decidir se ele deve cair, ou não; se deve ir para os lados, ou não. E vamos criar os estados também!
E teremos algo novo também, uma tarefa! Tarefa é uma nomenclatura usada no Panda3D, que se refere: A necessidade de prover a certos objetos de jogo, loops de execução. Isso nos permite criar uma ação que se repetirá a cada frame do jogo! Veja maiores detalhes aqui.
O código do módulo avatar ficará assim:
1 – um dicionário de estados: self.state = {'flying':True, 'left':False, 'right':False}
::> Ele guardará as informações se o avatar esta voando ('flying'), ou seja não está tocando a plataforma; E ainda se o avatar esta se deslocando para a esquerda('left') ou direita('right'). O estado 'flying' é o único marcado como True, porque assim que o jogo começa, nosso personagem está no alto.
2 – Temos um gerenciador de tarefas: taskMgr.add(self.movement, "Avatar Movement")
::> Nesse gerenciador, definimos a função self.movement, como a função que será executada a cada quadro, e nomeamos essa tarefa como "Avatar Movement".
3 – Uma nova função: def movement(self, task):
::> Essa é a função que será executada pelo gerenciador de tarefas a cada frame do jogo.
Nessa função, pelo fato dela ser chamada pelo gerenciador de tarefas, obrigatoriamente ela deve receber o argumento 'task', e retornar uma constante de Task, no caso Task.cont, para continuar a tarefa no próximo frame.
Também temos uma pequena lógica na função. Ela avalia se o estado 'flying' é verdadeiro, e se for, o posicionamento do avatar no eixo Z será modificado, reduzindo o valor, e a ilusão com isso, será que o avatar está caindo.
Até o dado momento, o avatar será "jogado para baixo" infinitamente. Vamos fazer com que ele para quando sua altura for igual a altura da plataforma, altera a função movement dessa forma:
if(self.persona.getZ() < -2.25):
Nessa alteração, modificamos a função de forma que assim que o nodepath persona, chegar na altura da plataforma, o estado 'flying' se torna Falso ('False'), e então, o avatar para de cair.
Aqui temos um exemplo bem simples de como construir uma lógica com base em valores, inteiros e boleanos. Em breve modificaremos esse código para solucionar um problema que veremos em breve.
Inputs de Usuário ————
Vamos adicionar a possibilidade do usuário pressionar as tecla direcionais e o avatar se deslocar pela tela, para a direita e esquerda. Isso é o que chamamos de Inputs de usuário, para outros detalhes clique aqui.
Inputs de usuários são eventos dentro do Panda3D. Para capturar esses eventos, devemos:
– declarar quais objetos devem ser notificados de certos eventos: accept()
– definir funções para executar ações;
veja abaixo as alterações no módulo avatar, discutiremos as alterações posteriormente:
As novidades ficam após a linha 34.
Nas linhas 35, 36, 37 e 38, notificamos ao Panda3D, que nosso avatar deve ser notificado quando as teclas direcionais direita e esquerda forem pressionadas e soltas. Os eventos 'arrow_left' e 'arrow_right' acontecem quando o usuário pressiona as teclas. Os eventos 'arrow_left-up', e 'arrow_right-up', acontecem quando o usuário solta as teclas.
Posteriormente, dizemos que a função self.changeState é a função que será executada, a cada vez que nosso objeto avatar for notificado dos eventos. Para finalizar esses "accepts", passamos as listas ['left',True], ['left',False], ['right',True] e ['right',False]. Essas listas serão utilizadas na função self.changeState, como parâmetros.
A função changeState, possui uma tarefa muito simples: mudar o estado do nosso avatar.
Ela está preparada com dois argumentos, key e value, que se referem aos estados no dicionário self.state, veja, self.state[key], ou seja o argumento 'key', deverá obrigatoriamente ser uma string (palavra) 'left' ou 'right', e os valores que serão definidos para o estado será sempre True ou False. Repare que os argumentos 'key' e 'value', enviados para a função, são aquelas listas ['left', True] e ['right',False]!!
Para finalizar essa implementação, e de fato fazer o avatar se deslocar pela tela, colocamos na função movement, uma lógica que observa se os estados 'left' e 'right' são verdadeiros para então deslocar o personagem pelo eixo X.
Sim!! Temos uma interatividade! Interatividade muito tosca… mas já é alguma coisa. Agora quando nosso avatar chega na plataforma, ele pode se deslocar!
Colisões ———-
No próximo post vamos melhorar significamente nosso código!
Se você reparar temos um grande problema: nosso avatar passa direto pelos obstáculos! Temos que implementar colisões!
Precisamos implementar uma colisão com a plataforma e com os obstáculos. Para isso usaremos algumas técnicas.
AVISO: No próximo post nosso código ganhará um pouco de complexidade!
Publicado em 23 de março de 2011 21 de julho de 2016 Categorias TutorialTags desenvolvimento, Game development, Game Engine, gamedev, Panda3D, python, tutorial2 comentários em Panda3D gamedev tutorial 2
Este é o primeiro post de uma série falando sobre desenvolvimento de jogos com Panda3D (versão 1.6).
Minha idéia é uma introdução sobre desenvolvimento de jogos, e eu escolhi o Panda3D como game engine, porque é um ótimo software, livre e multi-plataforma.
Antes de começar a seguir esses tutoriais, você precisa baixar uns arquivos, e seguir umas instruções para organizar as pastas dos arquivos. Acesse aqui.
O objetivo desse tutorial é fazer um jogo super simples: Uma garota, em um cenário futurista, tem que passar por umas barreiras e armadilhas.
Bueno… espero que vc já tenha o Panda3D 1.6.2 instalado, caso contrário, acesse www.panda3d.org e instale-o.
… vamos começar.
Para começar a escrever código do Panda3D, você precisa de um editor de texto simples, Microsoft Word ou Open Office Writer não servem!
Caso você trabalhe com Linux, Gedit é muito bom!
Caso você trabalhe com Mac, Xcode até dá… mas não é o melhor…
Caso você trabalhe com Windows, tente o Notepad++
Mas para todos eu sugiro o Eclipse com plugin de python (você pode usar o Eclipse for PHP), ele é um pouco difícil de configurar, mas vale o esforço.
Com tudo pronto, vamos digitar um código. Comece um novo arquivo, nomeie-o como Panda-tut-00.py, e vamos colocar o seguinte código:
Esse é o código mais simples que podemos fazer e ter o Panda3D funcionando!
A primeira linha importa a classe DirectStart, que define algumas configurações e inicia o sistema do Panda3D.
A linha run(), basicamente inicia o 'main loop' do Panda3D. Todo jogo precisa de um 'main loop'. O main loop, é a mágica que nos fornece o "Frame"! Graças aos "frame por segundo", podemos mudar o posicionamento dos objetos do jogo, durante o tempo, e assim a mágica acontece.
Para executar esse código, você precisa:
Caso você trabalhe com Linux (você já sabe oq fazer, eu sei…), navegue pelo Terminal até a pasta Panda-project/src, e execute: python Panda-tut-00.py
Caso você trabalhe com Mac (eu acho que você sabe oq fazer…), navegue pelo Terminal até a pasta Panda-project/src, e execute: python Panda-tut-00.py
Caso você trabalhe com Windows (ohh… Deus, que o Senhor tenha piedade de sua alma!)… Você precisará lembrar como navegar pelas pastas do seu sistema pelo Prompt de Comando, usando o comando 'cd'. Para abrir o prompt de commando, vá ao menu iniciar, e na pesquisa digite "command", e então navegue até a pasta Panda-project/src, e execute: python Panda-tut-00.py
Esse é o processo de como rodar as todos os códigos feitos para o Panda3D, decore!
Ok… vamos começar de verdade!
Vamos a um código de verdade, o anterior foi só para preparar alguma base. Vamos carregar para o jogo uma arena, e um plano de fundo:
Ao rodar o programa você verá algo assim:
LEMBRANDO: Isto é um código para computadores, nenhum erro de digitação é tolerado. Uma única letra errada, causará um erro. Outro erro bem comum é quando o Panda3D não encontra os arquivos que estamos carregando para o jogo, isso geralmente acontece se vc digitar o endereço dos arquivos errado, ou se não organizou os arquivos de forma correta… dá uma lida aqui.
1 – Tá vendo aquele número no canto superior direito da tela? São nossos frames por segundo! essas duas primeiras linhas são responsáveis por ele aparecer alí:
2 – Agora pedimos ao Panda3D, algumas das ferramentas que ele tem disponível para trabalhar. O Panda3D, é um conjunto de muitas ferramentas para desenvolvimento de games, e não precisamos de todas a todo momento, então pedimos essas ferramentas na medida que as usamos:
3 – Como somos pessoas do bem, criaremos nosso jogo com "programação orientada a objetos", isso quer dizer que cada objeto de nosso jogo será criado com um conjunto de código separado dos outros. E para começar, nossa própria lógica do jogo será um objeto em sí, e escreveremos uma classe para ela:
self.platforma = loader.loadModel("../assets/eggs/plataformaBase")
self.platforma.reparentTo(render)
self.platforma.setPos(0,10,0)
4 – Finalmente, instanciamos nossa classe do jogo, e executamos o main loop:
Nesse momento vamos falar sobre o que esta acontecendo dentro da classe Game. A classe Game é inicializada na função def __init__(self):
Esse função é executada uma única vez quando o objeto é instanciado(trocando em miúdos, quando ele é criado), e nessa função nós mandamos o Panda3D configurar alguns elementos do jogo: a arena e o plano de fundo. Vejamos o exemplo da plataforma.
Primeiramente pedimos ao Panda3D para importar o modelo "plataformaBase", e armazenar o 'caminho' para esse modelo na variável self.plataforma.
Porque esse é um ponto crucial?? Primeiro porque esse é o passo mais básico para carregar qualquer modelo para sua cena, mas mais importante que isso, precisamos entender esse tal de "render".
O "render" é o nó raiz do grafo de cena do Panda3D! Essa explicação foi suficiente pra você? Ótimo! Pule para o próximo capítulo. Se a definição não lhe serviu, preste atenção na explicação.
Primeiro, o que é um grafo de cena? Um grafo de cena é uma forma estruturada de se organizar elementos em um ambiente gráfico.
Quase todos os softwares gráficos que conheço usam um grafo de cena. Basicamente um grafo de cena, possui um 'nó' principal, que aponta para outros elementos que apareceram na cena. Essa forma estruturada serve para otimizar trabalho do sistema de renderização, comunicação entre objetos da cena, collisões, etc… Dê uma olhada nesse link, e nesse também!
Qualquer objeto que tenha que aparecer na cena, precisa estar lincado, direta ou indiretamente ao grafo de cena. Um objeto pode se linkar ao gafo diretamente, ou a outro objeto que já esteja linkado.
Cada elemento adicionado ao grafo de cena, temos um nó (node) e um caminho (nodepath). O nó é criado no grafo de cena, e o caminho para o nó (nodepath), é alocado na variável do código python. 'Nós' e 'caminho de nós' (nodepaths), não são a mesma coisa, mas no Panda3D, nodepaths são muito mais usados do que os nós propriamente ditos. Então, quando o código diz: self.plataforma = load.loadModel("../assets/eggs/plataformaBase"), o Panda3D, carrega o modelo 3D, cria um nó para o grafo de cena, salva o nodepath na variável self.plataforma.
Posteriormente, para que o objeto seja visível, é necessário linkar seu nó, ao 'render': self.plataforma.reparentTo(render).
Bem, o render é o nó principal desse grafo de cena, logo é o centro, o ponto de origem do sistema de coordenadas do Panda3D. Então, após importar o modelo, e linkar ao grafo, podemos posicioná-lo na cena: self.plataforma.setPos(0,10,0)
Esse processo se repetirá por todos os elementos que quisermos colocá-lo na cena. Então ao fim desse código teremos, uma estrutura com essa aparência:
SUGESTÃO: brinque com as posições dos objetos, e com seus links:
– altere os valores das cooredenadas tanto da plataforma, quanto do wall, para entender melhor os valores;
– link o wall, à plataforma: self.wall.reparentTo(self.plataforma);
– brinque com as coordenadas;
– linke a plataforma ao wall, e modifique as coordenadas.
Isso lhe ajudará a entender melhor essas coisas no Panda3D.
Publicado em 17 de março de 2011 21 de julho de 2016 Categorias TutorialTags Development, Game development, Game Engine, gamedev, Panda3D, python, tutorial3 comentários em Panda3D gamedev tutorial 1