Godot Version
4.5.1
Question
Hi all, this is my first Godot project (mainly for training purpose).
-
I have my player (CharacterBody2D) implemented using state machine using this tutorial
- Below is my node structure
-
I have my moving platform (AnimatableBody2D) implemented using path2D using this tutorial
- Below is my node structure
Both my player and platform has no error when running (no script syntax errors) but when standing on a moving platform in idle state (no input detected), the player auto slide / bounce on the platform which lead to weird movement.
I have the sample video of this bug on Google drive below:
- When the player stand on the platform, I stopped all the input, the player is in idle state (confirmed via console output) yet the player keep sliding left / right with the platform moving horizontally, or bouncing (switching between idle / fall stare) continuously with platform moving vertically
https://drive.google.com/file/d/1Q_RGjqXM-kvbsDQRnkiIlzz14QuF9HDp/view?usp=sharing
This bug has had me stalled for 2 weeks, somehow I tried to narrow down the cause (which I hope it’s correct).
For testing purpose, on the same moving platform nodes, I tried to clone my player → delete all state machine node → Apply this basic movement script then it WORKED (player standing still on moving platform, no weird sliding / bouncing). BUT when I inserted state machine and using this script as the stating state, the same bug happened.
extends CharacterBody2D
const SPEED = 100.0
const JUMP_VELOCITY = -400.0
func _physics_process(delta):
# Add the gravity.
if not is_on_floor():
velocity += get_gravity() * delta
# Handle jump.
if Input.is_action_just_pressed("ui_accept") and is_on_floor():
velocity.y = JUMP_VELOCITY
# Get the input direction and handle the movement/deceleration.
# As good practice, you should replace UI actions with custom gameplay actions.
var direction = Input.get_axis("ui_left", "ui_right")
if direction:
velocity.x = direction * SPEED
else:
velocity.x = move_toward(velocity.x, 0, SPEED)
move_and_slide()
So I GUESS that the state machine maybe is the problem, but no matter what I fix the problem still exist…
Below are my related scripts (the one that currently using state machine, not the testing one):
- Player (calling state machine)
extends CharacterBody2D class_name Player @onready var game_manager = %GameManager @onready var state_machine_action = $State_machine/State_machine_action @onready var animation_player_action = $Animation_player/AnimationPlayer_action @onready var animation_player_status = $Animation_player/AnimationPlayer_status @onready var audio_stream_player_2d = $AudioStreamPlayer2D @onready var sprite = $Sprite @onready var bullet_scene = preload("res://scenes/bullet.tscn") @onready var bullet_shoot_position = $Sprite/Bullet_shoot_position @export var power_multipler: float # Assign the Player node itself to state machine func _ready(): state_machine_action.init(self, animation_player_action, audio_stream_player_2d) func _process(delta): state_machine_action._process(delta) func _physics_process(delta): state_machine_action._physics_process(delta) func _shoot_bullet(): var new_bullet = bullet_scene.instantiate() var shooting_direction = 1 #facing right by default shooting_direction = -1 if sprite.scale.x == -1 else 1 # Shoot in the opposite direction when wall sliding if is_on_wall_only(): shooting_direction = get_wall_normal().x new_bullet.direction = Vector2(shooting_direction, 0) new_bullet.position = bullet_shoot_position.global_position get_parent().add_child(new_bullet) new_bullet.get_node("AudioStreamPlayer2D").play() func _air_slash(): if state_machine_action.Current_state.name == "Jump" or state_machine_action.Current_state.name == "Fall": animation_player_action.play("slash_3") # List of interacting item func _on_interactive_scanner_found_interactable(target_node): if target_node is coin: game_manager._add_coin()
- State machine (calling each state)
extends Node
class_name State_machine
@export var Starting_state: State
var Current_state: State
func init(controlled_player: CharacterBody2D, animation_player: AnimationPlayer, audio_player: AudioStreamPlayer2D):
# Assign all the child node (all states) to the Player
for child in get_children():
if child is State:
child.controlled_player = controlled_player
child.animation_player = animation_player
child.audio_player = audio_player
# Start the initial state
_change_state(Starting_state)
func _change_state(new_state: State):
if Current_state != null:
Current_state.exit()
Current_state = new_state
Current_state.enter()
func _process(delta):
var new_state = Current_state._state_process(delta)
if new_state != null:
_change_state(new_state)
func _unhandled_input(event):
var new_state = Current_state._state_unhandled_input(event)
if new_state != null:
_change_state(new_state)
func _physics_process(delta):
var new_state = Current_state._state_physics_process(delta)
if new_state != null:
_change_state(new_state)
- base State
extends Node
class_name State
@export var animation_name: String
@export var sound_effect: String
@export var duration_timer: float
var controlled_player: CharacterBody2D
var gravity = ProjectSettings.get_setting("physics/2d/default_gravity")
var animation_player
var audio_player
func enter():
#Played the desired animation
animation_player.play(animation_name)
#Played the desired audio track
_audio_handler()
print("ENTERING STATE: ", self.name, " | animation: ", animation_name)
func _audio_handler():
if sound_effect != "":
var path = "res://assets/sounds/%s.wav" % sound_effect
var audio_stream = load(path) as AudioStream
if audio_stream != null:
audio_player.stream = audio_stream
audio_player.play()
else:
print("failed to load audio at: " + path)
func exit():
pass
func _state_physics_process(delta: float) -> State:
return null
func _state_process(delta: float) -> State:
return null
func _state_unhandled_input(event: InputEvent) -> State:
return null
func _cooldown_ended(timer: float) -> bool:
if timer <= 0:
return true
else:
return false
- Idle State (extend from State)
extends State #IDLE STATE #List of available state @export_category("Available states") @export var Move_state: State @export var Dash_state: State @export var Rising_dash_state: State @export var Jump_state: State @export var Fall_state: State @export var Slash_1_state: State @onready var air_dash = $"../Air_dash" func enter(): super() controlled_player.velocity.x = 0 air_dash.current_air_dash_amount = air_dash.max_air_dash_amount func _state_physics_process(delta) -> State: if not controlled_player.is_on_floor(): return Fall_state controlled_player.move_and_slide() return null func _state_unhandled_input(event: InputEvent) -> State: if Input .is_action_just_pressed("shoot"): controlled_player._shoot_bullet() if controlled_player.is_on_floor(): if Input.is_action_pressed("move_left") or Input.is_action_pressed("move_right"): return Move_state if Input.is_action_pressed("up") and Input.is_action_pressed("melee"): return Rising_dash_state if Input.is_action_just_pressed("jump"): return Jump_state if Input.is_action_just_pressed("dash"): return Dash_state if Input .is_action_just_pressed("melee"): return Slash_1_state return null
Even adding platform_velocity to player velocity won’t fixed it so currently I’m stuck
. I hope that this is not a Godot bug but just my skill issue.
Thanks in advance.

