Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a signal for detecting action toggle states (pressed/released) in Input #43646

Closed
wants to merge 3 commits into from
Closed

Add a signal for detecting action toggle states (pressed/released) in Input #43646

wants to merge 3 commits into from

Conversation

Xrayez
Copy link
Contributor

@Xrayez Xrayez commented Nov 18, 2020

Closes godotengine/godot-proposals#1853.

image

Documentation and further improvements can be written later, if the feature is desired/approved.

To-do:

Works for those "just" pressed/released events. Will also emit signal while simulating action presses.

With GDScript 2.0:

extends Node2D

func _ready():
	Input.action_toggled.connect(Callable(self, "_on_action_toggled"))

func _on_action_toggled(action, pressed):
	if pressed:
		print("Action pressed: " + str(action))
	else:
		print("Action released: " + str(action))

@Calinou Calinou added this to the 4.0 milestone Nov 18, 2020
@Calinou Calinou added the cherrypick:3.x Considered for cherry-picking into a future 3.x release label Nov 18, 2020
@golddotasksquestions
Copy link

golddotasksquestions commented Nov 18, 2020

Thats amazing! ❤❤❤ Thank you so much, I did not expect anyone would pick this up, definitely not that fast!
May I asked why it's called "action_state_changed"? This sounds like the action would be "present" even when no input action was performed by the player. Why not just call it "input_action"?

@Xrayez
Copy link
Contributor Author

Xrayez commented Nov 18, 2020

The action_state there represents all actions which has been pressed so far (the initial state is not immediately initialized from the InputMap singleton), so technically you're right.

But one may also say that all actions are in "released" state while the game is started. When a player presses a key, then the state is changed from "unpressed" to "pressed" (pressed = true), and that's where the signal will be emitted. Likewise, when you release the pressed key, this will surely emit the same signal, but now with pressed = false.

It's also totally possible to add separate signals for this:

  • action_pressed(action)
  • action_released(action)

I'd personally add two signals, but I'm being conservative with this PR to increase the chances of approval. 😃

Also, if we add two signals for each pressed/released events, then it's also possible to connect two signals to the same method, if someone wants the functionality behind unified action_state_changed signal (yet you wouldn't be able to distinguish between the two).

Think about this as Buttons toggled signal.

That said, it's also possible to rename the signal to action_toggled, and/or add action_pressed/action_released signals alongside, just like with Button class.

@Xrayez Xrayez changed the title Add a signal for action press state changes in Input Add a signal for detecting action toggle states in Input Nov 18, 2020
@Xrayez Xrayez changed the title Add a signal for detecting action toggle states in Input Add a signal for detecting action toggle states (pressed/released) in Input Nov 18, 2020
@golddotasksquestions
Copy link

Interesting, thank your for the explanation! I had no idea about any of that.

To be honest as someone who is not intimately familiar with the source code and just uses GDScript, and already existing Editor functionality, I find Input "states" more confusing than anything. Input Map "state" is only mentioned here like a sidenote once. I did not feel like it played any relevant role until now, I actually did not pay any attention to it.

@Riteo
Copy link
Contributor

Riteo commented Nov 19, 2020

That said, it's also possible to rename the signal to action_toggled, and/or add action_pressed/action_released signals alongside, just like with Button class.

+1 for this approach.
IMO it makes sense to be consistent with Button, since they're conceptually the same thing, with the only difference between the two being that one is physical and the other is on a screen.
(Even though I find kind of redundant to both have a toggled and pressed/released signal available the same time, but i guess that this goes outside of this PR scope.)

@Xrayez
Copy link
Contributor Author

Xrayez commented Nov 19, 2020

Yes, I think can be renamed to action_toggled then.

The only difference with Button's toggle behavior is that if you press the button, it will remain pressed if you release key, unlike with physical keys. But it's actually not quite the same as using Input.action_press for simulating action presses: the action will remain pressed until Input.action_release is called (or a physical key is pressed/released).

But yeah, TODO: emit action_toggled signal on Input.action_press and Input.action_release as well, because this currently only works when the input event is parsed by the scene tree immdediately, or when the input event is emulated with parse_input_event().

@golddotasksquestions
Copy link

golddotasksquestions commented Nov 19, 2020

To be honest I find the button and Input behaviour quite confusing, and from a beginner perspective unreasonably inconsistent. To my novice mind, a virtual button should not be any different than a physical key button on my keyboard or the buttons on my mouse.
Ever since I first started using Godot I wished everything just worked the same way. Like:

Methods: (used to check for ongoing property states)

if $Button.is_pressed(bool):
if $Button.is_released(bool):
if $Button.is_toggled(bool):

if Input.is_pressed("action"):
if Input.is_released("action"):
if Input.is_toggled("action", bool):
if Input.is_strength("action", float):

Signals: (used to check for one_shot events)

_on_Button_pressed(button):
_on_Button_released(button):
_on_Button_toggled(button, bool):

_on_Input_pressed(action):
_on_Input_released(action):
_on_Input_toggled(action, bool):

Pressure sensitive Input devices are not common place (yet, aside from music and art production), but maybe in future if $Button.is_strength(float) would be requested too.

@Arecher
Copy link

Arecher commented Nov 21, 2020

This seems like a very welcome addition. I have recently been wondering why this doesn't exist as well.

Quick question: Does this allow you to set up a signal per InputAction in the Input Map (so when Move_Up: W is pressed/released, it will send a signal) or will it always send a signal whenever any InputAction is pressed/released (Whenever W, A, S, D or MouseButton1 etc is pressed/released)?

I often need to know when the Left Mouse Button is released, and it would be most efficient to only receive a signal when that happens, rather than when any Input is released. Either way, this already seems an infinitely better solution than filtering input through _process() or _input().

@Xrayez
Copy link
Contributor Author

Xrayez commented Nov 21, 2020

Quick question: Does this allow you to set up a signal per InputAction in the Input Map (so when Move_Up: W is pressed/released, it will send a signal) or will it always send a signal whenever any InputAction is pressed/released (Whenever W, A, S, D or MouseButton1 is pressed/released)?

If you're asking whether raw keyboard/mouse input events would emit a signal, then no, it only applies to input actions defined in the Input Map (namely, input events caught in _input() won't emit the signal in Input, because that only applies to InputEventAction events).

That said, this will emit a signal for any action indeed, currently there's no way to filter out specific actions, although this is in theory possible to implement.

The way I see it, may be actually possible to dynamically add a dedicated signal for each action in the Input Map. In fact I've come up with Input signal dispatcher which depends on functionality behind this PR:

extends Node2D

func _ready():
	for action in InputMap.get_actions():
		var args = [
			{ "pressed" : TYPE_BOOL }
		]
		Input.add_user_signal(action, args)

	Input.connect("action_toggled", self, "_on_input_action_toggled")

func _on_input_action_toggled(action, pressed):
	Input.emit_signal(action, pressed)

You could then connect specific input actions like so (instead of general-purpose action_toggled):

func _ready():
    Input.connect("ui_accept", $AnimationPlayer, "play")

I can try and see whether it's possible to do in C++, thanks for suggestion @Arecher! 🙂

Andrii Doroshenko (Xrayez) added 2 commits November 21, 2020 16:06
@Xrayez
Copy link
Contributor Author

Xrayez commented Nov 21, 2020

So yeah I've got it working already per action:

func _ready():
	# For any action.
	Input.action_toggled.connect(Callable(self, "_on_action_toggled"))
	# For specific actions defined in InputMap.
	Input.connect("ui_accept_pressed", Callable(self, "_on_ui_accept_pressed"))
	Input.connect("ui_accept_released", Callable(self, "_on_ui_accept_released"))

func _on_ui_accept_pressed():
	print("Pressed: ui_accept")
	
func _on_ui_accept_released():
	print("Released: ui_accept")

func _on_action_toggled(action, pressed):
	if pressed:
		print("Pressed (toggle): " + str(action))
	else:
		print("Released (toggle): " + str(action))

Those user-defined signals won't be reflected in the documentation, so this behavior will have to be documented in action_toggled signal description (which handles any action).

Each action requires adding two signals per action: one for "_pressed", and one for "_released" events. Those are appended to each name.

If you'd like a specific action to work like in "toggle" mode, you can just connect both signal to the same method.

@Xrayez Xrayez closed this Jun 25, 2021
@Calinou Calinou added archived and removed cherrypick:3.x Considered for cherry-picking into a future 3.x release labels Jun 25, 2021
@Xrayez Xrayez reopened this Dec 24, 2021
@Calinou Calinou removed the archived label Dec 24, 2021
@akien-mga
Copy link
Member

Closing as this contributor can no longer update this further.

@Zireael07
Copy link
Contributor

Yet another PR that is worth continuing regardless of any issues that might have happened with this particular contributor...

@YuriSizov
Copy link
Contributor

@Zireael07 We can't in good conscience merge their work now even if we approve the feature, because we have to assume they don't want their code to be included in the project anymore. But if anyone is willing to salvage this work, they are more than welcome to!

@rainlizard
Copy link
Contributor

we have to assume they don't want their code to be included in the project anymore

You could just ask him, he still exists.

@akien-mga
Copy link
Member

This user has been banned from the Godot community for repeatedly breaching our Code of Conduct, so no, we're not going to ask.

@godotengine godotengine locked and limited conversation to collaborators Aug 9, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add _on_Input_action(action) signal
9 participants