Attention | Topic was automatically imported from the old Question2Answer platform. | |
Asked By | amaou310 |
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:
_jumps_made += 1
_velocity.y = -jump_strength
elif is_double_jumping:
_jumps_made += 1
if _jumps_made <= maximum_jumps:
_velocity.y = -double_jump_strength
elif is_jump_cancelled:
_velocity.y = 0.0
elif is_idling or is_running:
_jumps_made = 0
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:
yield(owner, "ready")
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:
yield(owner, "ready")
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:
_jumps_made += 1
_velocity.y = -jump_strength
elif is_double_jumping:
_jumps_made += 1
if _jumps_made <= maximum_jumps:
_velocity.y = -double_jump_strength
elif is_jump_cancelled:
_velocity.y = 0.0
elif is_idling or is_running:
_jumps_made = 0
# 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
ramazan | 2022-07-11 09:12