My movement feels sluggish

Godot Version

Godot 4.2.2

Question

Hello, I work on my game’s movement and i wanted to make it modular from beginning, so i try implementing a state machine. I know it will be long but i will give all the details of my code( at least the useful one).

Physics_Component.gd

class_name PhysicsComponent extends PhysicsInterface

func accelerate(parent: CharacterBody2D, delta: float) -> void:
	parent.velocity.x = move_toward(velocity.x, direction * speed, speed * acceleration)
	parent.velocity.normalized()

func decelerate(parent: CharacterBody2D, delta: float) -> void:
	parent.velocity.x = move_toward(velocity.x, 0, speed * friction)
	parent.velocity.normalized()

func jump(parent: CharacterBody2D) -> void:
	parent.velocity.y = move_toward(velocity.y, -jump_force, jump_force * jump_scale) 
	parent.velocity.normalized()
	
func apply_gravity(parent: CharacterBody2D, delta: float) -> void:
	parent.velocity.y += gravity * fall_scale * delta 
	parent.velocity.normalized()

func move(parent: CharacterBody2D) -> void:
	parent.move_and_slide()

func manage_movement(parent: CharacterBody2D, delta: float) ->void:
	direction = Input.get_axis("move_left", "move_right")
	
	if direction:
		accelerate(parent, delta)
	else:
		decelerate(parent, delta)
		
	if !is_on_floor():
		apply_gravity(parent, delta)
	
	#print(direction)
	move(parent)
	

func check_movement(event: InputEvent) -> bool:
	if event.is_action_pressed("move_left") or event.is_action_pressed("move_right"):
		return true
	return false

func check_jump(parent: CharacterBody2D, event: InputEvent) -> bool:
	if event.is_action_pressed("jump") and parent.is_on_floor():
		return true
	return false

func get_direction() -> int:
	return direction

This a component that does all the physics related calculations.

State_Machine.gd

extends Node

@export var initial_state: State
var current_state: State

@export var animations: AnimationPlayer

@export var physics: PhysicsComponent

#Initialize the state machine by giving each child state a reference to the
#parent object it belongs to and enter the default starting_state.
func init(parent: CharacterBody2D) -> void:
	for child in get_children():
		child.parent = parent
		child.animations = animations
		child.physics = physics

	#Initialize to the default state
	change_state(initial_state)

#Change to the new state by first calling any exit logic on the current state
func change_state(new_state: State) -> void:
	if current_state:
		current_state.exit()
	current_state = new_state
	current_state.enter()
	print(current_state)

#Pass through functions for the player to call,
#handling state changes as needed
func process_physics(delta: float) -> void:
	var new_state: State = current_state.process_physics(delta)
	if new_state:
		change_state(new_state)

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

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

The state machine manager if needed

Idle_state.gd

extends State

@export
var fall_state: State
@export
var jump_state: State
@export
var move_state: State


func enter() -> void:
	super()

func process_input(event: InputEvent) -> State:
	if physics.check_jump(parent, event):
		return jump_state
	if physics.check_movement(event):
		return move_state
	return null

func process_physics(delta: float) -> State:
	#physics.move(parent)
	
	if !parent.is_on_floor():
		return fall_state
	return null
Move_state.gd

extends State

@export
var fall_state: State
@export
var idle_state: State
@export
var jump_state: State

func enter() -> void:
	super()

func process_input(event: InputEvent) -> State:
	if physics.check_jump(parent, event):
		return jump_state
	return null

func process_physics(delta: float) -> State:
	physics.manage_movement(parent, delta)
	
	if !parent.velocity:
		return idle_state
	
	#parent.animations.flip_h = movement < 0
	physics.move(parent)
	
	if !parent.is_on_floor():
		return fall_state
	return null

Jump_state.gd

extends State

@export
var fall_state: State
@export
var idle_state: State
@export
var move_state: State

func enter() -> void:
	super()
	physics.jump(parent)

func process_physics(delta: float) -> State:
	if parent.velocity.y < 0:
		return fall_state
	
	#if movement != 0:
		#parent.animations.flip_h = movement < 0
	physics.manage_movement(parent, delta)
	
	if parent.is_on_floor():
		if !physics.get_direction():
			return move_state
		return idle_state
	return null

Fall_state.gd

extends State

@export
var idle_state: State
@export
var move_state: State

func enter() -> void:
	super()

func process_physics(delta: float) -> State:
	#if movement != 0:
		#parent.animations.flip_h = movement < 0
	
	physics.manage_movement(parent, delta)
	
	if parent.is_on_floor():
		if !physics.get_direction():
			return move_state
		return idle_state
	return null

And this are all the state so far.

I will leave a link to video for showing exactly what happens here.

I don’t really know what is wrong. I suspected that is from the state machine, changing from the idle to move and having a hiccup but i don’t have discovered anything related with that.
Most probably will be a stupid reason but i can not find a solution and chatgpt wasn’t very helpfull either.

Maybe I’m dumb but I can’t really tell what’s wrong based on the video :sweat:

Is it not because of the acceleration? Can’t tell because don’t know what value the acceleration variable is set to, but I guess it takes a few frames to “speed up” when you press a key and that can make it feel sluggish.

1 Like

In the right side is the acceleration variable is set to be between 0 and 1 translated to 0 to 100% of the speed value and it’s set to 0.3.
it can make sense, but sometimes even if I press multiple times consecutively it didn’t start moving, it just stop taking inputs, like is calculating something(even though I don’t have any performance issues), but I will try remove it and see what changes.

Edit: ok the acceleration removing dosent work, but i found somehing that explain the problem better.
If i press both left and right key there is no movement but when i release this happens:
The Default Character: start moving to the other side
My custom one: still not move until i release the other key and press one several time to “restart” it somehow.

You could try changing this to:

if is_zero_approx(parent.velocity):
	return idle_state

Just an idea, I don’t think that’s the issue tbh, but maybe the velocity isn’t exactly 0 due to a float rounding error and your if statement doesn’t trigger and keep it in move state even though it’s not moving.

1 Like

Nope not a difference but is either the combination between the acceleration and deceleration freaks it out or an input problem. I will try more test to see what may cause this.

You are calling normalize on the velocity! This will reset your speed to 1, you should only normalize the input, but you do through get_axis which automatically normalizes it’s output.

1 Like

That was just a thought that might resolve it but I had problem before that. From want I learned online people said that is inofensive to put a normalized but I’ll delete it and try again @Monday solution maybe it resolve something

Edit: That doesn’t worked but i tried this to check if is input related:

#direction = Input.get_axis("move_left", "move_right")
	if Input.is_action_pressed("move_left"):
		direction = -1
	elif Input.is_action_pressed("move_right"):
		direction = 1
	else:
		direction = 0

a more tribal way of doing it, but theoretically works like without the hiccups now which make me think. I made something to influence get_axis to not get my input right?

Well, using get_axis, I guess it’s normal that if you press both inputs it won’t move since the two inputs “combined” should be 0.

That doesn’t happen this way because if ‘left’ is pressed it’ll just move left without even checking ‘right’.

So that much makes sense, but I don’t know why the other weirdness happens with get_axis…

2 Likes

In this same snippet you are changing the parent’s velocity to, a maybe inherited, different velocity variable, do you mean to use parent.velocity in the move_toward so that it will build speed over time?

And you do not multiply your acceleration by delta so your movement is frame-dependent

I believe you want this line, with your acceleration adjusted to be in pixels per second units

parent.velocity.x = move_toward(parent.velocity.x, direction * speed, delta * acceleration)
2 Likes

And i known will be something stupid like this :man_facepalming:. Thank you and @Monday so much for helping.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.