Trouble with state machines

Godot Version

4.3

Question

i followed this tutorial and made a state machine. now however, i want to change the state from a parent node. I’ve tried state.Transition.emit(true, “follow”) and all that but nothing is working

Can you paste your script? Videos are really bad for sifting through. For example scrubbing through that video I do not see signal Transition defined, though they use Transition.emit(self, "idle")

this is set up to either follow the player or have free cam

image

state machine node

extends Node

@export var initial_state : State

var current_state : State
var states : Dictionary = {}

func _ready():
	
	for child in get_children():
		if child is State:
			states[child.name.to_lower()] = child
			child.Transitioned.connect(on_child_transition)
	if initial_state:
		initial_state.Enter()
		current_state = initial_state
	
	
func _process(delta: float):
	print(current_state)
	if current_state:
		current_state.Update(delta)
func _physics_process(delta: float):
	if current_state:
		current_state.Update(delta)
func on_child_transition(state, new_state_name):
	if state != current_state:
		return
	
	var new_state = states.get(new_state_name.to_lower())
	if !new_state:
		return
		
	if current_state:
		current_state.Exit()
	new_state.Enter()
	
	current_state = new_state

follow state

extends State
class_name cameraFollow

@export var camera: Camera2D

func Enter():
	follow()
	camera.targetZoom = Vector2(2, 2)

func follow():
	if camera.target != "":
		var targetPos = Global.NODE_WORLD.get_node(camera.target).position
		camera.position += (targetPos - camera.position) / camera.smooth
	
	

func Update(delta):
	follow()

free state

class_name cameraFree

@export var camera: Camera2D
var dragVel = Vector2(0, 0)
var oldMousePos = Vector2(0, 0)
var termDragVel = Vector2(10, 10)
func Enter():
	camera.targetZoom = Vector2(4, 4)
	

func Update(delta: float) -> void:
	var mousePos = camera.get_viewport().get_mouse_position()
	if Input.is_action_pressed("lClick"):
		dragVel -= dragVel / 10
		if oldMousePos != mousePos:
			dragVel = (oldMousePos - mousePos)/2
		camera.position += (oldMousePos - mousePos)/camera.zoom
	else:
		camera.position += dragVel
		dragVel -= dragVel / 10
		if abs(dragVel.x) > termDragVel.x:
			dragVel.x = termDragVel.x * sign(dragVel.x)
		if abs(dragVel.y) > termDragVel.y:
			dragVel.y = termDragVel.y * sign(dragVel.y)
	oldMousePos = mousePos

Again I don’t see a signal Transition defined, I would bet it’s part of your base State script. Eitherway This line is probably what prevents you from changing states and does nothing.

Your first argument true is not a state or node, so it will return early, doing nothing. I believe you do not want this check at all so it may be better to remove the state argument and the if statement that follows.

i forgot to meantion there is a state script that has

extends Node
class_name State

signal Transitioned

func Enter():
	pass
func Exit():
	pass
func Update(_delta: float):
	pass
func Physics_Update(_delta: float):
	pass

inside it. however I think this problem might be too specific for anyone to answer, i gave the code to chat gpt and it told me to put

func change_state(new_state: State):
	if current_state:
		current_state.Exit()
	current_state = new_state
	if current_state:
		current_state.Enter()

inside of the state machine node and then call

var new_state = $StateMachineNode.states["camera_follow"]
$StateMachineNode.change_state(new_state)

in another node. sorry to waste your time but ill give you the solution mark anyway

You do already have that code in your script.

Is in the state machine script here:

The latter just has more checks, and the functional part is simpler. You could use

$StateMachineNode.on_child_transition($StateMachineNode.current_state, "camera_follow")

But that’s why I’m saying the function is made with too many silly checks. I would remove all these commented lines

func on_child_transition(new_state_name: String): # removed 'state' argument.
	#if state != current_state:
		#return
	
	var new_state = states.get(new_state_name.to_lower())
	#if !new_state: # allow it to error or use an assert
		#return
		
	if current_state:
		current_state.Exit()
	new_state.Enter()
	
	current_state = new_state

And now you can use

$StateMachineNode.on_child_transition("camera_follow")