This one is pretty nasty but I believe I can explain it clearly.
Picture a standard 2D platformer. I want to make the character jump when I press a specific button, triggering the relevant action as defined in the input map.
If that was all, I could simply write something like this:
func _input(event):
if event.is_action_pressed("player_jump"):
# jump
The event fires once and the action is executed once. Job's done.
Instead, I want to make it so that:
- for as long as the player holds down the jump button,
- the character will jump as soon as it meets another condition: that it is colliding with the floor. (and thus, if the jump key has not been released, once the character falls back on the floor he should jump again in an infinite cycle.)
My issue is not with registering the input, be it via _input(event)
or the Input
singleton. It is also not with ensuring that the second condition is met. The collision is fine.
My issue is with translating that input, either directly or through the management of booleans, into a single "jump" per frame.
I've tried several approaches: through an event detector inside _input(event)
, detecting it through the Input
singleton inside the _process
and even _fixed_process
function, and I keep running into the same issue: once its time to trigger the actual jump, it ends up triggering it multiple times, which leads to other issues.
I have looked at so many similar questions but not a single one of them is about quite the same problem.
Thank you in advance. Here's the relevant bits of the code. This script is inside the player's character.
extends RigidBody2D
# collision_detection
var is_colliding_with_world = false
func _ready():
# starts processes
set_process_input(true)
set_process(true)
set_physics_process(true)
# starts collision detection
set_contact_monitor(true)
set_max_contacts_reported(10)
# the player character will remain upright
set_mode(2)
func _process(delta): # runs every frame
# Collision detector (the first check is unnecessary)
if get_colliding_bodies().empty() == false: # If colliding with at least one body..
if get_colliding_bodies().back().get_groups().has("world") == true: # Which is part of the world:
is_colliding_with_world = true
else:
is_colliding_with_world = false
else:
is_colliding_with_world = false
# Check if colliding with world
if is_colliding_with_world == true:
if Input.is_action_pressed("player_jump"):
set_axis_velocity(Vector2(0,-jump_height))
print("player jumped!)
$Jump.play()
func _input(event):
# other stuff that's irrelevant
So to be clear, if I execute this script and press the key which triggers player_jump
while is_colliding_with_world == true
it will print("player jumped!")
about 9 times or so, no matter how fast I press it, which leads me to believe that all of those prints are happening in the same frame.
The core of my issue is that I can't find a way to ensure it only triggers once per frame while maintaining the functionality of:
1. for as long as the jump key is being pressed,
2. then as soon as is_colliding_with_ground
is true the player will jump.
It looks simple but it's so cursed lol
Godot 3.1.1 btw