Hold to jump higher system for godot 4

Godot Version

4

Question

I need to make a hold to jump higher system but don’t know how to do it.
This is my code so far:

extends CharacterBody2D


const SPEED = 300.0
const JUMP_VELOCITY = -400.0

var coyotetime = 0
var coyote = 0.25

# Get the gravity from the project settings to be synced with RigidBody nodes.
var gravity = ProjectSettings.get_setting("physics/2d/default_gravity")
@onready var anim = get_node("AnimationPlayer")

func ready():
	get_node("AnimatedSprite2D").play("Idle")

func _physics_process(delta):
	# Add the gravity.
	if not is_on_floor():
		velocity.y += gravity * delta
		coyotetime -= delta
	
	if is_on_floor():
		coyotetime = coyote
	
	# Handle jump.
	if Input.is_action_just_pressed("up"):
		if is_on_floor() or coyotetime >= 0:
			coyotetime = 0
			velocity.y = JUMP_VELOCITY
			anim.play("Jump")

	# 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("left", "right")
	if direction == -1:
		get_node("AnimatedSprite2D").flip_h = true
	elif direction == 1:
		get_node("AnimatedSprite2D").flip_h = false
	if direction:
		velocity.x = direction * SPEED
		if velocity.y == 0:
			anim.play("Run")
	else:
		velocity.x = move_toward(velocity.x, 0, SPEED)
		if velocity.y == 0:
			anim.play("Idle")
	if velocity.y > 0:
		anim.play("Fall")
	move_and_slide()

You should put your code in Preformatted Text format

or surround it with

```gdscript
<put code here>
```


But on the jump issue itself, the tricky part is that you want to prolong the jump if button keeps getting pressed, but after you started the jump (aka registered an initial y velocity impulse).

One way to do that is to:

  1. Keep track how long you allow to “keep” jumping after you left the ground (make sure you don’t get a free double jump if you just fall). You can do so with with states, a Timer or manually counting.
  2. Build ever so slightly more jump velocity over _physics_update()s based on whether you’re still jumping. You can eventually drive the additional velocity with a function or curve based on the states or timers from last step.
1 Like

Can you put code for it?

I can try, but that’d be real nice if your example would be well formatted with correct indent.

# States would help for more advanced mechanics such as double jump, etc.
enum JumpState {READY, JUMPING, FALLING}
var current_jump_state : JumpState
var elapsed_jump_time : float = 0.0 # Can use a timer and timeout signal instead

@export var jump_duration : float = 2.0

func _handle_jumping_state(delta):
    # If you're jumping, keep counting until you can no longer jump
    if current_jump_state == JumpState.JUMPING:
        elapsed_jump_time += delta
        # Fall if jumping for too long
        if elapsed_jump_time >= jump_duration:
            current_jump_state = JumpState.FALLING

    # Input
    if Input.is_action_just_pressed("up"):
        if current_jump_state == JumpState.READY and is_on_floor():
            current_jump_state = JumpState.JUMPING
            anim.play("Jump")
    if Input.is_action_just_released("up"):
        if current_jump_state == JumpState.JUMPING:
            current_jump_state = JumpState.FALLING

    # Reset state
    if current_jump_state == JumpState.FALLING and is_on_floor():
        current_jump_state = JumpState.READY
        elapsed_jump_time = 0.0


func _physics_process(delta):
    _handle_jumping_state(delta) # First

    # Then handle movement
    match current_jump_state:
        JumpingState.READY, JumpingState.FALLING:
            velocity.y = min(velocity.y + gravity * delta, MAX_FALLING_VELOCITY)
        JumpingState.JUMPING:
            velocity.y = JUMP_VELOCITY
            # or if you want to smooth/ease jump over time
            velocity.y = JUMP_VELOCITY * ease(elapsed_jump_time/jump_duration, 3.0)
            # figure out whether you want to factor in "delta" or not there

    move_and_slide()

fixed the formatting

1 Like

I’m so sorry for asking this but do you mind making it implemented into the code I already made

It was mainly so it could fit your code with small tweaking here and there but here it goes:

extends CharacterBody2D

enum JumpState {READY, COYOTE, JUMPING, FALLING}
var current_jump_state := JumpState.READY

const SPEED := 300.0
const JUMP_VELOCITY := -400.0
const COYOTE := 0.25

@export var jump_duration : float = 2.0

var elapsed_coyote := 0.0
var elapsed_jump := 0.0

# Get the gravity from the project settings to be synced with RigidBody nodes.
var gravity = ProjectSettings.get_setting("physics/2d/default_gravity")
@onready var anim = get_node("AnimationPlayer")

func ready():
	get_node("AnimatedSprite2D").play("Idle")

func _handle_jumping_state(delta) -> void:
    # Preprocess states
    match current_jump_state:
        JumpState.READY:
            # If you've left ground, start counting coyotee
            if !is_on_floor():
                current_jump_state = JumpState.COYOTE
                elapsed_coyote += delta

        JumpState.COYOTE:
            # If you're in coyote keep counting until you fall
            elapsed_coyote += delta
            if elapsed_coyote >= COYOTE:
                current_jump_state = JumpState.FALLING

        JumpState.JUMPING:
            # If you're jumping, keep counting until you can no longer jump
            elapsed_jump += delta
            if elapsed_jump >= jump_duration:
                current_jump_state = JumpState.FALLING


    # Input
    if Input.is_action_just_pressed("up"):
        var ready_to_jump : bool = current_jump_state == JumpState.READY and is_on_floor()
        var still_on_coyote_time : bool = current_jump_state == JumpState.COYOTE
        if  ready_to_jump or still_on_coyote_time:
            current_jump_state = JumpState.JUMPING
            anim.play("Jump")
    if Input.is_action_just_released("up"):
        if current_jump_state == JumpState.JUMPING:
            current_jump_state = JumpState.FALLING
            anim.play("Fall")

    # Reset state
    if current_jump_state == JumpState.FALLING and is_on_floor():
        current_jump_state = JumpState.READY
        elapsed_jump = 0.0
        elapsed_coyotee = 0.0

func _physics_process(delta):
	_handle_jumping_state(delta)

    # Manage vertical velocity
    match current_jump_state:
        JumpingState.READY, JumpingState.FALLING:
            velocity.y = min(velocity.y + gravity * delta, MAX_FALLING_VELOCITY)
        JumpingState.JUMPING:
            velocity.y = JUMP_VELOCITY
            # or if you want to smooth/ease jump over time
            velocity.y = JUMP_VELOCITY * ease(elapsed_jump/jump_duration, 3.0)
            # figure out whether you want to factor in "delta" or not there

	# 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("left", "right")
	if direction == -1:
		get_node("AnimatedSprite2D").flip_h = true
	elif direction == 1:
		get_node("AnimatedSprite2D").flip_h = false
	if direction:
		velocity.x = direction * SPEED
		if velocity.y == 0:
			anim.play("Run")
	else:
		velocity.x = move_toward(velocity.x, 0, SPEED)
		if velocity.y == 0:
			anim.play("Idle")

	move_and_slide()

I really feel bad for saying this but there are some errors with this. I fixed the indentations but the velocity.y = JUMP_VELOCITY * ease(elapsed_jump/jump_duration, 3.0) has a error so do I just delete or can you fix it

It also says that current_jump_state is not declared. Same with jump duration

I forgot to declare the top level variables, should be fixed now.

But I can’t encourage you enough to try and make sense of the code, I know it can be disorienting at first glance, but building that understanding will allow you to conveniently make the adjustments yourself.

1 Like

I know what the code does its just hard for me to find the right command

A third way to do it is to multiply Y velocity by a factor if it’s negative (i.e. player is going upwards) when the player releases the jump key. This is what the 2D platformer demo does. It’s not the most “correct” approach but it’s quite straightforward to implement.

func _input(event):
    # ...
    var is_jump_interrupted = Input.is_action_just_released("jump") and velocity.y < 0.0
    if is_jump_interrupted:
        # Decrease the Y velocity by multiplying it, but don't set it to 0
        # as to not be too abrupt.
        velocity.y *= 0.6
1 Like

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