Why doesn't InputEvent have is_action_just_pressed method when Input does?

Godot Version

4.5.stable

Question

I don’t understand why is_action_just_pressed, and its sibling is_action_just_released() are missing while using InputEvent either through _unhandled_input() or _input() or other methods.

I know, I know, it can easily be worked around by using toggled variables. Still. Why put is_just* in one and not the other. I imagine there is a reasonable answer to that.

For example, for those wondering about this.

With Input you can do something like:

	if Input.is_action_just_pressed("SHIFT"):
		speed *= 1.33
	if Input.is_action_just_released("SHIFT"):
		speed /= 1.33

and in your _process:

func _process(delta : float) -> void:
	velocity = velocity.normalized() * speed
    # Etc

-=-=-=-=-=
But with those missing methods you have to toggle a variable:

	if event.is_action_pressed("SHIFT"):
		running = !running

and in your _process:

func _process(delta : float) -> void:
        if running:
            speed *= 1.33
        else:
            speed \= 1.33
	velocity = velocity.normalized() * speed
    # Etc

I prefer the former because it makes the whole thing much clearer. Also, I prefer my _process* methods as empty as possible, avoiding branching and IFs or ternary, etc.

I just need to understand why these are absent in InputEvent.

1 Like

To my understanding event.is_action_pressed() IS basically equal to Input.is_action_just_pressed, they just didn’t add “just” in the name of the function.

_input() or _unhadled_input() will only be called when a key is pressed or released, so every time they’re called the key is “just” pressed, or “just” released (Hope it makes sense)

1 Like

It’s not absent, just different. InputEvent.is_action_pressed()'s second argument (allow_echo: bool = false) basically does the same.

Not as such no. is_just* will continually trigger as is_action_pressed() won’t.

Since the allow_echo parameter is false by default, they are identical.

InputEvent.is_action_pressed()and Input.is_action_pressed() are different, unless you pass true as second parameter for InputEvent. Which makes it pretty confusing.

Okay, okay. But WHY!? That’s what I don’t get. If those two are equivalent, then why have two? Why not use one way for both?

Pick one. is_action_just_pressed and is_action_just_released, or the second argument of is_action_pressed and is_action_released.

The whole thing is just confusing most people.

Nothing confusing about it. Input.is_action_just_pressed() retrieves the pressed state of the action in the current frame. InputEvent.is_action_pressed() contains the pressed state of the action at the time the event was received. The “just” part is implicit.

Input is a global singleton, it is accessible anywhere, an InputEvent is a RefCounted object it is created once for each input. Since events are created for each input it cannot have a “held” state, it’s only created on press/release. If you want a toggle code you can do so even easier with events.

if event.is_action("SHIFT"):
    running = event.is_pressed()

What I have a problem with is the different nomenclature. Why are they different between the singleton and InputEvent generated? That’s where the confusion is; the decision to go with a different method name, if they are actually doing the same thing.

Following the suggestions above—even if that was not part of the question—all I would have to do is switch one for the other and voilà, but it doesn’t entirely.

For the heck of it I just tried and it sort of work. But, if I use two keys, let’s say UP key and RIGHT key, to move diagonally, it won’t work. Only the last pressed key will be triggered whereas using the singleton Input, both will work, add themselves to the vector so the player can move diagonally.

Code won’t allow for diagonal movement:

    ...
	if event.is_action_pressed("LEFT", true):
		velocity.x -= 1
	if event.is_action_pressed("DOWN", true):
		velocity.y += 1
    ...

Won’t move the player diagonally. Only the most recent key will be processed.

This one will move fine in 8 directions.

    ...
	if Input.is_action_pressed("LEFT"):
		velocity.x -= 1
	if Input.is_action_pressed("DOWN"):
		velocity.y += 1
    ...

The player will move as expected.

So… To me, that means both methods work kinda the same, but taken in my context, it won’t work and I’m not 100% sure why, but I do have guesses.

They’re not doing the same thing, and each name has its context. You can have many variables named sum in your program within various functions or objects. Each represents something different within its context, even though their name is exactly the same. It’s similar with this.

“Just” is redundant with event so why have it in the name to make it needlessly longer. And singleton has two query functions (query the current state with “is_pressed” and query the state change in current frame with “is_just_pressed”), so it needs different names to distinguish between the two.

The “just” part for the event is implicitly contained in the fact that it is an event. The event always happens “just” now.

I’ll have to disagree with that. To me that’s very doubtful.

You can disagree all you want but it’s the truth. Event always happens “just” now. The fact that your handler code in _input() has been called means that the event “just” happened. Otherwise the handler function wouldn’t be called.

2 Likes

You’re just doing semantics now. Programming isn’t about semantics (most of the time). Things are named differently because they do different things or achieve similar things in different ways that can be observed.

No I’m not. But let’s start over.

I’ll state this on the record: Input.is_action_pressed() and InputEvent.is_action_pressed() actually do the same thing, can we agree on that? So it’s perfectly fine for them to have the same name, even in different contexts.

The proper equivalent of Input.is_action_just_pressed() from _input() would be:

_input(e):
	if e.is_pressed() and e.is_action("my_action"):
		pass

Absolutely, but as I said above, holding multiple keys won’t work with InputEvent, whereas using Input will. This is probably because of the internal state of Input, but for now that’s just a guess.

Holding multiple keys with _input() will work. You just need to record their state because Input is typically queried every frame wile _input() will be called only when something changes, i.e. when press or release event happens.

If you’re confused, write an Input version and I can give you the _input() equivalent, or vice versa. Sometimes one results in shorter code than another, depending your input type (event driven or state driven) but ultimately, the state is always changed by events, so strictly speaking the approaches are interchangeable.

Ultimately, the reason of the original question is because I want to switch from using Input to InputEvent since the former will be triggered anywhere, regardless of layers. So, for example, if I bring a menu up and the user click, I don’t necessarily want to player to react to that. Input will use that key always whereas InputEvent won’t.

Well using _input() should always be preferable, but for some stuff querying Input directly is more convenient. For gui events you should always use one of the _input(), _gui_input() or _unhandled_input()

If you want to track action state with _input() you’ll need to maintain your own state flags. So:

var left_pressed := false
var down_pressed := false
func _input(e):
	if e.is_action("LEFT"):
		left_pressed = e.is_pressed():
	if e.is_action("DOWN"):
		down_pressed = e.is_pressed():

And then in _process() act according to those state flags.