# How to implement player State Machine

Attention Topic was automatically imported from the old Question2Answer platform.

Hello community. I am very new to godot, and making 2d action platformer. I am trying to implement player state machine by following Finite State Machine in Godot by Nathan Lovato from GDQuest. However I am not sure how exactly I should write my code. I would really appreciate if someone could teach me how implement player state machine.

Current Player script. Player can move right and left, jump, and double jump.

``````extends KinematicBody2D

const UP_DIRECTION := Vector2.UP

export var can_move = true

export var speed := 200.0
export var jump_strength := 450
export var maximum_jumps := 2
export var double_jump_strength := 400
export var gravity := 1200

var _jumps_made := 0
var _velocity := Vector2.ZERO

onready var position2D = \$Position2D
onready var _animation_player: AnimationPlayer = \$Position2D/PlayerSkinIK/AnimationPlayer

func _physics_process(delta: float) -> void:

#Left and Right Movement Direction
var _horizontal_direction = (
Input.get_action_strength("move_right")
- Input.get_action_strength("move_left")
)

#X and Y velocity
_velocity.x = _horizontal_direction * speed
_velocity.y += gravity * delta

#Player State
var is_falling := _velocity.y > 0.0 and not is_on_floor()
var is_jumping := Input.is_action_just_pressed("jump") and is_on_floor()
var is_double_jumping := Input.is_action_just_pressed("jump") and is_falling
var is_jump_cancelled := Input.is_action_just_released("jump") and _velocity.y < 0.0
var is_idling := is_on_floor() and is_zero_approx(_velocity.x)
var is_running := is_on_floor() and not is_zero_approx(_velocity.x)

#Jump Counter
if is_jumping:
_velocity.y = -jump_strength
elif is_double_jumping:
if _jumps_made <= maximum_jumps:
_velocity.y = -double_jump_strength
elif is_jump_cancelled:
_velocity.y = 0.0
elif is_idling or is_running:

if (can_move == true):
#Velocity Calculation
_velocity = move_and_slide(_velocity, UP_DIRECTION)

#Flip Sprite
if get_global_mouse_position().x > \$Position2D/PlayerSkinIK.global_position.x:

position2D.scale.x=1
else:

position2D.scale.x=-1

#Flip Sprite for Walking Animation
if position2D.scale.x==1:

if Input.is_action_pressed("move_right"):
print("forward1")
if is_on_floor():
_animation_player.play("Player-Run IK")
elif Input.is_action_pressed("move_left"):
print("backward1")
if is_on_floor():
_animation_player.play("Player-Run IK Backward")

elif position2D.scale.x==-1:

if Input.is_action_pressed("move_left"):
print("forward2")
if is_on_floor():
_animation_player.play("Player-Run IK")
elif Input.is_action_pressed("move_right"):
print("backward2")
if is_on_floor():
_animation_player.play("Player-Run IK")

#Animation Control
if is_jumping or is_double_jumping:
_animation_player.play("Player-Jump IK")
elif is_falling:
_animation_player.play("Player-Fall IK")
elif is_idling:
_animation_player.play("Player-Idle IK")
``````

And these are scripts for the state machine:

State:

``````extends Node

class_name State

var state_machine = null

func handle_input(_event: InputEvent) -> void:
pass

func update(_delta: float) -> void:
pass

func physics_update(_delta: float) -> void:
pass

func enter(_msg := {}) -> void:
pass

func exit() -> void:
pass
``````

State Machine:

``````extends Node

class_name StateMachine

signal transitioned(state_name)

export var initial_state := NodePath()

onready var state: State = get_node(initial_state)

func _ready() -> void:

for child in get_children():
child.state_machine = self
state.enter()

func _unhandled_input(event: InputEvent) -> void:
state.handle_input(event)

func _process(delta:float) -> void:
state.update(delta)

func _physics_process(delta: float) -> void:
state.physics_update(delta)

func transition_to(target_state_name: String, msg: Dictionary = {}) -> void:
if not has_node(target_state_name):
return

state.exit()
state = get_node(target_state_name)
state.enter(msg)
emit_signal("transitioned", state.name)
``````

PlayerState:

``````extends "res://Scripts/State Machine Attempt 2/States.gd"

class_name PlayerState

var player: Player

func _ready() -> void:

player = owner as Player

assert(player != null)
``````

Player:

``````extends KinematicBody2D

class_name Player

enum States {ON_GROUND, IN_AIR}

var _state : int = States.ON_GROUND

const UP_DIRECTION := Vector2.UP

export var speed := 200.0
export var jump_strength := 450
export var maximum_jumps := 2
export var double_jump_strength := 400
export var gravity := 1200

var _jumps_made := 0
var _velocity := Vector2.ZERO

func _physics_process(delta: float) -> void:
var is_jumping: bool = _state == States.ON_GROUND and Input.is_action_just_pressed("jump")
var is_falling : bool = _state == States.IN_AIR and _velocity.y > 0.0
var is_double_jumping : bool = _state == States.IN_AIR and Input.is_action_just_pressed("jump") and is_falling
var is_jump_cancelled : bool = _state == States.ON_GROUND and States.IN_AIR and Input.is_action_just_released("jump") and _velocity.y < 0.0

if is_jumping:
_velocity.y = -jump_strength
elif is_double_jumping:
if _jumps_made <= maximum_jumps:
_velocity.y = -double_jump_strength
elif is_jump_cancelled:
_velocity.y = 0.0
elif is_idling or is_running:

# Moving the character.
_velocity = move_and_slide(_velocity, UP_DIRECTION)

if is_on_floor():
_state = States.ON_GROUND

func change_state(new_state: int) -> void:
var previous_state := _state
_state = new_state

# Initialize the new state.
match _state:
States.IN_AIR:
_speed = air_speed
States.ON_GROUND:
_speed = ground_speed
``````

Air:

``````extends "res://Scripts/State Machine Attempt 2/PlayerState.gd"

func enter(msg := {}) -> void:
if msg.has("do_jump"):
player.velocity.y = -player.jump_impulse

func physics_update(delta: float) -> void:

var input_direction_x: float = (
Input.get_action_strength("move_right")
- Input.get_action_strength("move_left")
)
player.velocity.x = player.speed * input_direction_x

player.velocity.y += player.gravity * delta
player.velocity = player.move_and_slide(player.velocity, Vector2.UP)

if player.is_on_floor():
if is_equal_approx(player.velocity.x, 0.0):
state_machine.transition_to("Idle")
else:
state_machine.transition_to("Run")
``````

Idle:

``````extends "res://Scripts/State Machine Attempt 2/PlayerState.gd"

func enter(_msg := {}) -> void:
owner.velocity = Vector2.ZERO

func update(delta: float) -> void:
if not player.is_on_floor():
state_machine.transition_to("Air")
return

if Input.is_action_just_pressed("jump"):
state_machine.transition_to("Air",{do_jump = true})
elif Input.is_action_pressed("move_left") or Input.is_action_pressed("move_right"):
state_machine.transiton_to("Run")
``````

Move:

``````extends PlayerState

class_name RunState

func _physics_update(delta: float) -> void:

if not player.is_on_floor():
state_machine.transition_to("Air")
return

var _horizontal_direction = (
Input.get_action_strength("move_right")
- Input.get_action_strength("move_left")
)

_velocity.x = _horizontal_direction * speed
_velocity.y += gravity * delta

_velocity = player.move_and_slide(_velocity, UP_DIRECTION)

if Input.is_action_just_pressed("jump"):
state_machine.transition_to("Air", {do_jump = true})
elif is_equal_approx(input_direction_x, 0.0):
state_machine.transition_to("Idle")
``````

I have that same problem.

if you want take a look at this

Godot Behaviour Tree

ramazan | 2022-07-11 09:12