Possible Animation Bug

Hi to all,

When I try to use the method " isPlaying() " to figure out if an actor has finished playing a certain animation, it works by returning True, so long as I specify that the animation plays forwards [ by passing a value of 1.0 as the first parameter to setPlayRate()]. However, when I specify that the animation plays backwards, even after the animation is done playing, querying the actor to see if a certain animation is playing still returns True. Here is a small sample to reproduce this:

from math import pi, sin, cos
 
from direct.showbase.ShowBase import ShowBase
from direct.task import Task
from direct.actor.Actor import Actor
 
class MyApp(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)
 
        # Load the environment model.
        self.scene = self.loader.loadModel("models/environment")
        # Reparent the model to render.
        self.scene.reparentTo(self.render)
        # Apply scale and position transforms on the model.
        self.scene.setScale(0.25, 0.25, 0.25)
        self.scene.setPos(-8, 42, 0)
 
        # Load and transform the panda actor.
        self.pandaActor = Actor("models/panda-model",
                                {"walk": "models/panda-walk4"})
        self.pandaActor=Actor()
        self.pandaActor.loadModel("models/panda-model")
        self.pandaActor.loadAnims({"walk": "models/panda-walk4"})
        self.pandaActor.setScale(0.005, 0.005, 0.005)
        self.pandaActor.reparentTo(self.render)
        #set up the dictionary:
        self.keyz_dctn={'front':0,'back':0}
        #the events:
        self.accept('1',self.set_key,['front',1])
        self.accept('2',self.set_key,['back',1])
        
        self.accept('1-up',self.set_key,['front',0])
        self.accept('2-up',self.set_key,['back',0])
        #the handler and the key task:
        self.anim_contrl=self.pandaActor.getAnimControl("walk")
        self.taskMgr.add(self.key_press_play_task, "key_task")
    
    def key_press_play_task(self,task):
        if(self.keyz_dctn['front']==1):
           if(not self.anim_contrl.isPlaying()): 
            print "PLAYING FORWARDS..."
            self.pandaActor.setPlayRate(1.0,"walk")
            self.pandaActor.play("walk")
            self.taskMgr.add(self.showAnimState, "anim_task")
        elif(self.keyz_dctn['back']==1):
           if(not self.anim_contrl.isPlaying()): 
            print "PLAYING BACKWARDS..."
            self.pandaActor.setPlayRate(-1.0,"walk")
            self.pandaActor.play("walk")
            self.taskMgr.add(self.showAnimState, "anim_task")
        return task.cont
    
    def set_key(self,key,value):
        self.keyz_dctn[key]=value
        
    # When the animation stops playing, print a message:
    def showAnimState(self, task):
        if(not self.anim_contrl.isPlaying()):
          print "ANIMATION IS NOT PLAYING..."
          taskMgr.remove("anim_task")
        return task.cont
 
app = MyApp()
app.run()

To use the sample, just run it, then press 1 to see the actor play an animation forwards, when this animation is done playing, the message “ANIMATION IS NOT PLAYING… " is printed out to the console. Pressing 2 (causes the actor to play the animation backwards) however does not result in the message being printed after the actor stops playing the animation backwards, because " isPlaying()” still returns True even after the actor has stopped playing that animation.

Any help to solve this is welcome,
Thanks.

Actually, it does get printed, but - and that’s the weird thing - at the very beginning of the animation. Just start by pressing 2 instead of 1 and you’ll see.

When you call self.anim_contrl.getNextFrame() right after starting the backwards playback, it will return the last frame in the animation (60 in your example) - which is to be expected - and it seems this is the problem: it looks like AnimControl.isPlaying() returns False when the last frame is reached, instead of checking whether the animation has run its course. When the reversed animation stops, it’s at frame 0, so it returns True.

A possible workaround is to check if self.anim_contrl.getFrame() returns the expected end frame:

from math import pi, sin, cos

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

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

        # Load the environment model.
        self.scene = self.loader.loadModel("models/environment")
        # Reparent the model to render.
        self.scene.reparentTo(self.render)
        # Apply scale and position transforms on the model.
        self.scene.setScale(0.25, 0.25, 0.25)
        self.scene.setPos(-8, 42, 0)

        # Load and transform the panda actor.
        self.pandaActor = Actor("models/panda-model",
                                {"walk": "models/panda-walk4"})
        self.pandaActor=Actor()
        self.pandaActor.loadModel("models/panda-model")
        self.pandaActor.loadAnims({"walk": "models/panda-walk4"})
        self.pandaActor.setScale(0.005, 0.005, 0.005)
        self.pandaActor.reparentTo(self.render)
        #the animation direction:
        self.direction = ''
        #the events:
        self.accept('1', self.set_direction, ['front'])
        self.accept('2', self.set_direction, ['back'])
        #the handler:
        self.anim_contrl=self.pandaActor.getAnimControl("walk")
        #current state of the animation
        self.anim_stopped = True

    def set_direction(self, direction):
        if self.anim_stopped:
            if direction == 'front':
                print "PLAYING FORWARDS..."
                self.anim_contrl.setPlayRate(1.0)
            elif direction == 'back':
                print "PLAYING BACKWARDS..."
                self.anim_contrl.setPlayRate(-1.0)
            self.direction = direction
            self.anim_stopped = False
            self.anim_contrl.play()
            self.taskMgr.add(self.showAnimState, "anim_task")

    # When the animation stops playing, print a message:
    def showAnimState(self, task):
        if self.direction == 'front':
            end_frame = self.anim_contrl.getNumFrames() - 1
        elif self.direction == 'back':
            end_frame = 0

        if self.anim_contrl.getFrame() == end_frame:
            print "ANIMATION IS NOT PLAYING...\n"
            self.anim_stopped = True
            return task.done

        return task.cont
 
app = MyApp()
app.run()

Thanks for the workaround, I’ve been able to successfully adapt it to my situation. So does that mean that this is a bug? That :

Or perhaps it’s specified that the method only works with forward playing animations? Either way thanks for the help. :astonished:

Since I can’t immediately find a reference to it being expected behaviour, I’d say it’s a bug, yes. And if it isn’t, I’m sure rdb will slap me around a bit and set me straight.

And thank you for helping me reach my 100th post! :smiley:

Yep, it’s a bug. I just fixed it. Thanks!