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
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.
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.
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.
if Input.is_action_just_released()
does work on Linux tho (3.3) but doesn't work on Windows and this is confusing.
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
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
Read next
- Docker keeps restarting? - Shell docker-bookstack
- Incorrect syntax near the keyword 'WHERE'. mssql - typeorm
- Use of greenlock-express with NestJS? - nest
- Header wrapping - markdown
- Bug Report - Windows, HOME environment - Cplusplus o3de
- aiopg aiopg 1.1.0+ is incompatible with SQLAlchemy 1.4 - Python
- The app should have a single instance to access db. - BoostNote.next
- NewPipe Play protect - Java