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:
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.
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.
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()
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
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.
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