From d9d0cfaf38cfb8f7b17af9d53ee8d327181cf9cf Mon Sep 17 00:00:00 2001 From: kobewi Date: Thu, 9 Nov 2023 23:54:09 +0100 Subject: [PATCH] Rework input actions to be reliable --- core/input/input.cpp | 127 ++++++++++++++++++++++----------------- core/input/input.h | 19 +++++- core/input/input_map.cpp | 18 +++++- core/input/input_map.h | 5 +- 4 files changed, 106 insertions(+), 63 deletions(-) diff --git a/core/input/input.cpp b/core/input/input.cpp index 2d48bdd4cf6f..257452b3d894 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -695,53 +695,34 @@ void Input::_parse_input_event_impl(const Ref &p_event, bool p_is_em } for (const KeyValue &E : InputMap::get_singleton()->get_action_map()) { - if (InputMap::get_singleton()->event_is_action(p_event, E.key)) { - Action &action = action_state[E.key]; - bool is_joypad_axis = jm.is_valid(); - bool is_pressed = false; - if (!p_event->is_echo()) { - if (p_event->is_action_pressed(E.key)) { - bool is_joypad_axis_valid_zone_enter = false; - if (is_joypad_axis) { - if (!action.axis_pressed) { - is_joypad_axis_valid_zone_enter = true; - action.pressed++; - action.axis_pressed = true; - } - } else { - action.pressed++; - } - if (action.pressed == 1 && (is_joypad_axis_valid_zone_enter || !is_joypad_axis)) { - action.pressed_physics_frame = Engine::get_singleton()->get_physics_frames(); - action.pressed_process_frame = Engine::get_singleton()->get_process_frames(); - } - is_pressed = true; - } else { - bool is_released = true; - if (is_joypad_axis) { - if (action.axis_pressed) { - action.axis_pressed = false; - } else { - is_released = false; - } - } - - if (is_released) { - if (action.pressed == 1) { - action.released_physics_frame = Engine::get_singleton()->get_physics_frames(); - action.released_process_frame = Engine::get_singleton()->get_process_frames(); - } - action.pressed = MAX(action.pressed - 1, 0); - } - } - action.exact = InputMap::get_singleton()->event_is_action(p_event, E.key, true); - } - - if (is_pressed || action.pressed == 0) { - action.strength = p_event->get_action_strength(E.key); - action.raw_strength = p_event->get_action_raw_strength(E.key); - } + const int event_index = InputMap::get_singleton()->event_get_index(p_event, E.key); + if (event_index == -1) { + continue; } + + Action &action = action_state[E.key]; + if (!p_event->is_echo()) { + if (p_event->is_action_pressed(E.key)) { + if (!action.pressed) { + action.pressed_physics_frame = Engine::get_singleton()->get_physics_frames(); + action.pressed_process_frame = Engine::get_singleton()->get_process_frames(); + } + action.pressed |= ((uint64_t)1 << event_index); + } else { + action.pressed &= ~((uint64_t)1 << event_index); + action.pressed &= ~(1 << MAX_EVENT); // Always release the event from action_press() method. + + if (!action.pressed) { + action.released_physics_frame = Engine::get_singleton()->get_physics_frames(); + action.released_process_frame = Engine::get_singleton()->get_process_frames(); + } + _update_action_strength(action, MAX_EVENT, 0.0); + _update_action_raw_strength(action, MAX_EVENT, 0.0); + } + action.exact = InputMap::get_singleton()->event_is_action(p_event, E.key, true); + } + _update_action_strength(action, event_index, p_event->get_action_strength(E.key)); + _update_action_raw_strength(action, event_index, p_event->get_action_raw_strength(E.key)); } if (event_dispatch_function) { @@ -858,13 +839,13 @@ void Input::action_press(const StringName &p_action, float p_strength) { // Create or retrieve existing action. Action &action = action_state[p_action]; - action.pressed++; - if (action.pressed == 1) { + if (!action.pressed) { action.pressed_physics_frame = Engine::get_singleton()->get_physics_frames(); action.pressed_process_frame = Engine::get_singleton()->get_process_frames(); } - action.strength = p_strength; - action.raw_strength = p_strength; + action.pressed |= 1 << MAX_EVENT; + _update_action_strength(action, MAX_EVENT, p_strength); + _update_action_raw_strength(action, MAX_EVENT, p_strength); action.exact = true; } @@ -872,13 +853,15 @@ void Input::action_release(const StringName &p_action) { // Create or retrieve existing action. Action &action = action_state[p_action]; - action.pressed--; - if (action.pressed == 0) { - action.released_physics_frame = Engine::get_singleton()->get_physics_frames(); - action.released_process_frame = Engine::get_singleton()->get_process_frames(); + action.pressed = 0; + action.strength = 0.0; + action.raw_strength = 0.0; + action.released_physics_frame = Engine::get_singleton()->get_physics_frames(); + action.released_process_frame = Engine::get_singleton()->get_process_frames(); + for (uint64_t i = 0; i <= MAX_EVENT; i++) { + action.strengths[i] = 0.0; + action.raw_strengths[i] = 0.0; } - action.strength = 0.0f; - action.raw_strength = 0.0f; action.exact = true; } @@ -1207,6 +1190,38 @@ void Input::_axis_event(int p_device, JoyAxis p_axis, float p_value) { parse_input_event(ievent); } +void Input::_update_action_strength(Action &p_action, int p_event_index, float p_strength) { + ERR_FAIL_INDEX(p_event_index, (int)MAX_EVENT + 1); + + float old_strength = p_action.strengths[p_event_index]; + p_action.strengths[p_event_index] = p_strength; + + if (p_strength > p_action.strength) { + p_action.strength = p_strength; + } else if (Math::is_equal_approx(old_strength, p_action.strength)) { + p_action.strength = p_strength; + for (uint64_t i = 0; i <= MAX_EVENT; i++) { + p_action.strength = MAX(p_action.strength, p_action.strengths[i]); + } + } +} + +void Input::_update_action_raw_strength(Action &p_action, int p_event_index, float p_strength) { + ERR_FAIL_INDEX(p_event_index, (int)MAX_EVENT + 1); + + float old_strength = p_action.raw_strengths[p_event_index]; + p_action.raw_strengths[p_event_index] = p_strength; + + if (p_strength > p_action.raw_strength) { + p_action.raw_strength = p_strength; + } else if (Math::is_equal_approx(old_strength, p_action.raw_strength)) { + p_action.raw_strength = p_strength; + for (uint64_t i = 0; i <= MAX_EVENT; i++) { + p_action.raw_strength = MAX(p_action.raw_strength, p_action.raw_strengths[i]); + } + } +} + Input::JoyEvent Input::_get_mapped_button_event(const JoyDeviceMapping &mapping, JoyButton p_button) { JoyEvent event; diff --git a/core/input/input.h b/core/input/input.h index bedc3fa0e33f..dd613c4877df 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -44,6 +44,8 @@ class Input : public Object { static Input *singleton; + static constexpr uint64_t MAX_EVENT = 31; + public: enum MouseMode { MOUSE_MODE_VISIBLE, @@ -103,11 +105,22 @@ private: uint64_t pressed_process_frame = UINT64_MAX; uint64_t released_physics_frame = UINT64_MAX; uint64_t released_process_frame = UINT64_MAX; - int pressed = 0; - bool axis_pressed = false; + uint64_t pressed = 0; bool exact = true; float strength = 0.0f; float raw_strength = 0.0f; + LocalVector strengths; + LocalVector raw_strengths; + + Action() { + strengths.resize(MAX_EVENT + 1); + raw_strengths.resize(MAX_EVENT + 1); + + for (uint64_t i = 0; i <= MAX_EVENT; i++) { + strengths[i] = 0.0; + raw_strengths[i] = 0.0; + } + } }; HashMap action_state; @@ -227,6 +240,8 @@ private: JoyAxis _get_output_axis(String output); void _button_event(int p_device, JoyButton p_index, bool p_pressed); void _axis_event(int p_device, JoyAxis p_axis, float p_value); + void _update_action_strength(Action &p_action, int p_event_index, float p_strength); + void _update_action_raw_strength(Action &p_action, int p_event_index, float p_strength); void _parse_input_event_impl(const Ref &p_event, bool p_is_emulated); diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp index ddfde0e7cdf1..78b9ada884e8 100644 --- a/core/input/input_map.cpp +++ b/core/input/input_map.cpp @@ -127,16 +127,21 @@ List InputMap::get_actions() const { return actions; } -List>::Element *InputMap::_find_event(Action &p_action, const Ref &p_event, bool p_exact_match, bool *r_pressed, float *r_strength, float *r_raw_strength) const { +List>::Element *InputMap::_find_event(Action &p_action, const Ref &p_event, bool p_exact_match, bool *r_pressed, float *r_strength, float *r_raw_strength, int *r_event_index) const { ERR_FAIL_COND_V(!p_event.is_valid(), nullptr); + int i = 0; for (List>::Element *E = p_action.inputs.front(); E; E = E->next()) { int device = E->get()->get_device(); if (device == ALL_DEVICES || device == p_event->get_device()) { if (E->get()->action_match(p_event, p_exact_match, p_action.deadzone, r_pressed, r_strength, r_raw_strength)) { + if (r_event_index) { + *r_event_index = i; + } return E; } } + i++; } return nullptr; @@ -179,6 +184,7 @@ void InputMap::action_erase_event(const StringName &p_action, const Ref>::Element *E = _find_event(input_map[p_action], p_event, true); if (E) { input_map[p_action].inputs.erase(E); + if (Input::get_singleton()->is_action_pressed(p_action)) { Input::get_singleton()->action_release(p_action); } @@ -216,7 +222,13 @@ bool InputMap::event_is_action(const Ref &p_event, const StringName return event_get_action_status(p_event, p_action, p_exact_match); } -bool InputMap::event_get_action_status(const Ref &p_event, const StringName &p_action, bool p_exact_match, bool *r_pressed, float *r_strength, float *r_raw_strength) const { +int InputMap::event_get_index(const Ref &p_event, const StringName &p_action, bool p_exact_match) const { + int index = -1; + event_get_action_status(p_event, p_action, p_exact_match, nullptr, nullptr, nullptr, &index); + return index; +} + +bool InputMap::event_get_action_status(const Ref &p_event, const StringName &p_action, bool p_exact_match, bool *r_pressed, float *r_strength, float *r_raw_strength, int *r_event_index) const { HashMap::Iterator E = input_map.find(p_action); ERR_FAIL_COND_V_MSG(!E, false, suggest_actions(p_action)); @@ -236,7 +248,7 @@ bool InputMap::event_get_action_status(const Ref &p_event, const Str return input_event_action->get_action() == p_action; } - List>::Element *event = _find_event(E->value, p_event, p_exact_match, r_pressed, r_strength, r_raw_strength); + List>::Element *event = _find_event(E->value, p_event, p_exact_match, r_pressed, r_strength, r_raw_strength, r_event_index); return event != nullptr; } diff --git a/core/input/input_map.h b/core/input/input_map.h index b4d5beacb3b9..6407ea489ed2 100644 --- a/core/input/input_map.h +++ b/core/input/input_map.h @@ -61,7 +61,7 @@ private: HashMap>> default_builtin_cache; HashMap>> default_builtin_with_overrides_cache; - List>::Element *_find_event(Action &p_action, const Ref &p_event, bool p_exact_match = false, bool *r_pressed = nullptr, float *r_strength = nullptr, float *r_raw_strength = nullptr) const; + List>::Element *_find_event(Action &p_action, const Ref &p_event, bool p_exact_match = false, bool *r_pressed = nullptr, float *r_strength = nullptr, float *r_raw_strength = nullptr, int *r_event_index = nullptr) const; TypedArray _action_get_events(const StringName &p_action); TypedArray _get_actions(); @@ -86,7 +86,8 @@ public: const List> *action_get_events(const StringName &p_action); bool event_is_action(const Ref &p_event, const StringName &p_action, bool p_exact_match = false) const; - bool event_get_action_status(const Ref &p_event, const StringName &p_action, bool p_exact_match = false, bool *r_pressed = nullptr, float *r_strength = nullptr, float *r_raw_strength = nullptr) const; + int event_get_index(const Ref &p_event, const StringName &p_action, bool p_exact_match = false) const; + bool event_get_action_status(const Ref &p_event, const StringName &p_action, bool p_exact_match = false, bool *r_pressed = nullptr, float *r_strength = nullptr, float *r_raw_strength = nullptr, int *r_event_index = nullptr) const; const HashMap &get_action_map() const; void load_from_project_settings();