Skip to content

Commit a420585

Browse files
committed
Merge pull request #109540 from bruvzg/input_timestamps
Add methods to check which event first triggered "just pressed/released" state.
2 parents 9016df6 + 10fd716 commit a420585

File tree

7 files changed

+114
-17
lines changed

7 files changed

+114
-17
lines changed

core/input/input.cpp

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ void Input::_bind_methods() {
123123
ClassDB::bind_method(D_METHOD("is_action_pressed", "action", "exact_match"), &Input::is_action_pressed, DEFVAL(false));
124124
ClassDB::bind_method(D_METHOD("is_action_just_pressed", "action", "exact_match"), &Input::is_action_just_pressed, DEFVAL(false));
125125
ClassDB::bind_method(D_METHOD("is_action_just_released", "action", "exact_match"), &Input::is_action_just_released, DEFVAL(false));
126+
ClassDB::bind_method(D_METHOD("is_action_just_pressed_by_event", "action", "event", "exact_match"), &Input::is_action_just_pressed_by_event, DEFVAL(false));
127+
ClassDB::bind_method(D_METHOD("is_action_just_released_by_event", "action", "event", "exact_match"), &Input::is_action_just_released_by_event, DEFVAL(false));
126128
ClassDB::bind_method(D_METHOD("get_action_strength", "action", "exact_match"), &Input::get_action_strength, DEFVAL(false));
127129
ClassDB::bind_method(D_METHOD("get_action_raw_strength", "action", "exact_match"), &Input::get_action_raw_strength, DEFVAL(false));
128130
ClassDB::bind_method(D_METHOD("get_axis", "negative_action", "positive_action"), &Input::get_axis);
@@ -411,6 +413,37 @@ bool Input::is_action_just_pressed(const StringName &p_action, bool p_exact) con
411413
}
412414
}
413415

416+
bool Input::is_action_just_pressed_by_event(const StringName &p_action, const Ref<InputEvent> &p_event, bool p_exact) const {
417+
ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), false, InputMap::get_singleton()->suggest_actions(p_action));
418+
ERR_FAIL_COND_V(p_event.is_null(), false);
419+
420+
if (disable_input) {
421+
return false;
422+
}
423+
424+
HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action);
425+
if (!E) {
426+
return false;
427+
}
428+
429+
if (p_exact && E->value.exact == false) {
430+
return false;
431+
}
432+
433+
if (E->value.pressed_event_id != p_event->get_instance_id()) {
434+
return false;
435+
}
436+
437+
// Backward compatibility for legacy behavior, only return true if currently pressed.
438+
bool pressed_requirement = legacy_just_pressed_behavior ? E->value.cache.pressed : true;
439+
440+
if (Engine::get_singleton()->is_in_physics_frame()) {
441+
return pressed_requirement && E->value.pressed_physics_frame == Engine::get_singleton()->get_physics_frames();
442+
} else {
443+
return pressed_requirement && E->value.pressed_process_frame == Engine::get_singleton()->get_process_frames();
444+
}
445+
}
446+
414447
bool Input::is_action_just_released(const StringName &p_action, bool p_exact) const {
415448
ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), false, InputMap::get_singleton()->suggest_actions(p_action));
416449

@@ -437,6 +470,37 @@ bool Input::is_action_just_released(const StringName &p_action, bool p_exact) co
437470
}
438471
}
439472

473+
bool Input::is_action_just_released_by_event(const StringName &p_action, const Ref<InputEvent> &p_event, bool p_exact) const {
474+
ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), false, InputMap::get_singleton()->suggest_actions(p_action));
475+
ERR_FAIL_COND_V(p_event.is_null(), false);
476+
477+
if (disable_input) {
478+
return false;
479+
}
480+
481+
HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action);
482+
if (!E) {
483+
return false;
484+
}
485+
486+
if (p_exact && E->value.exact == false) {
487+
return false;
488+
}
489+
490+
if (E->value.released_event_id != p_event->get_instance_id()) {
491+
return false;
492+
}
493+
494+
// Backward compatibility for legacy behavior, only return true if currently released.
495+
bool released_requirement = legacy_just_pressed_behavior ? !E->value.cache.pressed : true;
496+
497+
if (Engine::get_singleton()->is_in_physics_frame()) {
498+
return released_requirement && E->value.released_physics_frame == Engine::get_singleton()->get_physics_frames();
499+
} else {
500+
return released_requirement && E->value.released_process_frame == Engine::get_singleton()->get_process_frames();
501+
}
502+
}
503+
440504
float Input::get_action_strength(const StringName &p_action, bool p_exact) const {
441505
ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), 0.0, InputMap::get_singleton()->suggest_actions(p_action));
442506

@@ -895,10 +959,12 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
895959
_update_action_cache(E.key, action_state);
896960
// As input may come in part way through a physics tick, the earliest we can react to it is the next physics tick.
897961
if (action_state.cache.pressed && !was_pressed) {
962+
action_state.pressed_event_id = p_event->get_instance_id();
898963
action_state.pressed_physics_frame = Engine::get_singleton()->get_physics_frames() + 1;
899964
action_state.pressed_process_frame = Engine::get_singleton()->get_process_frames();
900965
}
901966
if (!action_state.cache.pressed && was_pressed) {
967+
action_state.released_event_id = p_event->get_instance_id();
902968
action_state.released_physics_frame = Engine::get_singleton()->get_physics_frames() + 1;
903969
action_state.released_process_frame = Engine::get_singleton()->get_process_frames();
904970
}
@@ -1027,6 +1093,7 @@ void Input::action_press(const StringName &p_action, float p_strength) {
10271093

10281094
// As input may come in part way through a physics tick, the earliest we can react to it is the next physics tick.
10291095
if (!action_state.cache.pressed) {
1096+
action_state.pressed_event_id = ObjectID();
10301097
action_state.pressed_physics_frame = Engine::get_singleton()->get_physics_frames() + 1;
10311098
action_state.pressed_process_frame = Engine::get_singleton()->get_process_frames();
10321099
}
@@ -1045,6 +1112,7 @@ void Input::action_release(const StringName &p_action) {
10451112
action_state.cache.strength = 0.0;
10461113
action_state.cache.raw_strength = 0.0;
10471114
// As input may come in part way through a physics tick, the earliest we can react to it is the next physics tick.
1115+
action_state.released_event_id = ObjectID();
10481116
action_state.released_physics_frame = Engine::get_singleton()->get_physics_frames() + 1;
10491117
action_state.released_process_frame = Engine::get_singleton()->get_process_frames();
10501118
action_state.device_states.clear();

core/input/input.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ class Input : public Object {
109109
uint64_t pressed_process_frame = UINT64_MAX;
110110
uint64_t released_physics_frame = UINT64_MAX;
111111
uint64_t released_process_frame = UINT64_MAX;
112+
ObjectID pressed_event_id;
113+
ObjectID released_event_id;
112114
bool exact = true;
113115

114116
struct DeviceState {
@@ -306,6 +308,8 @@ class Input : public Object {
306308
bool is_action_pressed(const StringName &p_action, bool p_exact = false) const;
307309
bool is_action_just_pressed(const StringName &p_action, bool p_exact = false) const;
308310
bool is_action_just_released(const StringName &p_action, bool p_exact = false) const;
311+
bool is_action_just_pressed_by_event(const StringName &p_action, const Ref<InputEvent> &p_event, bool p_exact = false) const;
312+
bool is_action_just_released_by_event(const StringName &p_action, const Ref<InputEvent> &p_event, bool p_exact = false) const;
309313
float get_action_strength(const StringName &p_action, bool p_exact = false) const;
310314
float get_action_raw_strength(const StringName &p_action, bool p_exact = false) const;
311315

doc/classes/Input.xml

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,20 @@
212212
If [param exact_match] is [code]false[/code], it ignores additional input modifiers for [InputEventKey] and [InputEventMouseButton] events, and the direction for [InputEventJoypadMotion] events.
213213
[b]Note:[/b] Returning [code]true[/code] does not imply that the action is [i]still[/i] pressed. An action can be pressed and released again rapidly, and [code]true[/code] will still be returned so as not to miss input.
214214
[b]Note:[/b] Due to keyboard ghosting, [method is_action_just_pressed] may return [code]false[/code] even if one of the action's keys is pressed. See [url=$DOCS_URL/tutorials/inputs/input_examples.html#keyboard-events]Input examples[/url] in the documentation for more information.
215-
[b]Note:[/b] During input handling (e.g. [method Node._input]), use [method InputEvent.is_action_pressed] instead to query the action state of the current event.
215+
[b]Note:[/b] During input handling (e.g. [method Node._input]), use [method InputEvent.is_action_pressed] instead to query the action state of the current event. See also [method is_action_just_pressed_by_event].
216+
</description>
217+
</method>
218+
<method name="is_action_just_pressed_by_event" qualifiers="const">
219+
<return type="bool" />
220+
<param index="0" name="action" type="StringName" />
221+
<param index="1" name="event" type="InputEvent" />
222+
<param index="2" name="exact_match" type="bool" default="false" />
223+
<description>
224+
Returns [code]true[/code] when the user has [i]started[/i] pressing the action event in the current frame or physics tick, and the first event that triggered action press in the current frame/physics tick was [param event]. It will only return [code]true[/code] on the frame or tick that the user pressed down the button.
225+
This is useful for code that needs to run only once when an action is pressed, and the action is processed during input handling (e.g. [method Node._input]).
226+
If [param exact_match] is [code]false[/code], it ignores additional input modifiers for [InputEventKey] and [InputEventMouseButton] events, and the direction for [InputEventJoypadMotion] events.
227+
[b]Note:[/b] Returning [code]true[/code] does not imply that the action is [i]still[/i] pressed. An action can be pressed and released again rapidly, and [code]true[/code] will still be returned so as not to miss input.
228+
[b]Note:[/b] Due to keyboard ghosting, [method is_action_just_pressed] may return [code]false[/code] even if one of the action's keys is pressed. See [url=$DOCS_URL/tutorials/inputs/input_examples.html#keyboard-events]Input examples[/url] in the documentation for more information.
216229
</description>
217230
</method>
218231
<method name="is_action_just_released" qualifiers="const">
@@ -223,7 +236,19 @@
223236
Returns [code]true[/code] when the user [i]stops[/i] pressing the action event in the current frame or physics tick. It will only return [code]true[/code] on the frame or tick that the user releases the button.
224237
[b]Note:[/b] Returning [code]true[/code] does not imply that the action is [i]still[/i] not pressed. An action can be released and pressed again rapidly, and [code]true[/code] will still be returned so as not to miss input.
225238
If [param exact_match] is [code]false[/code], it ignores additional input modifiers for [InputEventKey] and [InputEventMouseButton] events, and the direction for [InputEventJoypadMotion] events.
226-
[b]Note:[/b] During input handling (e.g. [method Node._input]), use [method InputEvent.is_action_released] instead to query the action state of the current event.
239+
[b]Note:[/b] During input handling (e.g. [method Node._input]), use [method InputEvent.is_action_released] instead to query the action state of the current event. See also [method is_action_just_released_by_event].
240+
</description>
241+
</method>
242+
<method name="is_action_just_released_by_event" qualifiers="const">
243+
<return type="bool" />
244+
<param index="0" name="action" type="StringName" />
245+
<param index="1" name="event" type="InputEvent" />
246+
<param index="2" name="exact_match" type="bool" default="false" />
247+
<description>
248+
Returns [code]true[/code] when the user [i]stops[/i] pressing the action event in the current frame or physics tick, and the first event that triggered action release in the current frame/physics tick was [param event]. It will only return [code]true[/code] on the frame or tick that the user releases the button.
249+
This is useful when an action is processed during input handling (e.g. [method Node._input]).
250+
[b]Note:[/b] Returning [code]true[/code] does not imply that the action is [i]still[/i] not pressed. An action can be released and pressed again rapidly, and [code]true[/code] will still be returned so as not to miss input.
251+
If [param exact_match] is [code]false[/code], it ignores additional input modifiers for [InputEventKey] and [InputEventMouseButton] events, and the direction for [InputEventJoypadMotion] events.
227252
</description>
228253
</method>
229254
<method name="is_action_pressed" qualifiers="const">

scene/gui/popup_menu.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,7 @@ void PopupMenu::_input_from_window_internal(const Ref<InputEvent> &p_event) {
483483
}
484484
if (p_event->is_action("ui_down", true) && p_event->is_pressed()) {
485485
if (is_joypad_event) {
486-
if (!input->is_action_just_pressed("ui_down", true)) {
486+
if (!input->is_action_just_pressed_by_event("ui_down", p_event, true)) {
487487
return;
488488
}
489489
joypad_event_process = true;
@@ -526,7 +526,7 @@ void PopupMenu::_input_from_window_internal(const Ref<InputEvent> &p_event) {
526526
}
527527
} else if (p_event->is_action("ui_up", true) && p_event->is_pressed()) {
528528
if (is_joypad_event) {
529-
if (!input->is_action_just_pressed("ui_up", true)) {
529+
if (!input->is_action_just_pressed_by_event("ui_up", p_event, true)) {
530530
return;
531531
}
532532
joypad_event_process = true;

scene/gui/slider.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ void Slider::gui_input(const Ref<InputEvent> &p_event) {
136136
return;
137137
}
138138
if (is_joypad_event) {
139-
if (!input->is_action_just_pressed("ui_left", true)) {
139+
if (!input->is_action_just_pressed_by_event("ui_left", p_event, true)) {
140140
return;
141141
}
142142
set_process_internal(true);
@@ -152,7 +152,7 @@ void Slider::gui_input(const Ref<InputEvent> &p_event) {
152152
return;
153153
}
154154
if (is_joypad_event) {
155-
if (!input->is_action_just_pressed("ui_right", true)) {
155+
if (!input->is_action_just_pressed_by_event("ui_right", p_event, true)) {
156156
return;
157157
}
158158
set_process_internal(true);
@@ -168,7 +168,7 @@ void Slider::gui_input(const Ref<InputEvent> &p_event) {
168168
return;
169169
}
170170
if (is_joypad_event) {
171-
if (!input->is_action_just_pressed("ui_up", true)) {
171+
if (!input->is_action_just_pressed_by_event("ui_up", p_event, true)) {
172172
return;
173173
}
174174
set_process_internal(true);
@@ -180,7 +180,7 @@ void Slider::gui_input(const Ref<InputEvent> &p_event) {
180180
return;
181181
}
182182
if (is_joypad_event) {
183-
if (!input->is_action_just_pressed("ui_down", true)) {
183+
if (!input->is_action_just_pressed_by_event("ui_down", p_event, true)) {
184184
return;
185185
}
186186
set_process_internal(true);

scene/gui/tab_bar.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
315315
bool is_joypad_event = (joypadmotion_event.is_valid() || joypadbutton_event.is_valid());
316316
if (p_event->is_action("ui_right", true)) {
317317
if (is_joypad_event) {
318-
if (!input->is_action_just_pressed("ui_right", true)) {
318+
if (!input->is_action_just_pressed_by_event("ui_right", p_event, true)) {
319319
return;
320320
}
321321
set_process_internal(true);
@@ -325,7 +325,7 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
325325
}
326326
} else if (p_event->is_action("ui_left", true)) {
327327
if (is_joypad_event) {
328-
if (!input->is_action_just_pressed("ui_left", true)) {
328+
if (!input->is_action_just_pressed_by_event("ui_left", p_event, true)) {
329329
return;
330330
}
331331
set_process_internal(true);

scene/main/viewport.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2301,35 +2301,35 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
23012301
if (joypadmotion_event.is_valid()) {
23022302
Input *input = Input::get_singleton();
23032303

2304-
if (p_event->is_action_pressed(SNAME("ui_focus_next")) && input->is_action_just_pressed(SNAME("ui_focus_next"))) {
2304+
if (p_event->is_action_pressed(SNAME("ui_focus_next")) && input->is_action_just_pressed_by_event(SNAME("ui_focus_next"), p_event)) {
23052305
next = from->find_next_valid_focus();
23062306
}
23072307

2308-
if (p_event->is_action_pressed(SNAME("ui_focus_prev")) && input->is_action_just_pressed(SNAME("ui_focus_prev"))) {
2308+
if (p_event->is_action_pressed(SNAME("ui_focus_prev")) && input->is_action_just_pressed_by_event(SNAME("ui_focus_prev"), p_event)) {
23092309
next = from->find_prev_valid_focus();
23102310
}
23112311

2312-
if (p_event->is_action_pressed(SNAME("ui_accessibility_drag_and_drop")) && input->is_action_just_pressed(SNAME("ui_accessibility_drag_and_drop"))) {
2312+
if (p_event->is_action_pressed(SNAME("ui_accessibility_drag_and_drop")) && input->is_action_just_pressed_by_event(SNAME("ui_accessibility_drag_and_drop"), p_event)) {
23132313
if (gui_is_dragging()) {
23142314
from->accessibility_drop();
23152315
} else {
23162316
from->accessibility_drag();
23172317
}
23182318
}
23192319

2320-
if (p_event->is_action_pressed(SNAME("ui_up")) && input->is_action_just_pressed(SNAME("ui_up"))) {
2320+
if (p_event->is_action_pressed(SNAME("ui_up")) && input->is_action_just_pressed_by_event(SNAME("ui_up"), p_event)) {
23212321
next = from->_get_focus_neighbor(SIDE_TOP);
23222322
}
23232323

2324-
if (p_event->is_action_pressed(SNAME("ui_left")) && input->is_action_just_pressed(SNAME("ui_left"))) {
2324+
if (p_event->is_action_pressed(SNAME("ui_left")) && input->is_action_just_pressed_by_event(SNAME("ui_left"), p_event)) {
23252325
next = from->_get_focus_neighbor(SIDE_LEFT);
23262326
}
23272327

2328-
if (p_event->is_action_pressed(SNAME("ui_right")) && input->is_action_just_pressed(SNAME("ui_right"))) {
2328+
if (p_event->is_action_pressed(SNAME("ui_right")) && input->is_action_just_pressed_by_event(SNAME("ui_right"), p_event)) {
23292329
next = from->_get_focus_neighbor(SIDE_RIGHT);
23302330
}
23312331

2332-
if (p_event->is_action_pressed(SNAME("ui_down")) && input->is_action_just_pressed(SNAME("ui_down"))) {
2332+
if (p_event->is_action_pressed(SNAME("ui_down")) && input->is_action_just_pressed_by_event(SNAME("ui_down"), p_event)) {
23332333
next = from->_get_focus_neighbor(SIDE_BOTTOM);
23342334
}
23352335
} else {

0 commit comments

Comments
 (0)