Hello, so I’m trying to create something similar to what I had in a previous project in another engine. I wanted to create a Finite State Machine to handle animations that would use generic names for animations every character would use. For example “run_left,” “run_up,” “reload,” etc. for animation names and then check if these animations are to be played. This is easy enough if I wanted to create one bloated character script that handles movement, attacks, and anything else that would require an animation, but in the spirit of decoupling I’d like to have them all be different scripts or scenes.
The issue I’m running into is just simply not knowing how to organize this or if its just easier to have the one character script that handles everything involving input and the animations that accompany the input.
Any suggestions are appreciated! Thanks in advance.
Here’s a simple scalable state machine, that i always use.
state machine script:
class_name StateMachine
extends Node
@export var use_anim_player:bool = false
@export var anim_player:AnimationPlayer
@export var subject:CharacterBody2D
@export var _initial_state:State
var current_state:State:set = _set_current_state
func _enter_tree() -> void:
current_state = _initial_state
for child:State in get_children():
child.subject = subject
child.new_state_request.connect(on_new_state_requested)
func on_new_state_requested(state:State) -> void:
current_state = state
current_state._enter()
func _set_current_state(new_state: State) -> void:
if current_state:
current_state._leave()
current_state = new_state
if use_anim_player:
set_animation()
func set_animation() -> void:
var anim_name:StringName = current_state.name.to_lower()
if anim_player.has_animation(anim_name):
anim_player.play(anim_name)
func _physics_process(delta:float) -> void:
current_state._state_process(delta)
state class script:
class_name State extends Node
var subject:CharacterBody2D
var direction:Vector2
signal new_state_request(state:State)
func _enter() -> void:
push_error("_enter function not implemented in state %s" % self.name)
func _leave() -> void:
push_error("_leave function not implemented in state %s" % self.name)
func _state_process(_delta:float) -> void:
push_error("state_process function not implemented in state %s" % self.name)
func _is_available() -> bool:
return true
Add a node to your character, name it StateMachine and attach the script.
Then for each state you want add a node as child, and attach a script which extends the state class of my second script.
Override the _enter, _leave and _state_process functions.
If you want to change states from “state A” to “state B”, put an export state in A and assign B.
When changing state use new_state_request.emit(“variable of B”) inside A.