Animações :: Gamedev receitas Panda3D

Quase qualquer jogo eletrônico utiliza animações para tornar o jogo mais imersivo.

Em jogos 3D as animações, e os modelos, são feitos em softwares especializados, como o Maya, Blender, 3DStudio, e posteriormente importadas pela game engine.

Outro detalhe comum, é que as game engines, separam as informações dos modelos 3D, e das animações, e geram arquivos distintos. Assim, temos um arquivo para o modelo 3D de um dado personagem, e suas animações, cada uma em um arquivo distinto.

No Panda3D, depois de você ter o seu modelo, e animações prontas, é necessário convertê-los para o formato .egg. O próprio Panda3D, possui softwares para essas conversões, dependendo do seu modelador 3D.

No Panda3D, quando se utiliza um modelo, que possui uma animação de esqueleto, é necessário se utilizar a classe Actor.

Para seguir esse tutorial, você precisará baixar alguns arquivos e seguir algumas instruções. Visite esse link antes de prosseguir!!

exemplo 1:

[code lang=”python”]
from direct.showbase.ShowBase import ShowBase
from direct.actor.Actor import Actor

class MyApp(ShowBase):
def __init__(self):
ShowBase.__init__(self)
self.carregaModelos()

def carregaModelos(self):
self.personaActor = Actor(‘../assets/eggs/personagem.egg’,
{
‘idle’:’../assets/eggs/personagem-parado’,
‘run’ :’../assets/eggs/personagem-correr’,
‘jump’:’../assets/eggs/personagem-pular’
}
)
self.personaActor.setScale(1)
self.personaActor.reparentTo(render)
self.personaActor.setPos(0,20,0)
self.personaActor.loop(‘run’)

game = MyApp()

run()
[/code]

No exemplo acima, apenas importamos um modelos com animação. A construção da classe Actor, exige:

– apontar o modelo do ‘personagem’.
– um dicionário com os nomes das animações, e seus respectivos arquivos.

Neste caso temos:
– O modelo. Localizado na pasta: ‘../assets/eggs/personagem.egg’
– Animação ‘idle’. Localizada na pasta: ‘../assets/eggs/personagem-parado’
– Animação ‘run’. Localizada na pasta: ‘../assets/eggs/personagem-correr’
– Animação ‘jump’. Localizada na pasta: ‘../assets/eggs/personagem-pular’

Posteriormente, a única diferença entre um modelo normal, e um Actor, está no comando loop(). Esse comando defini qual animação irá ser executada.

exemplo 2:

[code lang=”python”]

from direct.showbase.ShowBase import ShowBase
from direct.actor.Actor import Actor

class MyApp(ShowBase):
def __init__(self):
ShowBase.__init__(self)
self.carregaModelos()

self.accept(‘w’, self.mudaAnimacao, [‘run’])
self.accept(‘space’, self.mudaAnimacao, [‘jump’])
self.accept(‘s’, self.mudaAnimacao, [‘idle’])

def carregaModelos(self):
self.personaActor = Actor(‘../assets/eggs/personagem.egg’,
{
‘idle’:’../assets/eggs/personagem-parado’,
‘run’ :’../assets/eggs/personagem-correr’,
‘jump’:’../assets/eggs/personagem-pular’
}
)
self.personaActor.setScale(1)
self.personaActor.reparentTo(render)
self.personaActor.setPos(0,20,0)
self.personaActor.loop(‘run’)

def mudaAnimacao(self, name):
self.personaActor.loop(name)

game = MyApp()

run()
[/code]

Neste segundo exemplo, temos uma simples demonstração de como se fazer uma troca de animações. Ao se pressionar a tecla ‘w’, o personagem corre; Ao se pressionar a tecla ‘space’, o personagem ‘pula’; Ao se pressionar a tecla ‘s’ o personagem fica parado.

Observe que a transição entre animações é brusca. No exemplo abaixo, há uma sugestão de uso de “blend” de animações, o que permite transições mais suaves.

[code lang=”python”]

from pandac.PandaModules import loadPrcFileData
loadPrcFileData(”,’show-frame-rate-meter 1′)

from direct.showbase.ShowBase import ShowBase
from pandac.PandaModules import *
from direct.actor.Actor import Actor
from direct.interval.IntervalGlobal import *

class MyApp(ShowBase):
def __init__(self):
ShowBase.__init__(self)
self.carregaModelos()

self.accept(‘w’, self.mudaAnimacao, [‘run’])
self.accept(‘space’, self.mudaAnimacao, [‘jump’])
self.accept(‘s’, self.mudaAnimacao, [‘idle’])

#self.state: auxilia na mudanca entre animacoes
self.state = { ‘idle’:True,
‘run’:False,
‘jump’:False}

#self.blend: Uso de ‘Interval’ com ‘LerpFunction’ para realizar as mudancas de animacoes
self.blend = LerpFunc(self.blendAnim, fromData = 0, toData =1,
duration = .2, blendType=’noBlend’,
extraArgs=[], name=None)

def carregaModelos(self):
self.personaActor = Actor(‘../assets/eggs/personagem.egg’,
{
‘idle’:’../assets/eggs/personagem-parado’,
‘run’ :’../assets/eggs/personagem-correr’,
‘jump’:’../assets/eggs/personagem-pular’
}
)
self.personaActor.setScale(1)
self.personaActor.reparentTo(render)
self.personaActor.setPos(0,20,0)

#abilita o blend de animacoes
self.personaActor.enableBlend()

#opcao por iniciar todas as animacoes
self.personaActor.loop(‘run’)
self.personaActor.loop(‘idle’)
self.personaActor.loop(‘jump’)

#setControlEffect: definie o ‘peso’ de cada animacao no personagem(1 = 100% ; 0.1 = 10%)
self.personaActor.setControlEffect(‘jump’, 0.0)
self.personaActor.setControlEffect(‘idle’, 1.0)
self.personaActor.setControlEffect(‘run’, 0.0)

def mudaAnimacao(self, name):
# Essa funcao recebe o input do usuario, com um parametro de
# qual animacao ira iniciar (name).
# A LerpFunction self.blend recebe como argumentos extras,
# o nome do estado (self.state – ver código da funcao getCurrentState),
# e o nome da animacao que deve iniciar
self.blend.extraArgs = [self.getCurrentState(), name]
self.blend.start()

def getCurrentState(self):
if self.state[‘idle’]: return ‘idle’
elif self.state[‘run’]: return ‘run’
elif self.state[‘jump’]: return ‘jump’
else: return None

def changeStateTo(self, name):
self.state[‘idle’] = False
self.state[‘run’] = False
self.state[‘jump’] = False
self.state[name] = True

def blendAnim(self, data, atual, destino):
# Nessa funcao, ativada pela LerpFunction,
# os ‘pesos’ das animacoes sao trocados, e a transicao
# fica mais suave.
self.personaActor.setControlEffect(atual, 1-data)
self.personaActor.setControlEffect(destino, data)

if data == 1:
#ao final da transicao, mudamos o estado.
self.changeStateTo(destino)

game = MyApp()

run()

[/code]Every game uses animations to provide more immersion to the players.

In 3D games, animations and models, are made in specific softwares like Maya, Blender, 3DStudio, etc, and later on they are imported by the game engine.

Other common detail is that game engines usually uses separate files to the 3D models, and other files for the animations. This approach allows developers to manipulate those files more independently.

In Panda3D, you 3D model files (the model itself as well the animations) must be converted to the .egg format. This is a specific Panda3D’s file type, gracefully Panda3D, has some software to make such conversions from 3D modelers. Check Panda3D’s web site for more details.

When you want to use some rigged animated model on Panda3D, you must use the class Actor.

In order to keep following this tutorial, you need to download some files, and follow some instructions to set then up correctly.

sample 1:

[code lang=”python”]

from direct.showbase.ShowBase import ShowBase
from direct.actor.Actor import Actor

class MyApp(ShowBase):
def __init__(self):
ShowBase.__init__(self)
self.loadModels()

def loadModels(self):
self.personaActor = Actor(‘../assets/eggs/personagem.egg’,
{
‘idle’:’../assets/eggs/personagem-parado’,
‘run’ :’../assets/eggs/personagem-correr’,
‘jump’:’../assets/eggs/personagem-pular’
}
)
self.personaActor.setScale(1)
self.personaActor.reparentTo(render)
self.personaActor.setPos(0,20,0)
self.personaActor.loop(‘run’)

game = MyApp()

run()

[/code]

In the sample above, a model with three animations was imported, and set up to run one animation. In this case, a rigged model is been used, so we need to instantiate the Actor class. The Actor class demands:

– import a model file;

– a python dictionary with the animation names as keys, and it’s files as values.

In this case:

– the model. Located at folder: ‘../assets/eggs/personagem.egg’

– Animation ‘idle’. Located at folder: ‘../assets/eggs/personagem-parado’

– Animation ‘run’. Located at folder: ‘../assets/eggs/personagem-correr’

– Animation ‘jump’. Located at folder: ‘../assets/eggs/personagem-pular’

At the end, a loop() command sets the animation ‘run’ to be executed as a infinity loop. In case you want to test, try change the line: self.personaActor.loop(‘run’), and change the ‘run’, inside the loop() function call to ‘idle’ and ‘jump’. You will see diferent animations.

sample 2:

[code lang=”python”]

from direct.showbase.ShowBase import ShowBase
from direct.actor.Actor import Actor

class MyApp(ShowBase):
def __init__(self):
ShowBase.__init__(self)
self.loadModels()

self.accept(‘w’, self.changeAnimation, [‘run’])
self.accept(‘space’, self.changeAnimation, [‘jump’])
self.accept(‘s’, self.changeAnimation, [‘idle’])

def loadModels(self):
self.personaActor = Actor(‘../assets/eggs/personagem.egg’,
{
‘idle’:’../assets/eggs/personagem-parado’,
‘run’ :’../assets/eggs/personagem-correr’,
‘jump’:’../assets/eggs/personagem-pular’
}
)
self.personaActor.setScale(1)
self.personaActor.reparentTo(render)
self.personaActor.setPos(0,20,0)
self.personaActor.loop(‘run’)

def changeAnimation(self, name):
self.personaActor.loop(name)

game = MyApp()

run()

[/code]

In sample 2, there is a example showing how to change animations with user input. By pressing key ‘w’ the ‘run’ animation is played; by pressing key ‘s’, the ‘idle’ animation is played; And by pressing key ‘space’, the ‘jump’ animation is played.

You will see that the animation transition is quite bad. That because there is no blending during animation transitions. In next sample, we will fix that.

sample 3:

[code lang=”python”]
from pandac.PandaModules import loadPrcFileData
loadPrcFileData(”,’show-frame-rate-meter 1′)

from direct.showbase.ShowBase import ShowBase
from pandac.PandaModules import *
from direct.actor.Actor import Actor
from direct.interval.IntervalGlobal import *

class MyApp(ShowBase):
def __init__(self):
ShowBase.__init__(self)
self.loadModels()

self.accept(‘w’, self.changeAnimation, [‘run’])
self.accept(‘space’, self.changeAnimation, [‘jump’])
self.accept(‘s’, self.changeAnimation, [‘idle’])

#self.state: helps in changing animation logic
self.state = { ‘idle’:True,
‘run’:False,
‘jump’:False}

#self.blend: The ‘Interval’ with ‘LerpFunction’ is used to change the control effect smoothly
self.blend = LerpFunc(self.blendAnim, fromData = 0, toData =1,
duration = .2, blendType=’noBlend’,
extraArgs=[], name=None)

def loadModels(self):
self.personaActor = Actor(‘../assets/eggs/personagem.egg’,
{
‘idle’:’../assets/eggs/personagem-parado’,
‘run’ :’../assets/eggs/personagem-correr’,
‘jump’:’../assets/eggs/personagem-pular’
}
)
self.personaActor.setScale(1)
self.personaActor.reparentTo(render)
self.personaActor.setPos(0,20,0)

#enable Blend in animations (mandatory)
self.personaActor.enableBlend()

#let’s run all animations in loop at once
self.personaActor.loop(‘run’)
self.personaActor.loop(‘idle’)
self.personaActor.loop(‘jump’)

#setControlEffect: sets the “weight” that each animation will impolse to the model(1 = 100% ; 0.1 = 10%)
self.personaActor.setControlEffect(‘jump’, 0.0)
self.personaActor.setControlEffect(‘idle’, 1.0)
self.personaActor.setControlEffect(‘run’, 0.0)

def changeAnimation(self, name):
# This function will be called by an user input, with the argument ‘name’,
# this ‘name’, is the name for the next animation to play.
# The LerpFunction self.blend, shall receive as extra arguments,
# the name for current state (self.state – check getCurrentState function code),
# the name for the next animation
self.blend.extraArgs = [self.getCurrentState(), name]
self.blend.start()

def getCurrentState(self):
if self.state[‘idle’]: return ‘idle’
elif self.state[‘run’]: return ‘run’
elif self.state[‘jump’]: return ‘jump’
else: return None

def changeStateTo(self, name):
self.state[‘idle’] = False
self.state[‘run’] = False
self.state[‘jump’] = False
self.state[name] = True

def blendAnim(self, data, current, next):
# This function is called every frame during LerpFunction life (0.2 seconds),
# the animation’s ‘weight’ is changed slowly, and transitions blends.
# Smooth transitions!!
self.personaActor.setControlEffect(current, 1-data)
self.personaActor.setControlEffect(next, data)

if data == 1:
# at the end, we change the current state.
self.changeStateTo(next)

game = MyApp()

run()

[/code]

Deixe uma resposta

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