diff options
Diffstat (limited to 'addons/escoria-core/patterns/state_machine')
4 files changed, 230 insertions, 0 deletions
diff --git a/addons/escoria-core/patterns/state_machine/state.gd b/addons/escoria-core/patterns/state_machine/state.gd new file mode 100644 index 0000000..cfa9e0f --- /dev/null +++ b/addons/escoria-core/patterns/state_machine/state.gd @@ -0,0 +1,81 @@ +extends Node +class_name State +## Base interface for all states. +## +## This class doesn't do anything in itself but forces us to pass the right +## arguments to the methods below and makes sure every State object had all of +## these methods. + +## Signal sent when the state just changed. Parameter is the new state value.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |next_state_name|`Variant`|Name of the state that should become active after this state finishes.|yes|[br] +## [br] +signal finished(next_state_name) + + +## Initialize the state. E.g. change the animation[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func enter(): + return + + +## Clean up the state. Reinitialize values like a timer.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func exit(): + return + + +## Manage an input event while this state is active.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |_event|`InputEvent`|InputEvent to process|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func handle_input(_event: InputEvent): + return + + +## Perform an update while this state is active.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |_delta|`float`|float value obtained from a _process() call|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func update(_delta: float): + return + + +# Callback called when an animation is finished. +# [br] +# #### Parameters[br] +# - _anim_name: the animation name that just finished. +func _on_animation_finished(_anim_name: String): + return diff --git a/addons/escoria-core/patterns/state_machine/state.gd.uid b/addons/escoria-core/patterns/state_machine/state.gd.uid new file mode 100644 index 0000000..6ac0bde --- /dev/null +++ b/addons/escoria-core/patterns/state_machine/state.gd.uid @@ -0,0 +1 @@ +uid://c2huw3nfouhce diff --git a/addons/escoria-core/patterns/state_machine/state_machine.gd b/addons/escoria-core/patterns/state_machine/state_machine.gd new file mode 100644 index 0000000..2fa5d96 --- /dev/null +++ b/addons/escoria-core/patterns/state_machine/state_machine.gd @@ -0,0 +1,147 @@ +extends Node +class_name StateMachine +## Base interface for a generic state machine +## It handles initializing, setting the machine active or not +## delegating _physics_process, _input calls to the State nodes, +## and changing the current/active state. + +## Signal emitted when the state has changed. Parameter is the new current state.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |current_state|`Variant`|Instance of the state that became active.|yes|[br] +## [br] +signal state_changed(current_state) + +## Starting state. This node has to be set *before* the initialize(START_STATE) +## command is called. +var START_STATE: Node + +## List of states +var states_map = {} + +## Stack of states +var states_stack = [] # can also be used as a pushdown automaton + +## Value of the current state +var current_state = null + +## Name of the current state +var current_state_name = "" + +## Whether the state machine is currently enabled or not. +var _active = false: + set = set_active + + +## Initialize the state machine with the start_state parameter.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |start_state|`State`|State value to use as starting state for the state machine.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func initialize(start_state: State): + if START_STATE == null: + escoria.logger.error( + self, + "Starting state is required to be initialized with a defined state, + but it is null. Escoria cannot determine which of the defined states + (in states_map dictionary) is supposed to be the starting one. + Please assign a state to START_STATE in your implementation of the StateMachine class.") + for child in get_children(): + child.connect("finished", Callable(self, "_change_state")) + + set_active(true) + states_stack.push_front(start_state) + current_state = states_stack[0] + current_state.enter() + + +## Enable or disable the state machine.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |value|`bool`|if true, enables the state machine. If false, disables it.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_active(value: bool): + _active = value + set_physics_process(value) + set_process_input(value) + if not _active: + states_stack = [] + current_state = null + + +# Manage an input event by the state machine's current state. +# +# #### Parameters +# +# - event: InputEvent to manage. +func _input(event: InputEvent): + current_state.handle_input(event) + + +# Lets the state machine's current state perform an update during +# _physics_process() phase. +# +# #### Parameters +# +# - delta: float value corresponding to the elapsed time since last frame update. +func _physics_process(delta: float): + current_state.update(delta) + + +# Lets the state machine's current state perform an action on animation_finished +# signal. +# +# #### Parameters +# +# - anim_name: name of the animation that finished. +func _on_animation_finished(anim_name: String): + if not _active: + return + current_state._on_animation_finished(anim_name) + + +# Change the current state of the state machine using its name. The value of +# the state to be set is obtained in states_map dictionary. +# +# #### Parameters +# +# - state_name: name of the state to set. +func _change_state(state_name: String): + if not _active: + return + + escoria.logger.trace( + self, + "Dialog State Machine: Changing state from '%s' to '%s'." % [current_state_name, state_name] + ) + + current_state.exit() + + if state_name == "previous": + states_stack.pop_front() + else: + states_stack[0] = states_map[state_name] + + current_state = states_stack[0] + + state_changed.emit(current_state) + + #if state_name != "previous": + current_state.enter() + + current_state_name = state_name diff --git a/addons/escoria-core/patterns/state_machine/state_machine.gd.uid b/addons/escoria-core/patterns/state_machine/state_machine.gd.uid new file mode 100644 index 0000000..5b8e593 --- /dev/null +++ b/addons/escoria-core/patterns/state_machine/state_machine.gd.uid @@ -0,0 +1 @@ +uid://bti0tjqa05fo8 |
