Jumping bug when touching the ground

Godot Version

4.2.2

Question

Hello everyone a few days back i had a problem with my movement which had a quick solution but after that i enter tweak phase and didn’t feel right so i keep searching for solution. Even though is not what i was searching for i found this tutorial which made me refactor my code. Everything works fine beside jumping, or more specifically landing, where even though the velocity isn’t 0 because i press the button it completely stops moving and i need to repress the button to continue moving( video here). This is my code the only difference is that i have a state machine and i tried to implement the component in it:

Player.gd

extends CharacterBody2D

@onready var state_machine: Node = $State_Machine

func _ready() -> void:
	#Initialize the state machine, passing a reference of the player to the states,
	#that way they can move and react accordingly
	state_machine.init(self)

func _unhandled_input(event: InputEvent) -> void:
	state_machine.process_input(event)

func _physics_process(delta: float) -> void:
	state_machine.process_physics(delta)

func _process(delta: float) -> void:
	state_machine.process_frame(delta)

State_machine.gd

extends Node

@export var initial_state: State
var current_state: State

@export var animations: AnimationPlayer

@export_group("Components")
@export var input_component: InputComponent
@export_subgroup("Physics")
@export var gravity_component: GravityComponent
@export var jump_component: JumpComponent
@export var movement_component: MovementComponent


#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.input_component = input_component
		child.gravity_component = gravity_component
		child.jump_component = jump_component
		child.movement_component = movement_component

	#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)
State.gd

class_name State extends Node

@export var animation_name: String

var parent: CharacterBody2D
var animations: AnimationPlayer

var input_component: InputComponent
var gravity_component: GravityComponent
var jump_component: JumpComponent
var movement_component: MovementComponent

var type: State

func enter() -> void:
	type = self
	pass

func exit() -> void:
	pass

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

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

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

Idle.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 input_component.get_jump_input():
		return jump_state
	if input_component.direction:
		return move_state
	return null

func process_physics(delta: float) -> State:
	if !parent.is_on_floor():
		return fall_state
	return null

move.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 input_component.get_jump_input():
		return jump_state
	return null

func process_physics(delta: float) -> State:
	movement_component.handle_movement(parent, input_component.direction)
	
	if not parent.velocity.x:
		return idle_state

	if !parent.is_on_floor():
		return fall_state
	return null

jump.gd

extends State

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

func enter() -> void:
	super()
	jump_component.handle_jump(parent)

func process_physics(delta: float) -> State:
	gravity_component.handle_gravity(parent, delta)
	
	movement_component.handle_movement(parent, input_component.direction)
	
	if parent.velocity.y < 0:
		return fall_state
	
	if parent.is_on_floor():
		if not input_component.direction:
			return move_state
		return idle_state
	return null
fall.gd

extends State

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

func enter() -> void:
	super()

func process_physics(delta: float) -> State:
	gravity_component.handle_gravity(parent, delta)
	
	movement_component.handle_movement(parent, input_component.direction)
	
	if parent.is_on_floor():
		if not input_component.direction:
			return move_state
		return idle_state
	return null

Components

class_name MovementComponent extends Node

@export_subgroup("Settings")
@export var speed: float = 100

@export var ground_acceleration: float = 6.0
@export var ground_deceleration: float = 8.0

@export var air_acceleration: float = 10.0
@export var air_deceleration: float = 3.0

func handle_movement(parent: CharacterBody2D, direction: float) -> void:
	var velocity_change_speed: float = 0.0
	if parent.is_on_floor():
		velocity_change_speed = ground_acceleration if direction != 0 else ground_deceleration
	else:
		velocity_change_speed = air_acceleration if direction != 0 else air_deceleration

	parent.velocity.x = move_toward(parent.velocity.x, direction * speed, velocity_change_speed)
	parent.move_and_slide()

class_name JumpComponent extends Node

@export_subgroup("Settings")
@export var jump_velocity: float =-350.0

var is_jumping: bool = false

func handle_jump(parent: CharacterBody2D) -> void:
	if parent.is_on_floor():
		parent.velocity.y = jump_velocity
	
	is_jumping = parent.velocity.y < 0 and not parent.is_on_floor()

class_name GravityComponent extends Node

@export_subgroup("Settings")
@export var gravity: int = 980

var is_falling: bool = false

func handle_gravity(parent: CharacterBody2D, delta: float) -> void:
	if not parent.is_on_floor():
		parent.velocity.y += gravity * delta 
	
	is_falling = parent.velocity.y > 0 and not parent.is_on_floor()

class_name InputComponent extends Node

var direction: float = 0.0

func _unhandled_input(event: InputEvent) -> void:
	direction = Input.get_axis("move_left", "move_right")

func get_jump_input() -> bool:
	return Input.is_action_just_pressed("jump")

Looks to me like you are going to idle state when there is movement and vice versa.

	if parent.is_on_floor():
		if not input_component.direction:
			return move_state
		return idle_state
1 Like

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