uberflut
uberflut

Reputation: 150

Godot - Input.is_action_just_pressed() runs twice

So I have my Q and E set to control a Camera that is fixed in 8 directions. The problem is when I call Input.is_action_just_pressed() it sets true two times, so it does its content twice.

This is what it does with the counter:

0 0 0 0 1 1 2 2 2 2

How can I fix thix?

if Input.is_action_just_pressed("camera_right", true):
    if cardinal_count < cardinal_index.size() - 1:
        cardinal_count += 1
    else:
         cardinal_count = 0
    emit_signal("cardinal_count_changed", cardinal_count)

Upvotes: 5

Views: 11320

Answers (4)

kakeru
kakeru

Reputation: 21

In addition to Oliver's answer (I wanted to just comment on that answer, but I don't have enough rep for that, and this comment is way too long anyway): I'd say it is actually not a good practice to handle player's input in _physics_process().

Reason 1: input handling and moving things are 2 completely different systems.

You shouldn't mix them. While in tutorials they actually are mixed (e.g. if Input.is_action_pressed("move_right"): /*logic to move the body right*/), this is not a good practice for production even for simple games. My colleague told my an example how this is made in the first Quake: there is a structure in which you put "wish-direction of movement" and other desirable actions for the player / enemy / etc depending on input made. However, the actual decision of whether to move and where to move is made in a different place.

You can easily recall situations like player is stunned while they try to move to a side. Obviously, you won't move the player in this case. A naive attempt to address this might look like this:

if Inpus.is_action_pressed("move_right") and !is_player_stunned:
    # move body right

but then you will quickly find yourself in the hell of if-noodles: what if the player is stunned, and slowed, and has the controls reversed, and also 'snapped' to a wall by a big nail which does not let them move too far away from snap point... I hope it's now obvious that handling player inputs and making decisions based on them in a single place should not be a default.

If I were you I would move input handling into _process(), while leaving the rest physics-heavy things in _physics_process(). While in _process()? Described further, read on...

Reason 2: multiple physics frames per single render frame

I don't have a proof handy, but the situation is as follows. Physics simulation is very sensitive to changes in delta time, so it happens in fixed FPS (e.g. 60 or 120, configurable). However, if your game is not well-optimized or just heavy and runs in 30 FPS (render frames), the physics simulation will still run in 60 fps, resulting in 2 physics frames per render frame. Therefore, you potentially may handle the input twice per 1 visible frame (render frame). This is not absolutely bad, but this is subject to potentially weird behavior. Players may notice something weird in some scenarios, because they don't care and don't know about physics fps, they only see "render" fps and they will get 2 actions in 1 render frame which is weird.

Reason 3: less than 1 physics frame per single render frame

Similar situation, but vice versa: you have physics fps fixed to 60, but your game runs 1200 (render) FPS. In this example there are 20 render frames per 1 physics frame. This means that you don't immediately handle the players input when it's there, but rather delaying until the next physics frame. This may feel unresponsive (this, actually, something I faced in my own game, when testers complained that hits / punches don't count where they were made, but rather "offset" or delayed).

Usually people limit render fps to reasonable amounts anyway, so maybe the effect won't be critical or noticeable, but still.

Extra tip: you will bufferize player input and 'conditions'

For the sake of game responsiveness, you will come to the idea of bufferizing player input as well as conditions, when this input actually triggers action. A plain example is a jump in a simple platformer game. Player can jump when SPACE is pressed, but only if player is staying on the ground. If you don't bufferize input, you will make your players angry in situations when the character is about to land after a jump, but has not yet landed, and the player presses SPACE. Nothing happens, because, technically, the character is 1 millimeter away from the ground, so does not touch it, but this is too picky for players. So you bufferize input and "keep" the SPACE pressed for, say, 100ms after it was actually pressed. Now your players are happy, because they can jump infinitely - without precisely frame-matching the landing moment.

However, there's another case - when the characters steps off the platform. technically, they are off the platform and cannot jump, but this is usually also too punishing for players, so devs usually bufferize this condition as well: you "keep" this "is_on_platform" var or whatever true for, say, 100ms - and viola, now your players can jump even if they have missed the "end" or "edge" of the platform and hit SPACE a little bit later.

Summary

So, let's sum up:

  • Input handling should happen as soon as possible to ensure game responsiveness. Hence, it should happen in either _process() or _*_input() methods depending on your needs, but not in _physics_process(), because it runs in fixed FPS (=is not responsive to actual FPS at which the game is able to run)
  • Input handling and the actual logic (e.g. movement) should not necessarily happen in the same place. Otherwise you will get unreadable code soon.

Upvotes: 2

Oliver Nicholls
Oliver Nicholls

Reputation: 1431

I've encountered the same issue and in my case it was down to the fact that my scene (the one containing the Input.is_action_just_pressed check) was placed in the scene tree, and was also autoloaded, which meant that the input was picked up from both locations and executed twice.

I took it out as an autoload and Input.is_action_just_pressed is now triggered once per input.

Upvotes: 1

Theraot
Theraot

Reputation: 40295

On _process or _physics_process

Your code should work correctly - without reporting twice - if it is running in _process or _physics_process.

This is because is_action_just_pressed will return if the action was pressed in the current frame. By default that means graphics frame, but the method actually detect if it is being called in the physics frame or graphic frame, as you can see in its source code. And by design you only get one call of _process per graphics frame, and one call of _physics_process per physics frame.


On _input

However, if you are running the code in _input, remember you will get a call of _input for every input event. And there can be multiple in a single frame. Thus, there can be multiple calls of _input where is_action_just_pressed. That is because they are in the same frame (graphics frame, which is the default).


Now, let us look at the proposed solution (from comments):

if event is InputEventKey:
    if Input.is_action_just_pressed("camera_right", true) and not event.is_echo():
        # whatever
        pass

It is testing if the "camera_right" action was pressed in the current graphics frame. But it could be a different input event that one being currently processed (event).

Thus, you can fool this code. Press the key configured to "camera_right" and something else at the same time (or fast enough to be in the same frame), and the execution will enter there twice. Which is what we are trying to avoid.

To avoid it correctly, you need to check that the current event is the action you are interested in. Which you can do with event.is_action("camera_right"). Now, you have a choice. You can do this:

if event.is_action("camera_right") and event.is_pressed() and not event.is_echo():
    # whatever
    pass

Which is what I would suggest. It checks that it is the correct action, that it is a press (not a release) event, and it is not an echo (which are form keyboard repetition).

Or you could do this:

if event.is_action("camera_right") and Input.is_action_just_pressed("camera_right", true) and not event.is_echo():
    # whatever
    pass

Which I'm not suggesting because: first, it is longer; and second, is_action_just_pressed is really not meant to be used in _input. Since is_action_just_pressed is tied to the concept of a frame. The design of is_action_just_pressed is intended to work with _process or _physics_process, NOT _input.

Upvotes: 14

uberflut
uberflut

Reputation: 150

So, apparently theres a built in method for echo detection: is_echo()

Im closing this.

Upvotes: 0

Related Questions