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

Rework input actions to be reliable #84685

Merged
merged 1 commit into from
Nov 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 67 additions & 52 deletions core/input/input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -695,53 +695,34 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
}

for (const KeyValue<StringName, InputMap::Action> &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;
}
}
const int event_index = InputMap::get_singleton()->event_get_index(p_event, E.key);
if (event_index == -1) {
continue;
}

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 &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.exact = InputMap::get_singleton()->event_is_action(p_event, E.key, true);
}
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 (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);
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) {
Expand Down Expand Up @@ -858,27 +839,29 @@ 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;
}

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;
}

Expand Down Expand Up @@ -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;

Expand Down
19 changes: 17 additions & 2 deletions core/input/input.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ class Input : public Object {

static Input *singleton;

static constexpr uint64_t MAX_EVENT = 31;

public:
enum MouseMode {
MOUSE_MODE_VISIBLE,
Expand Down Expand Up @@ -103,11 +105,22 @@ class Input : public Object {
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<float> strengths;
LocalVector<float> 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<StringName, Action> action_state;
Expand Down Expand Up @@ -227,6 +240,8 @@ class Input : public Object {
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<InputEvent> &p_event, bool p_is_emulated);

Expand Down
18 changes: 15 additions & 3 deletions core/input/input_map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,16 +127,21 @@ List<StringName> InputMap::get_actions() const {
return actions;
}

List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match, bool *r_pressed, float *r_strength, float *r_raw_strength) const {
List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Ref<InputEvent> &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<Ref<InputEvent>>::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;
Expand Down Expand Up @@ -179,6 +184,7 @@ void InputMap::action_erase_event(const StringName &p_action, const Ref<InputEve
List<Ref<InputEvent>>::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);
}
Expand Down Expand Up @@ -216,7 +222,13 @@ bool InputMap::event_is_action(const Ref<InputEvent> &p_event, const StringName
return event_get_action_status(p_event, p_action, p_exact_match);
}

bool InputMap::event_get_action_status(const Ref<InputEvent> &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<InputEvent> &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<InputEvent> &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<StringName, Action>::Iterator E = input_map.find(p_action);
ERR_FAIL_COND_V_MSG(!E, false, suggest_actions(p_action));

Expand All @@ -236,7 +248,7 @@ bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const Str
return input_event_action->get_action() == p_action;
}

List<Ref<InputEvent>>::Element *event = _find_event(E->value, p_event, p_exact_match, r_pressed, r_strength, r_raw_strength);
List<Ref<InputEvent>>::Element *event = _find_event(E->value, p_event, p_exact_match, r_pressed, r_strength, r_raw_strength, r_event_index);
return event != nullptr;
}

Expand Down
5 changes: 3 additions & 2 deletions core/input/input_map.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ class InputMap : public Object {
HashMap<String, List<Ref<InputEvent>>> default_builtin_cache;
HashMap<String, List<Ref<InputEvent>>> default_builtin_with_overrides_cache;

List<Ref<InputEvent>>::Element *_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match = false, bool *r_pressed = nullptr, float *r_strength = nullptr, float *r_raw_strength = nullptr) const;
List<Ref<InputEvent>>::Element *_find_event(Action &p_action, const Ref<InputEvent> &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<InputEvent> _action_get_events(const StringName &p_action);
TypedArray<StringName> _get_actions();
Expand All @@ -86,7 +86,8 @@ class InputMap : public Object {

const List<Ref<InputEvent>> *action_get_events(const StringName &p_action);
bool event_is_action(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match = false) const;
bool event_get_action_status(const Ref<InputEvent> &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<InputEvent> &p_event, const StringName &p_action, bool p_exact_match = false) const;
bool event_get_action_status(const Ref<InputEvent> &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<StringName, Action> &get_action_map() const;
void load_from_project_settings();
Expand Down
Loading