-
-
Save turdfire/4fb1a8db218b324500f6842bb5dfdedb to your computer and use it in GitHub Desktop.
State Machine example for Godot 4
This file contains 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
extends State | |
# Idle State example for the player | |
func handle_input(event: InputEvent) -> void: | |
pass | |
func update(delta: float) -> void: | |
owner.apply_gravity(delta) | |
owner.move_player() | |
# jump | |
if Input.is_action_just_pressed("ui_accept"): | |
state_machine.transition_to("Jump") | |
# run | |
if Input.is_action_pressed("ui_right") or Input.is_action_pressed("ui_left"): | |
state_machine.transition_to("Run") | |
func physics_update(delta: float) -> void: | |
if not owner.is_on_floor(): | |
state_machine.transition_to("Fall") | |
func enter(msg := {}) -> void: | |
owner.animated_sprite_2d.play("idle") | |
func exit() -> void: | |
pass | |
This file contains 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
# Virtual base class for all states. | |
class_name State | |
extends Node | |
# Reference to the state machine, to call its `transition_to()` method directly. | |
# That's one unorthodox detail of our state implementation, as it adds a dependency between the | |
# state and the state machine objects, but we found it to be most efficient for our needs. | |
# The state machine node will set it. | |
var state_machine = null | |
# Virtual function. Receives events from the `_unhandled_input()` callback. | |
func handle_input(_event: InputEvent) -> void: | |
pass | |
# Virtual function. Corresponds to the `_process()` callback. | |
func update(_delta: float) -> void: | |
pass | |
# Virtual function. Corresponds to the `_physics_process()` callback. | |
func physics_update(_delta: float) -> void: | |
pass | |
# Virtual function. Called by the state machine upon changing the active state. The `msg` parameter | |
# is a dictionary with arbitrary data the state can use to initialize itself. | |
func enter(_msg := {}) -> void: | |
pass | |
# Virtual function. Called by the state machine before changing the active state. Use this function | |
# to clean up the state. | |
func exit() -> void: | |
pass |
This file contains 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
""" | |
Original code by GDQuest and contributors, CC BY-SA 4.0 | |
link: https://www.gdquest.com/tutorial/godot/design-patterns/finite-state-machine/ | |
""" | |
# Generic state machine. Initializes states and delegates engine callbacks | |
# (_physics_process, _unhandled_input) to the active state. | |
class_name StateMachine | |
extends Node | |
# Emitted when transitioning to a new state. | |
signal transitioned(state_name) | |
# Path to the initial active state. We export it to be able to pick the initial state in the inspector. | |
@export var initial_state := NodePath() | |
# The current active state. At the start of the game, we get the `initial_state`. | |
@onready var state: State = get_node(initial_state) | |
func _ready() -> void: | |
#yield(owner, "ready") | |
# await till owner is ready | |
await owner.ready | |
# The state machine assigns itself to the State objects' state_machine property. | |
for child in get_children(): | |
child.state_machine = self | |
state.enter() | |
# The state machine subscribes to node callbacks and delegates them to the state objects. | |
func _unhandled_input(event: InputEvent) -> void: | |
state.handle_input(event) | |
func _process(delta: float) -> void: | |
state.update(delta) | |
func _physics_process(delta: float) -> void: | |
state.physics_update(delta) | |
# This function calls the current state's exit() function, then changes the active state, | |
# and calls its enter function. | |
# It optionally takes a `msg` dictionary to pass to the next state's enter() function. | |
func transition_to(target_state_name: String, msg: Dictionary = {}) -> void: | |
# Safety check, you could use an assert() here to report an error if the state name is incorrect. | |
# We don't use an assert here to help with code reuse. If you reuse a state in different state machines | |
# but you don't want them all, they won't be able to transition to states that aren't in the scene tree. | |
if not has_node(target_state_name): | |
return | |
state.exit() | |
state = get_node(target_state_name) | |
state.enter(msg) | |
emit_signal("transitioned", state.name) | |
# custom. Display state name in label | |
if owner.has_node("StateLabel"): | |
owner.state_label.text = state.name |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment