Sub state machines LimboAI

Godot Version

4.7/4.6.3

Question

Using the limbo ai plugin and looking at tutorials on it, many of them say I can have a sub state machine within the main one but don’t tell/show me how to set it up or tell/show me how to change between states under the main one to the sub ones and vice versa.

could some one show an example with code on how to set this concept up with my states and structure?

in the named state machines node:

class_name PlayerMovementStateMachine extends LimboHSM

@export var player : PlayerController

@export var movement : PlayerMovementComponent

## States
@onready var floor_state: LimboState = %Floor
@onready var fall_state: LimboState = %Fall
@onready var jump_state: LimboState = %Jump 
@onready var crouch_state: LimboState = %Crouch
@onready var slide_state: LimboState = %Slide
@onready var hurt_state: LimboState = %Hurt
@onready var combo_01_state: LimboState = %Combo_01
@onready var combo_02_state: LimboState = %Combo_02
@onready var combo_03_state: LimboState = %Combo_03
@onready var air_combo_02_state: LimboState = %AirCombo_02
@onready var air_combo_01_state: LimboState = %AirCombo_01
@onready var crouch_attack_state: LimboState = %CrouchAttack
@onready var melee_heavy_attack_state: LimboState = %ChargedHeavyAttack

func _ready() -> void:
	initialize(player)
	set_active(true)
	initial_state = floor_state
	
	transitions_setup()

func _process(delta: float) -> void:
	movement.coyote_timer -= delta
	movement.buffer_jump_timer -= delta



func transitions_setup() -> void:
#region /// Movement Transitions
	add_transition(floor_state, crouch_state, "CROUCHING")
	add_transition(floor_state, slide_state, "SLIDING")
	add_transition(floor_state, jump_state, "JUMPING")
	add_transition(floor_state, fall_state, "FALLING")
	add_transition(floor_state, combo_01_state, "COMBO_01")
	add_transition(floor_state, melee_heavy_attack_state, "CHARGED_HEAVY")
	add_transition(floor_state, hurt_state, "HURTING")

	add_transition(crouch_state, floor_state, "FLOOR")
	add_transition(crouch_state, crouch_attack_state, "CROUCH_ATTACK")
	add_transition(crouch_state, hurt_state, "HURTING")

	add_transition(jump_state, fall_state, "FALLING")
	add_transition(jump_state, hurt_state, "HURTING")

	add_transition(fall_state, floor_state, "FLOOR")
	add_transition(fall_state, air_combo_01_state, "AIR_COMBO_01")
	add_transition(fall_state, hurt_state, "HURTING")
	add_transition(fall_state, jump_state, "JUMPING")

	add_transition(slide_state, fall_state, "FALLING")
	add_transition(slide_state, jump_state, "JUMPING")
	add_transition(slide_state, floor_state, "FLOOR")
	add_transition(slide_state, crouch_state, "CROUCHING")
	add_transition(slide_state, hurt_state, "HURTING")
#endregion

#region /// Attack Transitions
	add_transition(combo_01_state, combo_02_state, "COMBO_02")
	add_transition(combo_02_state, combo_03_state, "COMBO_03")
	add_transition(air_combo_01_state, air_combo_02_state, "AIR_COMBO_02")

	add_transition(combo_01_state, floor_state, "FLOOR")
	add_transition(combo_01_state, fall_state, "FALLING")
	add_transition(combo_01_state, hurt_state, "HURTING")

	add_transition(combo_02_state, floor_state, "FLOOR")
	add_transition(combo_02_state, fall_state, "FALLING")
	add_transition(combo_02_state, hurt_state, "HURTING")

	add_transition(combo_03_state, floor_state, "FLOOR")
	add_transition(combo_03_state, fall_state, "FALLING")
	add_transition(combo_03_state, hurt_state, "HURTING")

	add_transition(melee_heavy_attack_state, floor_state, "FLOOR")
	add_transition(melee_heavy_attack_state, floor_state, "FALLING")
	add_transition(melee_heavy_attack_state, hurt_state, "HURTING")

	add_transition(air_combo_01_state, fall_state, "FALLING")
	add_transition(air_combo_01_state, hurt_state, "HURTING")

	add_transition(air_combo_02_state, fall_state, "FALLING")
	add_transition(air_combo_02_state, hurt_state, "HURTING")

	add_transition(crouch_attack_state, crouch_state, "CROUCHING")
	add_transition(crouch_attack_state, hurt_state, "HURTING")
#endregion

#region /// Health Based Transitions
	add_transition(hurt_state, floor_state, "FLOOR")
	add_transition(hurt_state, fall_state, "FALLING")
#endregion

Looking at the docs it appears LimboHSM inherits LimboState, so its an example of the composite pattern … the HSM is a state and so it can be a member of an HSM.

LimboHSM

  • LimboState
  • BTstate
  • LImboHSM
    • LimboState
    • LimboState
    • BTState

etc

If you use the chained notation at the end of this page

Then it should be easy to add child to the LimboHSM sub state.

hsm = LimboHSM.new() 
add_child(hsm) 

# Use chained methods and delegation to set up states: 
var idle_state := LimboState.new().named("Idle") .call_on_enter(func(): animation_player.play("idle")) .call_on_update(_idle_update) 
var move_state := LimboState.new().named("Move") .call_on_enter(func(): animation_player.play("walk")) .call_on_update(_move_update) 

var hsm_state := LimboHSM.new()


hsm_state.add_child(idle_state)

hsm.add_child(hsm_state)

hsm_state.add_child(move_state) 

hsm_state.add_transition(idle_state, 
move_state, &"movement_started") 

hsm_state.add_transition(move_state, idle_state, &"movement_ended") 

hsm.initialize(self) 

hsm.set_active(true)

Modification of the code from the second link, add an extra hsm state as a child of the root hsm, then add all the other states to the child hsm instead of the root. Maybe the transitions will need to be modified but you can code and test easily.

Solution? I dont get many posts marked as solution.

I’ve looked at the documentation before asking and it just confused me further, plus the player State machine works how it is now just want to know how to setup sub-states/sub-state machines.

The hardest thing for me when coding is that I’d be wanting a visual on the code setup and the outer structure. Whether it’s the scene tree or just setting up the node it’s self, but the answers given to others or sometimes myself be very loose and vague which doesn’t help me very much.

long story short you got to spell it out directly for I to click. I also had the same issue when looking for how to nested state machines linked to the root for the animation tree but I chose to just use the animation player because it’s something I know. I just want to know how to do it so when I want to I can just to it.

Just add another state machine as a child of the state machine.

Then you can transition to it using the same notation as transitioning to an ordinary state.