Colisões :: Gamedev receitas Panda3D

Detetar colisões entre objetos é uma prática fundamental para (quase) todo jogo eletrônico.
Além de detetar as colisões, é importante saber o que fazer, depois que elas ocorrem.
As game engines, sempre possuem recursos para a deteção de colisões, e esse é um tópico muito importante de ser observado, antes de escolher qual game engine utilizar para seu projeto.

Em linhas gerais, os pontos básicos a serem observados são:
– como se configura a deteção de colisão;
– quais geometrias de colisão (esferas, cubos, planos …) são disponibilizados pela engine;
– Se a game engine, permite deteção de colisão com geometria 3D (mesh).

Quando as colisões são detetadas, em geral, eventos são disparados pelo sistema. E esses eventos, podem ser capturados por objetos de jogo; que iniciam funções, para gerar a dinâmica do jogo.

exemplo 1:

[code lang=”python”]
from direct.showbase.ShowBase import ShowBase
from direct.task import Task
from pandac.PandaModules import *

class MyApp(ShowBase):

def __init__(self):
ShowBase.__init__(self)

# Cria o Traverser e handlers: primeiro passo
base.cTrav = CollisionTraverser()
base.pusher = CollisionHandlerPusher()
base.pusher.addInPattern(‘%fn-into-%in’)

self.cubo = self.loader.loadModel(“box”);
self.cubo.reparentTo(self.render)
self.cubo.setScale(1)
self.cubo.setPos(-3,10,-2)

# Cria colliders: segundo passo
self.cuboCEsfera = CollisionNode(“cuboCEsfera”)
self.cuboCEsfera.addSolid( CollisionSphere(0,0,0, 1.05) )
self.cuboEsfNP = self.cubo.attachNewNode(self.cuboCEsfera)
self.cuboEsfNP.show()

# Apontar ao Traverser e ao Handler, quais objetos devem ser observados ao colidir, e como tratar as colisões entre eles : terceiro passo
base.cTrav.addCollider( self.cuboEsfNP, base.pusher )
base.pusher.addCollider( self.cuboEsfNP, self.cubo )

self.smiley = loader.loadModel(‘smiley’)
self.smiley.reparentTo(self.render)
self.smiley.setScale(0.5);
self.smiley.setPos(3, 10, -1.5)

self.smileyCEsfera = CollisionNode(“smileyCEsfera”)
self.smileyCEsfera.addSolid( CollisionSphere(0,0,0, 1.05) )
self.smileyEsfNP = self.smiley.attachNewNode(self.smileyCEsfera)
self.smileyEsfNP.show()

self.taskMgr.add(self.funcaoDeTarefa, “Tarefa de teste”)
self.accept(‘cuboCEsfera-into-smileyCEsfera’, self.eventoColisao)

def eventoColisao(self, entrada):
print “ocorreu choque entre objetos!!”
print entrada

def funcaoDeTarefa(self, task):
self.cubo.setX(self.cubo.getX()+0.05)
return Task.cont

app = MyApp()
app.run()
[/code]

Neste exemplo, realizamos uma deteção de colisão simples, com utilização de um “handler pusher” e geração de um evento no momento da colisão.
No Panda3D é necessário observar três pontos importantes:
1- A criação de um CollisionTraverser e um CollisionHandler;
2- A criação de: CollisionNode (nós de colisão) e CollisionSolid (sólidos de colisão);
3- A anexação dos CollisionNodes e CollisionSolid, para CollisionTraverser e CollisionHandler.

Etapas do código acima:

Passo 1:

# Cria o Traverser e handlers: primeiro passo
base.cTrav = CollisionTraverser()
base.pusher = CollisionHandlerPusher()
base.pusher.addInPattern(‘%fn-into-%in’)

Primeiramente, o CollisionTraveser foi criado. Posteriormente, o CollisionHandler foi criado como um Pusher (o Pusher não permite que objetos transpassem uns aos outros). Na terceira linha, adicionamos ao “Pusher”, a ordem de geração de eventos no momento das colisões.

Passo 2:

# Cria colliders: segundo passo
self.cuboCEsfera = CollisionNode(“cuboCEsfera”)
self.cuboCEsfera.addSolid( CollisionSphere(0,0,0, 1.05) )
self.cuboEsfNP = self.cubo.attachNewNode(self.cuboCEsfera)
self.cuboEsfNP.show()

Este passo é um pouco mais complicado.
Literalmente o código diz:
– crie um nó para colisões chamado “cuboCEsfera”, e guarde o caminho para ele na variável self.cuboCEsfera;
– adicione uma esfera de colisão, em “cuboCEsfera”.
– adicione ao nó self.cubo, o nó guardado na variável self.cuboCEsfera, e guarde o caminho na variável self.cuboEsfNP.
– mostre na tela a esfera de colisão.

Esses comandos são obrigatórios. Exceto o self.cuboEsfNP.show(), que serve apenas para uma pré-visualização.

Observe que esses passos se repetem a cada objeto onde que tem que interagir por colisão. Por exemplo, no objeto smiley, também criamos uma esfera de colisão.

Passo 3:

# Apontar ao Traverser e ao Handler, quais objetos devem ser observados ao colidir, e como tratar as colisões entre eles : terceiro passo
base.cTrav.addCollider( self.cuboEsfNP, base.pusher )
base.pusher.addCollider( self.cuboEsfNP, self.cubo )

Após definir cada nó de colisão (CollisionNode, passos 1 e 2), é necessário informar, como o sistema deve lidar com as colisões.
O comando : base.cTrav.addCollider( self.cuboEsfNP, base.pusher ), diz ao Traverser que as colisões envolvendo o nó self.cuboEsfNP, devem ser tratadas pelo handler “Pusher”.
O comando: base.pusher.addCollider( self.cuboEsfNP, self.cubo ), diz ao “Pusher”, ao se detectar colisões com o nó self.cuboEsfNP, o comportamento do pusher, deve ser aplicado ao nó self.cubo.

Este exemplo é bom para se usar em situações onde um objeto não pode atravessar outros objetos, como um personagem não atravessar uma parede.

Os três passos vistos até aqui, mostram a configuração básica para essa colisão, que constituem os passos principais para se configurar colisões no Panda3D. Agora, vejamos um detalhe interessante, a possibilidade de geração de eventos para cada colisão.

Quando criamos o Handler Pusher (passo 1), observe o comando: base.pusher.addInPattern(‘%fn-into-%in’). Este comando diz ao Pusher observar quando um objeto colidir com outro, e quando as colisões acontecerem, ele dispara eventos pelo sistema.

Observe a linha:
self.accept(‘cuboCEsfera-into-smileyCEsfera’, self.eventoColisao)
Esta linha é um comando de captura de eventos, no caso queremos capturar o evento que será gerado pelo Pusher, quando o cubo colidir com o smiley (‘cuboCEsfera-into-smileyCEsfera’). Observe que os nomes utilizados, são os nomes dos respectivos nós de colisão.

Quando esse evento é capturado, ele aciona a função self.eventoColisao. Observe, que essa função recebe, obrigatoriamente, um argumento, neste caso chamado de ‘entrada’. Essa variável, armazena informações relevantes à colisão, como coordenada da colisão, normal, penetração, etc.

exemplo 2:

[code lang=”python”]

from direct.showbase.ShowBase import ShowBase
from direct.task import Task
from pandac.PandaModules import *

class MyApp(ShowBase):

def __init__(self):
ShowBase.__init__(self)

# Cria o Traverser e handlers: primeiro passo
base.cTrav = CollisionTraverser()
base.event = CollisionHandlerEvent()
base.event.addInPattern(‘%fn-into-%in’)
base.event.addAgainPattern(‘%fn-again-%in’)
base.event.addOutPattern(‘%fn-outof-%in’)

self.cubo = self.loader.loadModel(“box”);
self.cubo.reparentTo(self.render)
self.cubo.setScale(1)
self.cubo.setPos(-3,10,-2)
# Cria colliders: segundo passo
self.cuboCEsfera = CollisionNode(“cuboCEsfera”)
self.cuboCEsfera.addSolid( CollisionSphere(0,0,0, 1.05) )
self.cuboEsfNP = self.cubo.attachNewNode(self.cuboCEsfera)
self.cuboEsfNP.show()
# Apontar ao Traverser e ao Handler, quais objetos devem ser observados ao colidir, e como tratar as colisoes entre eles : terceiro passo
base.cTrav.addCollider( self.cuboEsfNP, base.event )

self.smiley = loader.loadModel(‘smiley’)
self.smiley.reparentTo(self.render)
self.smiley.setScale(0.5);
self.smiley.setPos(0, 10, -1.5)

self.smileyCEsfera = CollisionNode(“smileyCEsfera”)
self.smileyCEsfera.addSolid( CollisionSphere(0,0,0, 1.05) )
self.smileyEsfNP = self.smiley.attachNewNode(self.smileyCEsfera)
self.smileyEsfNP.show()

self.taskMgr.add(self.funcaoDeTarefa, “Tarefa de teste”)

#captura dos eventos de colisao
self.accept(‘cuboCEsfera-into-smileyCEsfera’, self.eventoColisaoIN)
self.accept(‘cuboCEsfera-again-smileyCEsfera’, self.eventoColisaoAGAIN)
self.accept(‘cuboCEsfera-outof-smileyCEsfera’, self.eventoColisaoOUT)

def eventoColisaoIN(self, entrada):
print “ocorreu toque entre objetos!!”
print entrada

def eventoColisaoOUT(self, entrada):
print “ocorreu afastamento entre objetos!!”
print entrada

def eventoColisaoAGAIN(self, entrada):
print “objetos ainda se tocando!!”

def funcaoDeTarefa(self, task):
self.cubo.setX(self.cubo.getX()+0.05)
return Task.cont

app = MyApp()
app.run()

[/code]

Neste segundo exemplo, temos uma demonstração do uso do CollisionHandlerEvent.
Esse handler, digamos é o pai do ‘pusher’. A diferença é que ele não impede os objetos atravessem uns aos outros. Nesse código, vemos que o cubo vai atravessar o smiley sem nenhum problema.

O Handler Event, é uma ótima forma de se criar censores pela arena de jogo. Por exemplo, um local onde um personagem passa e abre uma porta, ou dispara uma armadilha.

Observe as alterações nos passos 1 e 3, e na captura dos eventos.
As diferenças são poucas. A diferença mais significativa, é na criação, e captura, dos eventos. Neste segundo exemplo, além do evento ‘into’, definimos os eventos “outof” e “again”.

Os eventos “into”, acontessem no momento em que os objetos se tocam.
Os eventos “outof”, acontessem no momento em que os objetos param de se tocar.
Os eventos “again”, acontessem enquanto os objetos estiverem se tocando.


Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *