As written in the title, I'm trying to implement precise movement for my playable character and I've run into a problem where my character's jump is overshooting the desired value I've set in the jump_height_pixels variable.
Every function here except ground_check is called every physics_process frame.
I've followed this tutorial for my math calculations:

Any advice would be appreciated.

Here's the code that manages the character jump behavior:

extends Node

@export_category("Dependency Injections")
@export var character_body : CharacterBody2D
@export var state_chart : StateChart
@export_category("Jump Settings")
@export var jump_height_pixels : float
@export var jump_time_to_peak_seconds : float
@export_category("Fall Settings")
@export var jump_time_to_descent_seconds : float

var jump_time_to_peak_counter_seconds : float = 0.0

@onready var jump_velocity : float = ((2.0 * jump_height_pixels) / jump_time_to_peak_seconds) * -1.0
@onready var jump_gravity : float = ((-2.0 * jump_height_pixels) / (jump_time_to_peak_seconds * jump_time_to_peak_seconds)) * -1.0
@onready var fall_gravity : float = ((-2.0 * jump_height_pixels) / (jump_time_to_descent_seconds * jump_time_to_descent_seconds)) * -1.0

func ground_check(_delta):
	if !character_body.is_on_floor():
		state_chart.send_event("on_air")
		return
	state_chart.send_event("on_ground")


func get_gravity() -> float:
	return jump_gravity if character_body.velocity.y < 0.0 else fall_gravity


func apply_gravity(delta):
	character_body.velocity.y += get_gravity() * delta


func jump_character(delta):
	var is_jump_button_held = Input.get_action_strength("jump")
	character_body.velocity.y = jump_velocity
	jump_time_to_peak_counter_seconds += delta

	if jump_time_to_peak_counter_seconds >= jump_time_to_peak_seconds:
		state_chart.send_event("jump_completed")
		jump_time_to_peak_counter_seconds = 0.0

	if is_jump_button_held == 0: #equal to false
		state_chart.send_event("jump_completed")
		jump_time_to_peak_counter_seconds = 0.0

    GodotUser111 question - what's happening in your physics_process(delta) function? I'm comparing your code to that Youtuber's GitHub example and nothing in the code is jumping at me yet (disregarding the lack of x movement in code because you're obviously focusing on the jump).

    Is apply_gravity(delta) happening in the physics process?

    Another thing - you have a variable looking for zero: is there a reason you're not using a bool? Unless you're using get_action_strength somewhere I am not seeing? I'd just change that to Input.is_action_just_pressed('jump').

      SnapCracklins

      Hello!

      The physics_process(delta) does not exist in this class. The functions instead receives a signal every physics frame that more or less works like physics_process(delta) that is being sent by another class thanks to the State Chart plugin I'm using. The functions are not always being called as there are some states where some functions are disabled to make way for other functionalities I plan for the player character.

      Yes. 'apply_gravity(delta)' receives a signal each physics frame if the current state that is active sends signals to it.

      No. No reason at all at the moment, I just thought it would look uniform with the rest of the if statements inside the function.

      I also tried using the code in the Github example on a fresh scene after reading your comment. I didn't encounter the issue so I'm left wondering if its because my jump_character function is called every physics frame as long as the player holds the jump button during the jump state. Would that require a different calculation?

      • xyz replied to this.

        GodotUser111 I didn't encounter the issue so I'm left wondering if its because my jump_character function is called every physics frame as long as the player holds the jump button during the jump state. Would that require a different calculation?

        Yes because you reset the velocity to jump_velocity as long as button is held, nullifying all the slowdown that should accumulate from gravity application. Since no one can hold an action for exactly one frame, your code will effectively hold the velocity at jump_velocity value for multiple frames at the start of jump, causing the player to go higher than expected.

        You should set velocity to jump_velocity only once when jump is initiated.

          xyz

          xyz Yes because you reset the velocity to jump_velocity as long as button is held, nullifying all the slowdown that should accumulate from gravity application. Since no one can hold an action for exactly one frame, your code will effectively hold the velocity at jump_velocity value for multiple frames at the start of jump, causing the player to go higher than expected.

          I see! I didn't realize this was the case and it should've been obvious to me with how the tutorial only applied the velocity once.

          Applying the velocity just once got the behavior working as intended. The downside is that the player now can't gauge how high their jump can be by holding the jump button but I suppose I can just make the fall gravity be heavier than usual as a workaround if the jump button is released early. I wonder what the calculations would be if I really want the velocity to be applied each frame the player holds the jump button...

          Thanks for the help everyone!