How to change the state of a FSM after some time?

My game has several objects with which the player can interact: doors, levers, chests, this kind of things. A door or a chest has the following states: Locked, Closed, Opening, Open, Closing, Broken.

I figured out how to use Finite State Machines, or FSM (1), to implement this without reinventing the wheel myself.

I’m wondering how to automatically change a state after some time has passed. For example, the Opening state should only last as long as the actor animation of the door opening, and then the state should be Open.

I haven’t really seen a scheduling system built in Panda3d, to which I would ask to execute a callback at a given time.

So… polling? I was thinking of adding a .tick() method on every FSM. For example:

    def enterOpening(self):
        self.actor.play('opening')
        self.sounds['opening'].play()
        now = globalClock.get_frame_time()
        self.anim_end_time = now + 2.0 # Anim lasts 2 seconds
    def tick(self):
        if self.state == 'Opening':
            now = globalClock.get_frame_time()
            if now >= self.anim_end_time:
                self.request('Open')

Same for the Closing state. Then I have to call this ‘tick’ every frame for every FSM. Somehow, it feels wasteful.

Is there something I can do with Intervals or something?

(1) http://www.panda3d.org/manual/index.php/Finite_State_Machines

Hi, you could try using a doMethodLater which invokes the request after 2 seconds.

Wow, that was easy.

    def enterOpening(self):
        anim = self.state
        self.actor.play(anim)
        self.sounds[anim].play()
        taskMgr.doMethodLater(2.0, self.requestTask, 'Open')
    def requestTask(self, task):
        self.request(task.name)
        return task.done

It’s not very clean to use the desired state as a name for the task. But I couldn’t understand how to pass extra parameters, it kept throwing errors at me.

So, it’s working :slight_smile:.

One worry though: it’s related to destroying my object. If I want my object gone before the two seconds (for example, the player enters another level and the door gets unloaded), can I remove the task from the task manager?

What I do is keep ahold of the task that is returned when you add a task, for example:

self.my_special_task = taskMgr.doMethodLater(2.0, self.requestTask, 'Open')

Then you can make an exitOpening function and put in there:

self.my_special_task.remove()

As long as you call .cleanup() on the FSM when destroying your object, the exit function (if one exists) will be called for whatever state the FSM is in. It will also get called whenever the FSM transitions from Opening to any other state.