godot Input fires twice, when combined with signal. - Cplusplus

3.1 Beta Windows 10 Pro - Version 1803 (OS Build 17134.472)

Issue description:

While creating a function to spawn building in my game, I found that two where spawned whenever I moved the mouse. After investigating I found that the problem was caused by using a Input check inside the signal function. Apparently the signal executes multiple times, before the Input stops.

func _on_StaticBody_input_event(camera, event, click_position, click_normal, shape_idx):

    if Input.is_action_just_released("Build_Select") :
        PlaceBuilding(RoundV)

To try and fix this I used a bool approach where the code Input is used inside _process(delta), this didn't work and was a horrible idea in the first place.

Finally after consulting the manual and searching for a solution I found something that kinda works:

func _on_StaticBody_input_event(camera, event, click_position, click_normal, shape_idx):

    #Works
    if event is InputEventMouseButton :
        if Input.is_action_just_released("Build_Select") :
            PlaceBuilding(RoundV)

    #Doesn't work, probably because the signal is from a collision object
    if event is InputEventKey :
        if Input.is_action_just_released("Build_Select") :
            PlaceBuilding(RoundV)

The problem is that it only fixes the problem for mouse inputs. With the key input nothing happens.

Now for the request:

    if event is InputEventAction :
        if Input.is_action_just_released("Build_Select") :
            PlaceBuilding(RoundV)

Ideally I would like if we could just check if the defined Action is being used, so it doesn't have to look exactly like that.

I did try a ray cast, this caused worse problems. Because to calculate where the ray should be casting I had to use a Input, and it looks like Godot can only solve one input at a time. When ever the mouse moved I couldn't place anything.

Am I missing something, how should this work in Godot?

Minimal reproduction project: MGM_InputFiresTwice.zip

Asked Oct 15 '21 16:10
avatar MysteryGM
MysteryGM

6 Answer:

You are not using the input system properly.

You have two solutions: - Either you want to check input inside of an event handler (typically _input(event)), in that case you must check whether or not the event is an action or not:

func _input(event):
    if event.is_action_released("my_action"):
         # Do something
  • Or you want to check outside of an event handler function (typically in _process(delta)), in that case, you should use the Input singleton:
func _process(delta):
    if Input.is_action_just_released("my_action"):
         # Do something

Usually, you use the first one to check actions that are triggered at a given instant, like a menu validation, and the second one to check for long-time actions, like push the acceleration button.

1
Answered Jan 15 '19 at 09:59
avatar  of groud
groud

First, thanks for the response.

Usually, you use the first one to check actions that are triggered at a given instant

I eventually figured this out, although I don't know if what I am doing now is even correct. Basically at the moment I have a variable called Position and it works like this:

extends Spatial

export (PackedScene) var BuildingToSpawn = null
var BuildPosition = Vector3.ZERO


func PlaceBuilding():
    #Spawn the selected building
    if BuildingToSpawn != null :
        BuildingToSpawn = BuildingToSpawn as PackedScene #redundant but I need null
        var NewInstance = BuildingToSpawn.instance() as Spatial
        NewInstance.transform.origin = BuildPosition #Global

        self.add_child(NewInstance)

    #If it failled place a temp error marker, Reloading the game will try to fix it
    else:
        var NewInstance = $ErrorPointer.duplicate()
        NewInstance.DebugType = "Building" #What should the error pretend to be?
        NewInstance.transform.origin = BuildPosition #Global

        self.add_child(NewInstance)


func _on_StaticBody_input_event(camera, event, click_position, click_normal, shape_idx):

    var RoundV = Vector3(floor(click_position.x),floor(click_position.y),floor(click_position.z))
    $Cursor.transform.origin = RoundV

    BuildPosition = RoundV #This is the only value allowed to feed the Global


func _process(delta):

    if Input.is_action_just_released("Build_Select") :
        PlaceBuilding()

I dislike it a bit, because BuildPosition is a Global inside the script, breaking the encapsulated design, but I see no other way of passing the variable to _process(delta) without it.

I looked around and see no clear examples of how signals and inputs should be used together. The documents explains each on there own.

1
Answered Jan 15 '19 at 15:10
avatar  of MysteryGM
MysteryGM

I looked around and see no clear examples of how signals and inputs should be used together. The documents explains each on there own.

Your problem is not really clear. But as you can see on your signal callback, you have an event variable passed. You can probably do something like event.is_action_released("name") to check the event for an action.

I dislike it a bit, because BuildPosition is a Global inside the script, breaking the encapsulated design.

You have to use such kind of variables. Godot does not provide private variables, so you have to use your own convention to distinguish between private and public ones. usually, the convention is to name your variable with an underscore at the beginning.

Anyway, this is not the place to ask for such help, but for raising bugs / request engine features. You should use the other community channels beforehand to ask for help.

1
Answered Jan 15 '19 at 15:21
avatar  of groud
groud

if Input.is_action_just_released() does work on Linux tho (3.3) but doesn't work on Windows and this is confusing.

1
Answered May 07 '21 at 09:14
avatar  of me2beats
me2beats

I have the same problem with my L2 (and presumably R2) buttons.
L2 and R2 are xbox-one controller trigger (variable) button types.
I gather my input in the physicsprocess function.
I have a print line to debug which shows me the frame number: print("get_tree().get_frame() = ", get_tree().get_frame())) The other (not L2, R2) 'buttons' on the xbox controller do not have this issue so I think it is a problem when the variable-trigger type buttons only.
I get the input like this using the 'Just Pressed' variation as to prevent double input when holding the button down: input_just_pressed[button_map.l2] = Input.is_action_just_pressed("ui_l2_" + str(input_controller))

When I press L2 once it registers twice in two different consecutive frames:

get_tree().get_frame() = 306
get_tree().get_frame() = 307

I will most likely have to add code to account for this 'debounce' input issue. Something along the line of keeping track of the delta for inputs as I do for input duration counts for other gameplay (holding buttons for x seconds etc).

Thanks, Shaun

1
Answered May 29 '21 at 17:10
avatar  of sjharb
sjharb

I have the same problem with my L2 (and presumably R2) buttons. L2 and R2 are xbox-one controller trigger (variable) button types. I gather my input in the physicsprocess function. I have a print line to debug which shows me the frame number: print("get_tree().get_frame() = ", get_tree().get_frame())) The other (not L2, R2) 'buttons' on the xbox controller do not have this issue so I think it is a problem when the variable-trigger type buttons only. I get the input like this using the 'Just Pressed' variation as to prevent double input when holding the button down: input_just_pressed[button_map.l2] = Input.is_action_just_pressed("ui_l2_" + str(input_controller))

When I press L2 once it registers twice in two different consecutive frames:

get_tree().get_frame() = 306
get_tree().get_frame() = 307

I will most likely have to add code to account for this 'debounce' input issue. Something along the line of keeping track of the delta for inputs as I do for input duration counts for other gameplay (holding buttons for x seconds etc).

Thanks, Shaun

Ok I found my issue. I am storing the inputs in an array: input_just_pressed[button_map.l2]

But I was not collecting the input for certain open menu/hud selection conditions. By not collecting the input and updating my button press array, the value was still set to 'true' (i.e. pressed) for my input processing. So if anyone else is storing these input values they will want to be aware of such a coding mistake.

Thanks, Shaun

1
Answered May 29 '21 at 17:54
avatar  of sjharb
sjharb