Godot: Scroll wheel up or down .is_action_just_pressed() does not register

Created on 18 Feb 2020  路  16Comments  路  Source: godotengine/godot

Godot version: 3.2.stable

OS/device including version: Win64

Issue description:
When using the mouse wheel, Input.is_action_just_pressed() does not register, while Input.is_action_just_released() does.

Steps to reproduce:
Works:

extends Node2D

var value = 10

func _process(_delta):
    if Input.is_action_just_released("wheel_up"):
        value += 1
        print(value)
    if Input.is_action_just_released("wheel_down"):
        value -= 1
        print(value)

Does nothing:

extends Node2D

var value = 10

func _process(_delta):
    if Input.is_action_just_pressed("wheel_up"):
        value += 1
        print(value)
    if Input.is_action_just_pressed("wheel_down"):
        value -= 1
        print(value)

Also does nothing:

extends Node2D

var value = 10

func _process(_delta):
    if Input.is_action_pressed("wheel_up"):
        value += 1
        print(value)
    if Input.is_action_pressed("wheel_down"):
        value -= 1
        print(value)
bug input

Most helpful comment

It's not about introducing spinning speed like it's possible to do for the mouse cursor. The user will always be able to decide wheel rotation speed with the finger; let me explain:


Case one

Normal mouse with mouse wheel friction:

__Frame 1__
The wheel is rotated by 1 tick

  • is_action_just_pressed() == true
  • is_action_pressed() == true
  • is_action_just_released() == false

__Frame 2__
Since the mouse wheel physical friction, the rotation is not yet complete and this frame the input is not registered:

  • is_action_just_pressed() == false
  • is_action_pressed() == false
  • is_action_just_released() == true

__Frame 3__
This frame the wheel rotation is complete and the input is again registered.

  • is_action_just_pressed() == true
  • is_action_pressed() == true
  • is_action_just_released() == false

Case two

Gaming mouse with mouse wheel friction turned off:

__Frame 1__
Mild finger kick

  • is_action_just_pressed() == true
  • is_action_pressed() == true
  • is_action_just_released() == false

__Frame 2__
Continue rotation

  • is_action_just_pressed() == false
  • is_action_pressed() == true
  • is_action_just_released() == false

__Frame 3__
The wheel stop

  • is_action_just_pressed() == false
  • is_action_pressed() == false
  • is_action_just_released() == true

All 16 comments

I'm not sure if we can make it so it fires both "pressed" and "released" events at the same time. Or should it just fire "pressed" events?

Mouse wheel buttons have the peculiar property of being released immediately as soon as they're pressed. No other keyboard or mouse buttons have this property.

Fun fact: This is why some people bind the "jump" key to the mouse wheel to bunnyhop in games like Half-Life :slightly_smiling_face:

I'm not sure if we can make it so it fires both "pressed" and "released" events at the same time. Or should it just fire "pressed" events?

I would expect all three to behave exactly the same.
If there is consensus not to include pressed and just pressed, then I think there should be a clear error going off saying: use 'is_action_just_released("action")' instead.
Otherwise people spend hours and maybe days on pointless bug hunting.

Interesting fun fact :)

Just throwing a warning that discourages using is_action_just_pressed/released for mouse wheels to begin with is also an option. It's a precision "button" that can be scrolled insanely fast on some mice with free wheels after all.

func _input(event):
    if event.is_action_pressed("wheel_up"):
        value += 1

^ This works just fine and is a more sensible way to deal with the mouse wheel.

@proarunas How could we detect the user has attempted to trigger an action using the mouse wheel?

I don't quite understand the question.
Is_action_pressed InputEvent works fine for mouse wheel, so you can detect the wheel just fine with _input(event) (and related) signal methods. It's just the polling that gives problems, which is understandable, as the mouse wheels aren't really intended to be polled.

I'd personally just mark action_just_preseed events for the mouse wheel as deprecated, and leave a clear warning in the docs that you should probably not use them.

@proarunas Ah, I see what you mean now. I thought you wanted to print a warning message at runtime when the user attempts to use Input.is_action_pressed() with an action that's bound to a mouse wheel button.

I ran into the same problem, a warning would be really helpful but it's not enough IMO.

Let's suppose that you have to create a game that allows the user to bind any action to any available input. This is a common and a must have feature to make the game accessible to people with motor disorders.

The concept of action is good to integrate the above feature, because this abstraction layer allows the developer to build the game around a concept and not a peripheral; in this way any action can be triggered by any input.

Of course, as @Calinou rightly said, doesn't make sense fire two opposite events in the same frame; what I propose is to fire the is_action_just_pressed when the wheel starts to spin and is_action_just_released when the wheel stop spin - so the function is_action_pressed returns true during the spinning phase.

Note: A lot of gaming mouses have a physical friction button that allows to spin the mouse wheel for a long period with just a mild finger kick. This is a cool feature that is possible to use with the above approach that can make the game much more accessible to any kind of player.

what I propose is to fire the is_action_just_pressed when the wheel starts to spin and is_action_just_released when the wheel stop spin

The mouse wheel doesn't have a concept of "spinning speed", unless it supports high-precision scrolling. This is common on trackpads, but very uncommon on desktop mice.

It's not about introducing spinning speed like it's possible to do for the mouse cursor. The user will always be able to decide wheel rotation speed with the finger; let me explain:


Case one

Normal mouse with mouse wheel friction:

__Frame 1__
The wheel is rotated by 1 tick

  • is_action_just_pressed() == true
  • is_action_pressed() == true
  • is_action_just_released() == false

__Frame 2__
Since the mouse wheel physical friction, the rotation is not yet complete and this frame the input is not registered:

  • is_action_just_pressed() == false
  • is_action_pressed() == false
  • is_action_just_released() == true

__Frame 3__
This frame the wheel rotation is complete and the input is again registered.

  • is_action_just_pressed() == true
  • is_action_pressed() == true
  • is_action_just_released() == false

Case two

Gaming mouse with mouse wheel friction turned off:

__Frame 1__
Mild finger kick

  • is_action_just_pressed() == true
  • is_action_pressed() == true
  • is_action_just_released() == false

__Frame 2__
Continue rotation

  • is_action_just_pressed() == false
  • is_action_pressed() == true
  • is_action_just_released() == false

__Frame 3__
The wheel stop

  • is_action_just_pressed() == false
  • is_action_pressed() == false
  • is_action_just_released() == true

This looks like it would make a lot of sense @AndreaCatania

Sure, all you have to do is convince all the mouse manufacturers to fire some signal for when the wheel is still spinning...

All we have is a sequence of scroll up/down signals, and we must choose what events to fire.

For action_pressed, current behaviour is fine. the problem is action_just_pressed. And if you want to keep the behaviour independent of hardware, you sort of have to fire both pressed and released events in the same frame.

It's not necessary change how the hardware works but just how Godot interpret the mouse wheel inputs.

Currently Godot intercept the rotation input from the mouse wheel and emits immediately the exit event (released).
In my opinion when a single tick rotation is captured, Godot should emit immediately the _pressed_ event and then the next frame, _only_ if the next mouse wheel rotation input is not registered, emits the _released_ event.

This will make the wheel behaviour a lot more adherent to the action concept making possible what I written here https://github.com/godotengine/godot/issues/36322#issuecomment-589964001

Except different mice will fire those events at different rates. You now have to specify what is the window for the next event to count "still spinning". This goes against the idea of this being hardware (and OS for that matter) independent.

Also, some people actually expect now the mouse to fire a lot of released events, this change would break all the code that relies on that.

Except different mice will fire those events at different rates.

This doesn't change the current behaviour; indeed different mouse fires release events at different rates. That is the wanted and correct behaviour.

Also, some people actually expect now the mouse to fire a lot of released events, this change would break all the code that relies on that.

This is a breaking change, you are correct, and in case this change would be for the version 4.x.

then the next frame, _only_ if the next mouse wheel rotation input is not registered, emits the _released_ event.

This part is the problem. How do you define a frame ?
Some set amount of time? Every X amount of godot update calls? I just don't see any possible way to do it that would work for all mice and all users consistently. And if it's inconsistent, it's pointless having it.

With the way it works currently, you can still define your own frames and have your described behaviour. With your proposal, you are forcing people into that model.

I don't want to change the current way of registering events. Rather, I want to flip the emitted event and add a new one on the next adjacent frame, processed as soon as the game instance frame rate allows (exactly like it's now, basically).

Was this page helpful?
0 / 5 - 0 ratings