#State Machine

18 messages · Page 1 of 1 (latest)

swift solstice
#

Hi so I've been following the state machine beginner guide video since I'm building my very first 2D platformer game and I wanted to reorganize the code into separate manageable bite sized pieces before it quickly gets out of control.

Anyways, focussing on the Player first i basically replicated the process of the guide all the way down until I reached creating the state class itself. I'm confused as to where I put that script/code?

It is stated it can be 'anywhere' and Godot can reach it globally etc etc or something along those lines, but that whole piece has lost me and I pretty much can't continue without it. Any help would be massive.

(I just started Godot & game development as a whole 2 days ago, followed the tutorial by Brackeys and went on to make a custom health system using my own sprite sheet i also learned how to make. So this is all very new to me apologies.)

tardy lance
#

Mine is inside my player scene. Inside the tree, I have a StateMachine node, then many state nodes as a child of that. The script is attached to statemachine, and each state has its own script attached to the applicable child.

#

For reaching something globally in godot, typically that means something like this:

in state.gd:

class_name State
extends Node```

Basically referring to the state in other scripts without referencing its file path. 

Basically put them in their own area (a states folder perhaps), and then do this at the top of the script:

Example is IdleState, but thats the name of whatever state you want.

```class_name IdleState
extends "res://player/states/State.gd"```
#

This tells godot it can be used globally, such as:

var idle_state = IdleState.new()

Instead of:

var IdleState = preload("res://player/states/IdleState.gd")

swift solstice
#

Hmm well what i have right now, is a player scene and inside that, this structure. Just sending it as a image because it's easier than writing it~
I have ofc the player script which is also made a Player class, then I have a script for the state_machine node which is this code

`
extends Node

@export
var starting_state: State

var current_state: State

Intialize the state machine by giving each child state a refernce to the

parent object it belongs to and enter the default starting_state.

func init(parent: Player) -> void:
for child in get_children():
child.parent = parent

# Intialize to the default state
change_state(starting_state)

func change_state(new_state: State) -> void:
if current_state:
current_state.exit()

current_state = new_state
current_state.enter()

Pass through functions for the Player to call,

handling state changes as needed.

func process_physics(delta: float) -> void:
var new_state = current_state.process_physics(delta)
if new_state:
change_state(new_state)

func process_input(event: InputEvent) -> void:
var new_state = current_state.process_input(event)
if new_state:
change_state(new_state)

func process_frame(delta: float) -> void:
var new_state = current_state.process_frame(delta)
if new_state:
change_state(new_state)
`

This is very carbon copy from the guide. So like that's where I am with it but the State class itself I don't know where to place it, in just another node and attached script in the tree under the player or state machine? Or just in the state_machine.gd file udner what already is there?

tardy lance
# swift solstice Hmm well what i have right now, is a player scene and inside that, this structur...

If im understanding you correctly, create a new script file called something like state.gd and define the State base class there. This keeps things modular and is especially helpful as your number of states grows.

Example: state.gd

class_name State

var parent: Player

func enter() -> void:
    pass

func exit() -> void:
    pass

func process_physics(delta: float) -> State:
    return null

func process_input(event: InputEvent) -> State:
    return null

func process_frame(delta: float) -> State:
    return null```

Then all of your other states extend off of that. The base "state.gd" doesnt need to be in scene. 

You can also technically define it inside of the statemachine script, but its messy. That would be something like this:

```class State:
    var parent: Player

    func enter(): pass
    func exit(): pass
    func process_physics(delta): return null
    func process_input(event): return null
    func process_frame(delta): return null```

Then referencing it would be:

```extends state_machine.State```
swift solstice
#

ah okay and so I basically just realised, you can make scripts within the project that aren't attached to a node within a scene etc. I didn't know that

tardy lance
#

Yes some

swift solstice
#

or at least, i assume that is the case

mortal crest
#

To cut down on code you can also use new state classes based on what they control.

For example you might have a jump_state and from that two different variants depending on whether a character can double jump or not.

#

That way the bulk of the code is handled by the base jump_state while the inherited ones just focus on the minor changes

swift solstice
#

Hmmm okay. I'm just trying to wrap my head around state machines in general. I was following this video: https://www.youtube.com/watch?v=oqFbZoA2lnU for help on creating one but overall despite it being meant for beginners I basically am left fairly confused overall.

Here's an updated look at how I like to do state machines in Godot 4. I'm going to go over some simpler, starter techniques today, and in the next video I'll go in-depth about techniques you can use when today's examples don't cut it.

Text version: https://shaggydev.com/2023/10/08/godot-4-state-machines

Sample project: https://github.com/thesh...

▶ Play video
#

I basically just want to make my code very modular and easy to expand with content without it becoming a confusing mess so i figured a state machine is one of the better ways to basically separate out logic

tardy lance
#

Then I suggest a standalone state.gd script

#

When Im home I can show you what I have.

#

But you basically have it. Just make your state.gd and pair it with state machine.

mortal crest
#

The idea behind them is fairly simple. Your player sends a signal to the state handler. The state handler relays it to the state, and the state acts on it.

The state handler would manage the switching between different states. Here's a simple example from my own code:

class_name State

var main: Node ## Parent node

func init(parent: Node) -> void:
  main = parent

func process_frame(delta: float) -> State:
  return null

func process_physics(delta: float) -> State:
  return null

func handle_input() -> State:
  return null```
The empty functions would be overridden when you create a new derived State object like so:
```extends State

@export var duck_state: State
@export var run_state: State

func process_frame(delta: float) -> State:
  if player_ducked(): return duck_state
  if player_moved(): return run_state```
swift solstice
#

Thanks guys/girls I think i understand/got a better grasp of it now