I've got an FSM at the heart of a platformer engine (see it in action here), and it's been a breeze to use. It's entirely in GDScript, and not a lot of script at that. There's no need to make a specific C++ implementation, and in fact doing so would prove too restrictive when you wanted to make minor customizations.
At its heard is the State superclass, State.gd. Note that it's slightly specialized for my own purposes:
# Superclass (faux interface) common to all finite state machine / pushdown automata.
var player = null
# Return the unique string name of the state. Must be overridden.
# Handle any transitions into this state. Subclasses should first chain to this method.
self.player = player
# Exit the current state, enter a new one.
player.state = state
# Transition to a new animation; by default, one matching the name of the State (if it exists).
# Can be overridden without chaining.
var name = get_name()
# Handle input events.
# Update physics processing.
# Handle exit events.
For an object that makes use of this FSM, I've got the following bits of code. I won't explain them here since you're already familiar with them, but for anyone who's interested, there's a fantastic tutorial at http://gameprogrammingpatterns.com/state.html
# Initialize state.
state = Starting_State.new()
# The current state gets to intercept input events.
# Send the input to the current state if we haven't already handled it.
var new_state = state._input(event)
# Switch states but don't forward input, because presumably that event was handled.
if new_state != null:
# Let the current state handle the processing logic; also handle the changing of states.
# Update the current state; handle switching.
# Call the given function with the given arg and iterate if state changes.
func _state_loop(function, arg):
# Keep a list of old states to prevent cycles.
var old_states = 
var new_state = state.call(function, arg)
while new_state != null:
var new_state_name = new_state.get_name()
# Throw an exception if we re-enter a previously visited state this frame.
assert(old_states.find(new_state_name) == -1)
# Let our new state run this cycle since our old state ended.
new_state = state.call(function, arg)
The state loop is a bit nonstandard -- I wanted to make sure states would instantaneously advance to a final resting state when multiple conflicting conditions were received, and I wanted to make sure that there weren't any infinite loops. So far, so good.
Creating new states is dead simple. Just extend my custom State.gd class and override whatever the desired functionality is. For instance, Standing.gd:
const Backflipping = preload("res://Player/States/Backflipping.gd")
const Ducking = preload("res://Player/States/Ducking.gd")
const Falling = preload("res://Player/States/Falling.gd")
const Jumping = preload("res://Player/States/Jumping.gd")
const Walking = preload("res://Player/States/Walking.gd")
# Reset double-jumps upon landing.
player.has_extra_jump = player.can_double_jump
if player.should_jump(event) and not player.is_attacking():
# Regular Jump.
if player.is_grounded() == false:
if Input.is_action_pressed("player_duck") or player.is_roof_blocked():
if player.get_horizontal_movement() != 0 and not player.is_attacking():
I think I've got about 13 states at present, most of which ought to be present in that video. It's definitely worth the effort to set up and get comfortable with a state machine; complex behaviors are so much more elegant and the separation of code is manageable.
A snapshot of the source code for the project in the video is available here.