diff --git a/core/input_map.cpp b/core/input_map.cpp index 6c82b8dc87cb..ee94657ed1f8 100644 --- a/core/input_map.cpp +++ b/core/input_map.cpp @@ -125,7 +125,7 @@ List InputMap::get_actions() const { return actions; } -List>::Element *InputMap::_find_event(Action &p_action, const Ref &p_event, bool *p_pressed, float *p_strength) const { +List>::Element *InputMap::_find_event(Action &p_action, const Ref &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength) const { ERR_FAIL_COND_V(!p_event.is_valid(), nullptr); for (List>::Element *E = p_action.inputs.front(); E; E = E->next()) { @@ -136,7 +136,7 @@ List>::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_action.deadzone)) { + if (e->action_match(p_event, p_pressed, p_strength, p_raw_strength, p_action.deadzone)) { return E; } } @@ -221,7 +221,7 @@ bool InputMap::event_is_action(const Ref &p_event, const StringName return event_get_action_status(p_event, p_action); } -bool InputMap::event_get_action_status(const Ref &p_event, const StringName &p_action, bool *p_pressed, float *p_strength) const { +bool InputMap::event_get_action_status(const Ref &p_event, const StringName &p_action, bool *p_pressed, float *p_strength, float *p_raw_strength) const { Map::Element *E = input_map.find(p_action); ERR_FAIL_COND_V_MSG(!E, false, _suggest_actions(p_action)); @@ -238,7 +238,8 @@ bool InputMap::event_get_action_status(const Ref &p_event, const Str bool pressed; float strength; - List>::Element *event = _find_event(E->get(), p_event, &pressed, &strength); + float raw_strength; + List>::Element *event = _find_event(E->get(), p_event, &pressed, &strength, &raw_strength); if (event != nullptr) { if (p_pressed != nullptr) { *p_pressed = pressed; @@ -246,6 +247,9 @@ bool InputMap::event_get_action_status(const Ref &p_event, const Str if (p_strength != nullptr) { *p_strength = strength; } + if (p_raw_strength != nullptr) { + *p_raw_strength = raw_strength; + } return true; } else { return false; diff --git a/core/input_map.h b/core/input_map.h index 1d4460750bcb..c2aa131882f1 100644 --- a/core/input_map.h +++ b/core/input_map.h @@ -54,7 +54,7 @@ class InputMap : public Object { mutable Map input_map; - List>::Element *_find_event(Action &p_action, const Ref &p_event, bool *p_pressed = nullptr, float *p_strength = nullptr) const; + List>::Element *_find_event(Action &p_action, const Ref &p_event, bool *p_pressed = nullptr, float *p_strength = nullptr, float *p_raw_strength = nullptr) const; Array _get_action_list(const StringName &p_action); Array _get_actions(); @@ -80,7 +80,7 @@ class InputMap : public Object { const List> *get_action_list(const StringName &p_action); bool event_is_action(const Ref &p_event, const StringName &p_action) const; - bool event_get_action_status(const Ref &p_event, const StringName &p_action, bool *p_pressed = nullptr, float *p_strength = nullptr) const; + bool event_get_action_status(const Ref &p_event, const StringName &p_action, bool *p_pressed = nullptr, float *p_strength = nullptr, float *p_raw_strength = nullptr) const; const Map &get_action_map() const; void load_from_globals(); diff --git a/core/os/input.cpp b/core/os/input.cpp index 898e8710bdb6..eb046ab645d3 100644 --- a/core/os/input.cpp +++ b/core/os/input.cpp @@ -61,6 +61,9 @@ void Input::_bind_methods() { 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_raw_strength); + 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)); ClassDB::bind_method(D_METHOD("remove_joy_mapping", "guid"), &Input::remove_joy_mapping); ClassDB::bind_method(D_METHOD("joy_connection_changed", "device", "connected", "name", "guid"), &Input::joy_connection_changed); @@ -125,10 +128,13 @@ void Input::_bind_methods() { void Input::get_argument_options(const StringName &p_function, int p_idx, List *r_options) const { #ifdef TOOLS_ENABLED - const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", 0) ? "'" : "\""; + const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", false) ? "'" : "\""; String pf = p_function; - if (p_idx == 0 && (pf == "is_action_pressed" || pf == "action_press" || pf == "action_release" || pf == "is_action_just_pressed" || pf == "is_action_just_released" || pf == "get_action_strength")) { + if (p_idx == 0 && (pf == "is_action_pressed" || pf == "action_press" || pf == "action_release" || + pf == "is_action_just_pressed" || pf == "is_action_just_released" || + pf == "get_action_strength" || pf == "get_action_raw_strength" || + pf == "get_axis" || pf == "get_vector")) { List pinfo; ProjectSettings::get_singleton()->get_property_list(&pinfo); diff --git a/core/os/input.h b/core/os/input.h index d84dedbce196..346732355c77 100644 --- a/core/os/input.h +++ b/core/os/input.h @@ -85,6 +85,10 @@ class Input : public Object { virtual bool is_action_just_pressed(const StringName &p_action) const = 0; virtual bool is_action_just_released(const StringName &p_action) const = 0; virtual float get_action_strength(const StringName &p_action) const = 0; + virtual float get_action_raw_strength(const StringName &p_action) const = 0; + + 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; virtual float get_joy_axis(int p_device, int p_axis) const = 0; virtual String get_joy_name(int p_idx) = 0; diff --git a/core/os/input_event.cpp b/core/os/input_event.cpp index fa6d4acdf171..dbda4ac0944e 100644 --- a/core/os/input_event.cpp +++ b/core/os/input_event.cpp @@ -61,12 +61,17 @@ bool InputEvent::is_action_released(const StringName &p_action) const { } float InputEvent::get_action_strength(const StringName &p_action) const { - bool pressed; float strength; - bool valid = InputMap::get_singleton()->event_get_action_status(Ref((InputEvent *)this), p_action, &pressed, &strength); + bool valid = InputMap::get_singleton()->event_get_action_status(Ref((InputEvent *)this), p_action, nullptr, &strength); return valid ? strength : 0.0f; } +float InputEvent::get_action_raw_strength(const StringName &p_action) const { + float raw_strength; + bool valid = InputMap::get_singleton()->event_get_action_status(Ref((InputEvent *)this), p_action, nullptr, nullptr, &raw_strength); + return valid ? raw_strength : 0.0f; +} + bool InputEvent::is_pressed() const { return false; } @@ -83,7 +88,7 @@ String InputEvent::as_text() const { return String(); } -bool InputEvent::action_match(const Ref &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const { +bool InputEvent::action_match(const Ref &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const { return false; } @@ -290,7 +295,7 @@ String InputEventKey::as_text() const { return kc; } -bool InputEventKey::action_match(const Ref &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const { +bool InputEventKey::action_match(const Ref &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const { Ref key = p_event; if (key.is_null()) { return false; @@ -460,7 +465,7 @@ Ref InputEventMouseButton::xformed_by(const Transform2D &p_xform, co return mb; } -bool InputEventMouseButton::action_match(const Ref &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const { +bool InputEventMouseButton::action_match(const Ref &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const { Ref mb = p_event; if (mb.is_null()) { return false; @@ -709,7 +714,7 @@ bool InputEventJoypadMotion::is_pressed() const { return Math::abs(axis_value) >= 0.5f; } -bool InputEventJoypadMotion::action_match(const Ref &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const { +bool InputEventJoypadMotion::action_match(const Ref &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const { Ref jm = p_event; if (jm.is_null()) { return false; @@ -780,7 +785,7 @@ float InputEventJoypadButton::get_pressure() const { return pressure; } -bool InputEventJoypadButton::action_match(const Ref &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const { +bool InputEventJoypadButton::action_match(const Ref &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const { Ref jb = p_event; if (jb.is_null()) { return false; @@ -999,7 +1004,7 @@ bool InputEventAction::is_action(const StringName &p_action) const { return action == p_action; } -bool InputEventAction::action_match(const Ref &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const { +bool InputEventAction::action_match(const Ref &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const { Ref act = p_event; if (act.is_null()) { return false; diff --git a/core/os/input_event.h b/core/os/input_event.h index 4ae4933d616d..50f83d613a69 100644 --- a/core/os/input_event.h +++ b/core/os/input_event.h @@ -202,6 +202,7 @@ class InputEvent : public Resource { 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; // To be removed someday, since they do not make sense for all events virtual bool is_pressed() const; @@ -212,7 +213,7 @@ class InputEvent : public Resource { virtual Ref xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const; - virtual bool action_match(const Ref &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const; + virtual bool action_match(const Ref &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const; virtual bool shortcut_match(const Ref &p_event) const; virtual bool is_action_type() const; @@ -298,7 +299,7 @@ class InputEventKey : public InputEventWithModifiers { uint32_t get_scancode_with_modifiers() const; uint32_t get_physical_scancode_with_modifiers() const; - virtual bool action_match(const Ref &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const; + virtual bool action_match(const Ref &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const; virtual bool shortcut_match(const Ref &p_event) const; virtual bool is_action_type() const { return true; } @@ -357,7 +358,7 @@ class InputEventMouseButton : public InputEventMouse { bool is_doubleclick() const; virtual Ref xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const; - virtual bool action_match(const Ref &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const; + virtual bool action_match(const Ref &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const; virtual bool is_action_type() const { return true; } virtual String as_text() const; @@ -414,7 +415,7 @@ class InputEventJoypadMotion : public InputEvent { virtual bool is_pressed() const; - virtual bool action_match(const Ref &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const; + virtual bool action_match(const Ref &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const; virtual bool is_action_type() const { return true; } virtual String as_text() const; @@ -441,7 +442,7 @@ class InputEventJoypadButton : public InputEvent { void set_pressure(float p_pressure); float get_pressure() const; - virtual bool action_match(const Ref &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const; + virtual bool action_match(const Ref &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const; virtual bool shortcut_match(const Ref &p_event) const; virtual bool is_action_type() const { return true; } @@ -526,7 +527,7 @@ class InputEventAction : public InputEvent { virtual bool is_action(const StringName &p_action) const; - virtual bool action_match(const Ref &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const; + virtual bool action_match(const Ref &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const; virtual bool shortcut_match(const Ref &p_event) const; virtual bool is_action_type() const { return true; } diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml index a3d7ec17267b..aca494d72b14 100644 --- a/doc/classes/Input.xml +++ b/doc/classes/Input.xml @@ -54,6 +54,15 @@ [b]Note:[/b] This method only works on iOS, Android, and UWP. On other platforms, it always returns [constant Vector3.ZERO]. On Android the unit of measurement for each axis is m/s² while on iOS and UWP it's a multiple of the Earth's gravitational acceleration [code]g[/code] (~9.81 m/s²). + + + + + + + Returns a value between 0 and 1 representing the raw intensity of the given action, ignoring the action's deadzone. In most cases, you should use [method get_action_strength] instead. + + @@ -63,6 +72,18 @@ Returns a value between 0 and 1 representing the intensity of the given action. In a joypad, for example, the further away the axis (analog sticks or L2, R2 triggers) is from the dead zone, the closer the value will be to 1. If the action is mapped to a control that has no axis as the keyboard, the value returned will be 0 or 1. + + + + + + + + + Get axis input by specifying two actions, one negative and one positive. + This is a shorthand for writing [code]Input.get_action_strength("positive_action") - Input.get_action_strength("negative_action")[/code]. + + @@ -205,6 +226,25 @@ Returns the mouse mode. See the constants for more information. + + + + + + + + + + + + + + + Gets an input vector by specifying four actions for the positive and negative X and Y axes. + This method is useful when getting vector input, such as from a joystick, directional pad, arrows, or WASD. The vector has its length limited to 1 and has a circular deadzone, which is useful for using vector input as movement. + By default, the deadzone is automatically calculated from the average of the action deadzones. However, you can override the deadzone to be whatever you want (on the range of 0 to 1). + + diff --git a/main/input_default.cpp b/main/input_default.cpp index ba5c02bce0ea..e439bbdfbd79 100644 --- a/main/input_default.cpp +++ b/main/input_default.cpp @@ -145,6 +145,50 @@ float InputDefault::get_action_strength(const StringName &p_action) const { return E->get().strength; } +float InputDefault::get_action_raw_strength(const StringName &p_action) const { +#ifdef DEBUG_ENABLED + bool has_action = InputMap::get_singleton()->has_action(p_action); + ERR_FAIL_COND_V_MSG(!has_action, false, "Request for nonexistent InputMap action '" + String(p_action) + "'."); +#endif + const Map::Element *E = action_state.find(p_action); + if (!E) { + return 0.0f; + } + + return E->get().raw_strength; +} + +float Input::get_axis(const StringName &p_negative_action, const StringName &p_positive_action) const { + return get_action_strength(p_positive_action) - get_action_strength(p_negative_action); +} + +Vector2 Input::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) const { + Vector2 vector = Vector2( + get_action_raw_strength(p_positive_x) - get_action_raw_strength(p_negative_x), + get_action_raw_strength(p_positive_y) - get_action_raw_strength(p_negative_y)); + + if (p_deadzone < 0.0f) { + // If the deadzone isn't specified, get it from the average of the actions. + p_deadzone = (InputMap::get_singleton()->action_get_deadzone(p_positive_x) + + InputMap::get_singleton()->action_get_deadzone(p_negative_x) + + InputMap::get_singleton()->action_get_deadzone(p_positive_y) + + InputMap::get_singleton()->action_get_deadzone(p_negative_y)) / + 4; + } + + // Circular length limiting and deadzone. + float length = vector.length(); + if (length <= p_deadzone) { + return Vector2(); + } else if (length > 1.0f) { + return vector / length; + } else { + // Inverse lerp length to map (p_deadzone, 1) to (0, 1). + return vector * (Math::inverse_lerp(p_deadzone, 1.0f, length) / length); + } + return vector; +} + float InputDefault::get_joy_axis(int p_device, int p_axis) const { _THREAD_SAFE_METHOD_ int c = _combine_device(p_axis, p_device); @@ -424,10 +468,12 @@ void InputDefault::_parse_input_event_impl(const Ref &p_event, bool 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.f; + action.strength = 0.0f; + action.raw_strength = 0.0f; action_state[E->key()] = action; } action_state[E->key()].strength = p_event->get_action_strength(E->key()); + action_state[E->key()].raw_strength = p_event->get_action_raw_strength(E->key()); } } diff --git a/main/input_default.h b/main/input_default.h index 281e30efefff..973af6ef413a 100644 --- a/main/input_default.h +++ b/main/input_default.h @@ -55,6 +55,7 @@ class InputDefault : public Input { uint64_t idle_frame; bool pressed; float strength; + float raw_strength; }; Map action_state; @@ -224,6 +225,7 @@ class InputDefault : public Input { virtual bool is_action_just_pressed(const StringName &p_action) const; virtual bool is_action_just_released(const StringName &p_action) const; virtual float get_action_strength(const StringName &p_action) const; + virtual float get_action_raw_strength(const StringName &p_action) const; virtual float get_joy_axis(int p_device, int p_axis) const; String get_joy_name(int p_idx);