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

Allow checking for exact matches with Action events. #44355

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
43 changes: 30 additions & 13 deletions core/input/input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,11 @@ void Input::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_key_pressed", "keycode"), &Input::is_key_pressed);
ClassDB::bind_method(D_METHOD("is_mouse_button_pressed", "button"), &Input::is_mouse_button_pressed);
ClassDB::bind_method(D_METHOD("is_joy_button_pressed", "device", "button"), &Input::is_joy_button_pressed);
ClassDB::bind_method(D_METHOD("is_action_pressed", "action"), &Input::is_action_pressed);
ClassDB::bind_method(D_METHOD("is_action_just_pressed", "action"), &Input::is_action_just_pressed);
ClassDB::bind_method(D_METHOD("is_action_just_released", "action"), &Input::is_action_just_released);
ClassDB::bind_method(D_METHOD("get_action_strength", "action"), &Input::get_action_strength);
ClassDB::bind_method(D_METHOD("get_action_raw_strength", "action"), &Input::get_action_strength);
ClassDB::bind_method(D_METHOD("is_action_pressed", "action", "exact"), &Input::is_action_pressed, DEFVAL(false));
ClassDB::bind_method(D_METHOD("is_action_just_pressed", "action", "exact"), &Input::is_action_just_pressed, DEFVAL(false));
ClassDB::bind_method(D_METHOD("is_action_just_released", "action", "exact"), &Input::is_action_just_released, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_action_strength", "action", "exact"), &Input::get_action_strength, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_action_raw_strength", "action", "exact"), &Input::get_action_strength, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_axis", "negative_action", "positive_action"), &Input::get_axis);
ClassDB::bind_method(D_METHOD("get_vector", "negative_x", "positive_x", "negative_y", "positive_y", "deadzone"), &Input::get_vector, DEFVAL(-1.0f));
ClassDB::bind_method(D_METHOD("add_joy_mapping", "mapping", "update_existing"), &Input::add_joy_mapping, DEFVAL(false));
Expand Down Expand Up @@ -234,51 +234,67 @@ bool Input::is_joy_button_pressed(int p_device, int p_button) const {
return joy_buttons_pressed.has(_combine_device(p_button, p_device));
}

bool Input::is_action_pressed(const StringName &p_action) const {
return action_state.has(p_action) && action_state[p_action].pressed;
bool Input::is_action_pressed(const StringName &p_action, bool p_exact) const {
return action_state.has(p_action) && action_state[p_action].pressed && (p_exact ? action_state[p_action].exact : true);
}

bool Input::is_action_just_pressed(const StringName &p_action) const {
bool Input::is_action_just_pressed(const StringName &p_action, bool p_exact) const {
const Map<StringName, Action>::Element *E = action_state.find(p_action);
if (!E) {
return false;
}

if (p_exact && E->get().exact == false) {
return false;
}

if (Engine::get_singleton()->is_in_physics_frame()) {
return E->get().pressed && E->get().physics_frame == Engine::get_singleton()->get_physics_frames();
} else {
return E->get().pressed && E->get().idle_frame == Engine::get_singleton()->get_idle_frames();
}
}

bool Input::is_action_just_released(const StringName &p_action) const {
bool Input::is_action_just_released(const StringName &p_action, bool p_exact) const {
const Map<StringName, Action>::Element *E = action_state.find(p_action);
if (!E) {
return false;
}

if (p_exact && E->get().exact == false) {
return false;
}

if (Engine::get_singleton()->is_in_physics_frame()) {
return !E->get().pressed && E->get().physics_frame == Engine::get_singleton()->get_physics_frames();
} else {
return !E->get().pressed && E->get().idle_frame == Engine::get_singleton()->get_idle_frames();
}
}

float Input::get_action_strength(const StringName &p_action) const {
float Input::get_action_strength(const StringName &p_action, bool p_exact) const {
const Map<StringName, Action>::Element *E = action_state.find(p_action);
if (!E) {
return 0.0f;
}

if (p_exact && E->get().exact == false) {
return 0.0f;
}

return E->get().strength;
}

float Input::get_action_raw_strength(const StringName &p_action) const {
float Input::get_action_raw_strength(const StringName &p_action, bool p_exact) const {
const Map<StringName, Action>::Element *E = action_state.find(p_action);
if (!E) {
return 0.0f;
}

if (p_exact && E->get().exact == false) {
return 0.0f;
}

return E->get().raw_strength;
}

Expand Down Expand Up @@ -584,14 +600,15 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em

for (const Map<StringName, InputMap::Action>::Element *E = InputMap::get_singleton()->get_action_map().front(); E; E = E->next()) {
if (InputMap::get_singleton()->event_is_action(p_event, E->key())) {
// Save the action's state
if (!p_event->is_echo() && is_action_pressed(E->key()) != p_event->is_action_pressed(E->key())) {
// If not echo and action pressed state has changed
if (!p_event->is_echo() && is_action_pressed(E->key(), false) != p_event->is_action_pressed(E->key())) {
Action action;
action.physics_frame = Engine::get_singleton()->get_physics_frames();
action.idle_frame = Engine::get_singleton()->get_idle_frames();
action.pressed = p_event->is_action_pressed(E->key());
action.strength = 0.0f;
action.raw_strength = 0.0f;
action.exact = InputMap::get_singleton()->event_is_action(p_event, E->key(), true);
action_state[E->key()] = action;
}
action_state[E->key()].strength = p_event->get_action_strength(E->key());
Expand Down
11 changes: 6 additions & 5 deletions core/input/input.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ class Input : public Object {
uint64_t physics_frame;
uint64_t idle_frame;
bool pressed;
bool exact;
float strength;
float raw_strength;
};
Expand Down Expand Up @@ -261,11 +262,11 @@ class Input : public Object {
bool is_key_pressed(int p_keycode) const;
bool is_mouse_button_pressed(int p_button) const;
bool is_joy_button_pressed(int p_device, int p_button) const;
bool is_action_pressed(const StringName &p_action) const;
bool is_action_just_pressed(const StringName &p_action) const;
bool is_action_just_released(const StringName &p_action) const;
float get_action_strength(const StringName &p_action) const;
float get_action_raw_strength(const StringName &p_action) const;
bool is_action_pressed(const StringName &p_action, bool p_exact = false) const;
bool is_action_just_pressed(const StringName &p_action, bool p_exact = false) const;
bool is_action_just_released(const StringName &p_action, bool p_exact = false) const;
float get_action_strength(const StringName &p_action, bool p_exact = false) const;
float get_action_raw_strength(const StringName &p_action, bool p_exact = false) const;

float get_axis(const StringName &p_negative_action, const StringName &p_positive_action) const;
Vector2 get_vector(const StringName &p_negative_x, const StringName &p_positive_x, const StringName &p_negative_y, const StringName &p_positive_y, float p_deadzone = -1.0f) const;
Expand Down
28 changes: 14 additions & 14 deletions core/input/input_event.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,31 +44,31 @@ int InputEvent::get_device() const {
return device;
}

bool InputEvent::is_action(const StringName &p_action) const {
return InputMap::get_singleton()->event_is_action(Ref<InputEvent>((InputEvent *)this), p_action);
bool InputEvent::is_action(const StringName &p_action, bool p_exact_match) const {
return InputMap::get_singleton()->event_is_action(Ref<InputEvent>((InputEvent *)this), p_action, p_exact_match);
}

bool InputEvent::is_action_pressed(const StringName &p_action, bool p_allow_echo) const {
bool InputEvent::is_action_pressed(const StringName &p_action, bool p_allow_echo, bool p_exact_match) const {
bool pressed;
bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, &pressed);
bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, p_exact_match, &pressed, nullptr, nullptr);
return valid && pressed && (p_allow_echo || !is_echo());
}

bool InputEvent::is_action_released(const StringName &p_action) const {
bool InputEvent::is_action_released(const StringName &p_action, bool p_exact_match) const {
bool pressed;
bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, &pressed);
bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, p_exact_match, &pressed, nullptr, nullptr);
return valid && !pressed;
}

float InputEvent::get_action_strength(const StringName &p_action) const {
float InputEvent::get_action_strength(const StringName &p_action, bool p_exact_match) const {
float strength;
bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, nullptr, &strength);
bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, p_exact_match, nullptr, &strength, nullptr);
return valid ? strength : 0.0f;
}

float InputEvent::get_action_raw_strength(const StringName &p_action) const {
float InputEvent::get_action_raw_strength(const StringName &p_action, bool p_exact_match) const {
float raw_strength;
bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, nullptr, nullptr, &raw_strength);
bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, p_exact_match, nullptr, nullptr, &raw_strength);
return valid ? raw_strength : 0.0f;
}

Expand Down Expand Up @@ -100,10 +100,10 @@ void InputEvent::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_device", "device"), &InputEvent::set_device);
ClassDB::bind_method(D_METHOD("get_device"), &InputEvent::get_device);

ClassDB::bind_method(D_METHOD("is_action", "action"), &InputEvent::is_action);
ClassDB::bind_method(D_METHOD("is_action_pressed", "action", "allow_echo"), &InputEvent::is_action_pressed, DEFVAL(false));
ClassDB::bind_method(D_METHOD("is_action_released", "action"), &InputEvent::is_action_released);
ClassDB::bind_method(D_METHOD("get_action_strength", "action"), &InputEvent::get_action_strength);
ClassDB::bind_method(D_METHOD("is_action", "action", "exact_match"), &InputEvent::is_action, DEFVAL(false));
ClassDB::bind_method(D_METHOD("is_action_pressed", "action", "allow_echo", "exact_match"), &InputEvent::is_action_pressed, DEFVAL(false), DEFVAL(false));
ClassDB::bind_method(D_METHOD("is_action_released", "action", "exact_match"), &InputEvent::is_action_released, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_action_strength", "action", "exact_match"), &InputEvent::get_action_strength, DEFVAL(false));

ClassDB::bind_method(D_METHOD("is_pressed"), &InputEvent::is_pressed);
ClassDB::bind_method(D_METHOD("is_echo"), &InputEvent::is_echo);
Expand Down
10 changes: 5 additions & 5 deletions core/input/input_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,11 @@ class InputEvent : public Resource {
void set_device(int p_device);
int get_device() const;

bool is_action(const StringName &p_action) const;
bool is_action_pressed(const StringName &p_action, bool p_allow_echo = false) const;
bool is_action_released(const StringName &p_action) const;
float get_action_strength(const StringName &p_action) const;
float get_action_raw_strength(const StringName &p_action) const;
bool is_action(const StringName &p_action, bool p_exact_match = false) const;
bool is_action_pressed(const StringName &p_action, bool p_allow_echo = false, bool p_exact_match = false) const;
bool is_action_released(const StringName &p_action, bool p_exact_match = false) const;
float get_action_strength(const StringName &p_action, bool p_exact_match = false) const;
float get_action_raw_strength(const StringName &p_action, bool p_exact_match = false) const;

// To be removed someday, since they do not make sense for all events
virtual bool is_pressed() const;
Expand Down
22 changes: 12 additions & 10 deletions core/input/input_map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ void InputMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("action_erase_event", "action", "event"), &InputMap::action_erase_event);
ClassDB::bind_method(D_METHOD("action_erase_events", "action"), &InputMap::action_erase_events);
ClassDB::bind_method(D_METHOD("action_get_events", "action"), &InputMap::_action_get_events);
ClassDB::bind_method(D_METHOD("event_is_action", "event", "action"), &InputMap::event_is_action);
ClassDB::bind_method(D_METHOD("event_is_action", "event", "action", "exact_match"), &InputMap::event_is_action, DEFVAL(false));
ClassDB::bind_method(D_METHOD("load_from_project_settings"), &InputMap::load_from_project_settings);
}

Expand Down Expand Up @@ -95,7 +95,7 @@ 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_pressed, float *p_strength, float *p_raw_strength) const {
List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match, bool *p_pressed, float *p_strength, float *p_raw_strength) const {
ERR_FAIL_COND_V(!p_event.is_valid(), nullptr);

for (List<Ref<InputEvent>>::Element *E = p_action.inputs.front(); E; E = E->next()) {
Expand All @@ -106,7 +106,9 @@ List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Re

int device = e->get_device();
if (device == ALL_DEVICES || device == p_event->get_device()) {
if (e->action_match(p_event, p_pressed, p_strength, p_raw_strength, p_action.deadzone)) {
if (p_exact_match && e->shortcut_match(p_event)) {
return E;
} else if (!p_exact_match && e->action_match(p_event, p_pressed, p_strength, p_raw_strength, p_action.deadzone)) {
return E;
Comment on lines +109 to 112
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is where the check actually happens.

"Action Match" has remained "fuzzy" as it was previously, and "Shortcut Match" is used to do the exact check.

This could potentially be changed to pass the "p_exact" parameter to action_match and just do the comparison inside that method.

}
}
Expand Down Expand Up @@ -134,7 +136,7 @@ void InputMap::action_set_deadzone(const StringName &p_action, float p_deadzone)
void InputMap::action_add_event(const StringName &p_action, const Ref<InputEvent> &p_event) {
ERR_FAIL_COND_MSG(p_event.is_null(), "It's not a reference to a valid InputEvent object.");
ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'.");
if (_find_event(input_map[p_action], p_event)) {
if (_find_event(input_map[p_action], p_event, true)) {
return; // Already addded.
}

Expand All @@ -143,13 +145,13 @@ void InputMap::action_add_event(const StringName &p_action, const Ref<InputEvent

bool InputMap::action_has_event(const StringName &p_action, const Ref<InputEvent> &p_event) {
ERR_FAIL_COND_V_MSG(!input_map.has(p_action), false, "Request for nonexistent InputMap action '" + String(p_action) + "'.");
return (_find_event(input_map[p_action], p_event) != nullptr);
return (_find_event(input_map[p_action], p_event, true) != nullptr);
}

void InputMap::action_erase_event(const StringName &p_action, const Ref<InputEvent> &p_event) {
ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'.");

List<Ref<InputEvent>>::Element *E = _find_event(input_map[p_action], p_event);
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)) {
Expand Down Expand Up @@ -185,11 +187,11 @@ const List<Ref<InputEvent>> *InputMap::action_get_events(const StringName &p_act
return &E->get().inputs;
}

bool InputMap::event_is_action(const Ref<InputEvent> &p_event, const StringName &p_action) const {
return event_get_action_status(p_event, p_action);
bool InputMap::event_is_action(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match) const {
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_pressed, float *p_strength, float *p_raw_strength) const {
bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match, bool *p_pressed, float *p_strength, float *p_raw_strength) const {
Map<StringName, Action>::Element *E = input_map.find(p_action);
ERR_FAIL_COND_V_MSG(!E, false, "Request for nonexistent InputMap action '" + String(p_action) + "'.");

Expand All @@ -207,7 +209,7 @@ bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const Str
bool pressed;
float strength;
float raw_strength;
List<Ref<InputEvent>>::Element *event = _find_event(E->get(), p_event, &pressed, &strength, &raw_strength);
List<Ref<InputEvent>>::Element *event = _find_event(E->get(), p_event, p_exact_match, &pressed, &strength, &raw_strength);
if (event != nullptr) {
if (p_pressed != nullptr) {
*p_pressed = pressed;
Expand Down
6 changes: 3 additions & 3 deletions core/input/input_map.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class InputMap : public Object {

mutable Map<StringName, Action> input_map;

List<Ref<InputEvent>>::Element *_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool *p_pressed = nullptr, float *p_strength = nullptr, float *p_raw_strength = nullptr) const;
List<Ref<InputEvent>>::Element *_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match = false, bool *p_pressed = nullptr, float *p_strength = nullptr, float *p_raw_strength = nullptr) const;

Array _action_get_events(const StringName &p_action);
Array _get_actions();
Expand All @@ -78,8 +78,8 @@ class InputMap : public Object {
void action_erase_events(const StringName &p_action);

const List<Ref<InputEvent>> *action_get_events(const StringName &p_action);
bool event_is_action(const Ref<InputEvent> &p_event, const StringName &p_action) const;
bool event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool *p_pressed = nullptr, float *p_strength = nullptr, float *p_raw_strength = nullptr) const;
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 *p_pressed = nullptr, float *p_strength = nullptr, float *p_raw_strength = nullptr) const;

const Map<StringName, Action> &get_action_map() const;
void load_from_project_settings();
Expand Down