0 votes

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:

  1. for as long as the player holds down the jump button,
  2. 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

asked Jun 16 in Engine by Missingnull (12 points)

Please log in or register to answer this question.

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.