State¶
Intent: Let an object alter its behavior when its internal state changes by encoding states as explicit structures rather than scattered booleans.
Acron Mapping¶
Acron ships a production-ready FSM framework. Use it — do not hand-roll a state machine.
Pattern role |
Acron equivalent |
|---|---|
State machine |
Class inheriting |
State |
Named string (e.g. |
Enter action |
|
Exit action |
|
Transition |
|
Guard (allowed transitions) |
|
How to Implement¶
from direct.fsm.FSM import FSMSubclass
FSM. Pass a name string tosuper().__init__('MyFSM').Define
enterX()/exitX()pairs for each stateX.enterX()starts tasks, plays animations, enables collisions.exitX()reverses them.Optionally restrict valid transitions:
defaultTransitions = {'Idle': ['Walk', 'Jump'], 'Walk': ['Idle', 'Jump'], 'Jump': ['Idle']}.Trigger transitions from game logic with
self.request('Walk'). Illegal transitions raise a clean exception.Call
self.cleanup()on object destruction — automatically exits the current state and transitions back to'Off'.
Code Sketch (Python)¶
from direct.fsm.FSM import FSM
class PlayerFSM(FSM):
defaultTransitions = {
'Idle': ['Walk', 'Jump'],
'Walk': ['Idle', 'Jump'],
'Jump': ['Idle'],
}
def __init__(self, actor):
super().__init__('PlayerFSM')
self.actor = actor
def enterIdle(self):
self.actor.loop('idle')
def exitIdle(self):
self.actor.stop()
def enterWalk(self):
self.actor.loop('walk')
taskMgr.add(self._move_task, 'playerMove')
def exitWalk(self):
self.actor.stop()
taskMgr.remove('playerMove')
def _move_task(self, task):
self.actor.setY(self.actor, 5 * globalClock.getDt())
return task.cont
def enterJump(self):
self.actor.play('jump')
taskMgr.doMethodLater(0.6, self._land, 'playerLand')
def exitJump(self):
taskMgr.remove('playerLand')
def _land(self, task):
self.request('Idle')
return task.done
Pitfalls¶
Mismatched enter/exit: everything started in
enterX()must be stopped inexitX(). Tasks not removed here leak and run in the wrong state.Re-entering the same state:
request('Walk')while already in'Walk'is a no-op by default. If you need refresh behavior, callself.request('Off')first or handle it explicitly.Accessing
self.stateduring transition: it isNonewhile exiting one state and entering the next. Useself.oldState/self.newStateif you need to inspect mid-transition.