Last active
April 12, 2021 09:35
-
-
Save PranavSK/b3eff3da7be23998f5be58f5d4b85094 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class_name StateMachine | |
| extends Node | |
| """ | |
| An automata implemented as a Node which supports a pushdown stack. | |
| The below is a state machine that can be used as a finite state machine or a | |
| pushdown automata by using Object or Object-derived instances as nodes. | |
| Implement one of the following callback functions in the state object to get the | |
| relevant behaviour. | |
| Called by the state machine after the state object is registered. | |
| func on_register(state_machine: StateMachine) -> void: pass | |
| Called by the state machine after the state object is unregistered. | |
| func on_unregister() -> void: pass | |
| Called by the state machine when the state is added to the stack. | |
| func on_enter() -> void: pass | |
| Called by the state machine when the state is removed from the stack. | |
| func on_exit() -> void: pass | |
| Called by the state machine when the state is pushed down the stack. | |
| func on_pause() -> void: pass | |
| Called by the state machine when the stack is popped and this state object is | |
| brought to the top of the stack. | |
| func on_resume() -> void: pass | |
| Called by the state machine on every update. | |
| func on_update(delta) -> void: pass | |
| Check if any transition from this state occurs. | |
| func check_transition(state_machine: StateMachine) -> bool: return false | |
| """ | |
| var _states: = {} | |
| var _state_names_stack = [] | |
| var _prev_state_name | |
| func register_state(state_name: String, state: Object) -> void: | |
| """ | |
| Register a state to use with the state machine. | |
| Parameters: | |
| state_name (String): The name of the state to register. | |
| state (Object): The state object to register. | |
| """ | |
| if _states.has(state_name): | |
| push_error("The given state %s is already registered." % state_name) | |
| return | |
| _states[state_name] = state | |
| if state.has_method("on_register"): | |
| state.on_register() | |
| func is_registered(state_name: String) -> bool: | |
| """ | |
| Check if a state is already registered. | |
| Parameters: | |
| state_name (String): The name of the state to check. | |
| Returns: | |
| (bool): True if a state with state_name is registered, else false. | |
| """ | |
| return _states.has(state_name) | |
| func unregister_state(state_name: String): | |
| """ | |
| Unregister a state and remove all its instances from the state stack. | |
| The state machine callbacks are not invoked if an instance of the state was | |
| in the stack. | |
| Parameters: | |
| state_name (String): The name of the state to unregister and remove. | |
| """ | |
| var state_to_remove = _states[state_name] | |
| var inds_to_remove: = [] | |
| for ind in _state_names_stack.size(): | |
| if _state_names_stack[ind] == state_name: | |
| inds_to_remove.append(ind) | |
| for ind in inds_to_remove: | |
| _state_names_stack.remove(ind) | |
| if state_to_remove.has_method("on_unregister"): | |
| state_to_remove.on_unregister() | |
| _states.erase(state_name) | |
| func get_registered_state(state_name: String) -> Object: | |
| """ | |
| Get the state object registered with the name given. | |
| Parameters: | |
| state_name (String): The name of the state to retrieve. | |
| Returns: | |
| (Object): The state object registered with state_name. | |
| """ | |
| return _states[state_name] | |
| func get_current_state() -> Object: | |
| """ | |
| Get current active state object. | |
| Returns: | |
| (Object): The state object that is currently active | |
| """ | |
| return get_registered_state(_state_names_stack.back()) | |
| func get_current_state_name() -> String: | |
| """ | |
| Get current active state name. | |
| Returns: | |
| (String): The name of the state that is currently active. | |
| """ | |
| return _state_names_stack.back() | |
| func get_prev_state_name() -> String: | |
| """ | |
| Get previously active state name. | |
| Return: | |
| (String): The name of the previously active state. | |
| """ | |
| return _prev_state_name | |
| func change_state(new_state_name: String) -> void: | |
| """ | |
| Replaces the active state with another state, without notifying the | |
| underlying state. | |
| Parameters | |
| new_state_name (String): The name of the state to change to. | |
| It needs to be registered. | |
| """ | |
| _prev_state_name = get_current_state_name() | |
| pop_state_silent() | |
| push_state_silent(new_state_name) | |
| func pop_state() -> void: | |
| """Pops the current state and resumes the underlying state.""" | |
| _prev_state_name = get_current_state_name() | |
| pop_state_silent() | |
| var curr_state = get_current_state() | |
| if curr_state and curr_state.has_method("on_resume"): | |
| curr_state.on_resume() | |
| func pop_state_silent() -> void: | |
| """Pops the current state without notifying the underlying state.""" | |
| var old_state_name = _state_names_stack.pop_back() | |
| var old_state = get_registered_state(old_state_name) | |
| if old_state and old_state.has_method("on_exit"): | |
| old_state.on_exit() | |
| func pop_all(excluding_root: = false) -> void: | |
| """ | |
| Pops all states in the stack. | |
| Parameters | |
| excluding_root (bool): Whether to keep the bottom state. | |
| """ | |
| while _state_names_stack.size() > 1 if excluding_root else 0: | |
| pop_state() | |
| func push_state(new_state_name: String) -> void: | |
| """ | |
| Pushes a state to the top of the stack and pauses the underlying state. | |
| Parameters | |
| new_state_name (String): The name of the state to push. | |
| It needs to be registered. | |
| """ | |
| _prev_state_name = get_current_state_name() | |
| var old_state = get_current_state() | |
| if old_state and old_state.has_method("on_pause"): | |
| old_state.on_pause() | |
| push_state_silent(new_state_name) | |
| func push_state_silent(new_state_name: String) -> void: | |
| """ | |
| Pushes a state to the top of the stack without notifying the underlying | |
| state. | |
| Parameters | |
| new_state_name (String): The name of the new state to push. | |
| It needs to be registered. | |
| """ | |
| _state_names_stack.push_back(new_state_name) | |
| var new_state = get_registered_state(new_state_name) | |
| if new_state.has_method("on_enter"): | |
| new_state.on_enter() | |
| func update(delta: float) -> void: | |
| """ | |
| Update the state machine. | |
| Updates the current active state and then checks for any valid transitions. | |
| Parameters: | |
| delta (float): The time delta relevant for the update tick. | |
| """ | |
| if _state_names_stack.empty(): | |
| return | |
| if get_current_state().has_method("on_update"): | |
| get_current_state().on_update(delta) | |
| # TODO: Else check the transition table of the state machine. | |
| # if ( | |
| # get_current_state().has_method("check_transition") and | |
| # get_current_state().check_transition(self) | |
| # ): | |
| # # A transition has occured from within the state. | |
| # return | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment