Loop animation when key is pressed down

So currently when I press the up arrow the panda moves forward and plays the first part of the animation. How do I make it so that if I hold down the up arrow it will continue to move and loop the actor animation?
here is my current code:

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


class MyApp(ShowBase):

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

        # Load the environment model.
        self.environ = self.loader.loadModel("/Developer/Panda3D/models/environment")
        self.environ.reparentTo(self.render)

        # We do not have a skybox, so we will just use a sky blue background color
        self.setBackgroundColor(0.53, 0.80, 0.92, 1)


        self.pandaActor = Actor("models/panda-model",
                                {"walk": "models/panda-walk4"})
        self.pandaActor.setScale(0.005, 0.005, 0.005)
        self.pandaActor.reparentTo(self.render)


        self.accept("arrow_up", self.Move)
        self.accept("arrow_right", self.Move2)

        self.taskMgr.add(self.camera_task, "camera")

    def Move(self):
        self.pandaActor.setY(self.pandaActor, -200)
        self.pandaActor.setPlayRate(2, "walk")
        self.pandaActor.play("walk")

    def Move2(self):
        self.pandaActor.setH(self.pandaActor, -90)

    def camera_task(self, task):
        self.camera.setPos(self.pandaActor.getX(), self.pandaActor.getY() + 15, 3)
        self.camera.lookAt(self.pandaActor)
        return Task.cont

app = MyApp()
app.run()

Thanks!

if you want to keep your current way of handling the walking mechanic, you need to add a few things.

  1. you also need to accept the arrow_up-repeat event to catch the respective event multiple times if the button is held down.
  2. you need to check if the walk animation is playing inside the Move function and only start it if it’s not.
  3. instead of play you need to use the loop function
  4. as it’s now looping indefinitely, you need to accept the arrow_up-up event to catch the event when the player releases the up arrow again
  5. add another function for the event caught in 4. in which you simply call self.pandaActor.stop() to stop the animations currently playing on it.

here’s how it would look like in the end:

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


class MyApp(ShowBase):

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

        # Load the environment model.
        self.environ = self.loader.loadModel("environment")
        self.environ.reparentTo(self.render)

        # We do not have a skybox, so we will just use a sky blue background color
        self.setBackgroundColor(0.53, 0.80, 0.92, 1)


        self.pandaActor = Actor("models/panda-model",
                                {"walk": "models/panda-walk4"})
        self.pandaActor.setScale(0.005, 0.005, 0.005)
        self.pandaActor.reparentTo(self.render)


        self.accept("arrow_up", self.Move)
        self.accept("arrow_up-repeat", self.Move)
        self.accept("arrow_up-up", self.stopMove)
        self.accept("arrow_right", self.Move2)

        self.taskMgr.add(self.camera_task, "camera")

    def Move(self):
        self.pandaActor.setY(self.pandaActor, -200)
        self.pandaActor.setPlayRate(2, "walk")
        walk = self.pandaActor.getAnimControl("walk")
        if not walk.isPlaying():
            self.pandaActor.loop("walk")

    def stopMove(self):
        self.pandaActor.stop("walk")

    def Move2(self):
        self.pandaActor.setH(self.pandaActor, -90)

    def camera_task(self, task):
        self.camera.setPos(self.pandaActor.getX(), self.pandaActor.getY() + 15, 3)
        self.camera.lookAt(self.pandaActor)
        return Task.cont

app = MyApp()
app.run()

Though, note, that this will result in a little pause between the initial down press of the button and the repeat event.

Also, note, that this is not really the typical way of handling this kind of things. Usually you’d use tasks, like you already did for your camera task, in which you check whether keys are pressed down and handle them in your code accordingly. I’ll show you how your code could be changed to use tasks as a more elegant alternative for the forward movement. I’ve left the rotation in the old way as in this case it would be the simpler way if you want a full 90° rotation each key press and not a partial rotation as long as the key is held down.

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


class MyApp(ShowBase):

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

        # Load the environment model.
        self.environ = self.loader.loadModel("environment")
        self.environ.reparentTo(self.render)

        # We do not have a skybox, so we will just use a sky blue background color
        self.setBackgroundColor(0.53, 0.80, 0.92, 1)


        self.pandaActor = Actor("models/panda-model",
                                {"walk": "models/panda-walk4"})
        self.pandaActor.setScale(0.005, 0.005, 0.005)
        self.pandaActor.reparentTo(self.render)

        self.accept("arrow_right", self.Move2)

        self.taskMgr.add(self.move_task, "move")
        self.taskMgr.add(self.camera_task, "camera")

    def move_task(self, task):
        # we need this to calculate the distance that should be taken each frame
        dt = globalClock.getDt()

        # check if the up arrow is held down
        # don't get confused by the name of mouseWatcherNode.
        # It's handling not only the mouse but also keyboard buttons.
        if base.mouseWatcherNode.isButtonDown("arrow_up"):
            # notice the usage of dt in the calculation
            self.pandaActor.setY(self.pandaActor, -200 * dt)

            self.pandaActor.setPlayRate(2, "walk")

            # check if the walk animation is playing
            walk = self.pandaActor.getAnimControl("walk")
            if not walk.isPlaying():
                # if not, start it
                self.pandaActor.loop("walk")
        else:
            # stop the walk animation if the up arrow is not pressed
            self.pandaActor.stop("walk")
        return Task.cont

    def Move2(self):
        self.pandaActor.setH(self.pandaActor, -90)

    def camera_task(self, task):
        self.camera.setPos(self.pandaActor.getX(), self.pandaActor.getY() + 15, 3)
        self.camera.lookAt(self.pandaActor)
        return Task.cont

app = MyApp()
app.run()
1 Like

thank you this helped me a ton!!