Skip to content

Commit

Permalink
Allow getting Input axis/vector values by specifying multiple actions
Browse files Browse the repository at this point in the history
For get_vector, use raw values and handle deadzones appropriately
  • Loading branch information
aaronfranke committed Nov 11, 2020
1 parent 195d58b commit 4abf189
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 2 deletions.
38 changes: 37 additions & 1 deletion core/input/input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,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_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);
Expand Down Expand Up @@ -215,7 +218,9 @@ void Input::get_argument_options(const StringName &p_function, int p_idx, List<S
const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", 0) ? "'" : "\"";

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_axis" || pf == "get_vector")) {
List<PropertyInfo> pinfo;
ProjectSettings::get_singleton()->get_property_list(&pinfo);

Expand Down Expand Up @@ -335,6 +340,37 @@ float Input::get_action_raw_strength(const StringName &p_action) const {
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 Input::get_joy_axis(int p_device, int p_axis) const {
_THREAD_SAFE_METHOD_
int c = _combine_device(p_axis, p_device);
Expand Down
4 changes: 3 additions & 1 deletion core/input/input.h
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,6 @@ class Input : public Object {
JoyAxisList _get_output_axis(String output);
void _button_event(int p_device, int p_index, bool p_pressed);
void _axis_event(int p_device, int p_axis, float p_value);
float _handle_deadzone(int p_device, int p_axis, float p_value);

void _parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_emulated);

Expand Down Expand Up @@ -269,6 +268,9 @@ class Input : public Object {
float get_action_strength(const StringName &p_action) const;
float get_action_raw_strength(const StringName &p_action) 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;

float get_joy_axis(int p_device, int p_axis) const;
String get_joy_name(int p_idx);
Array get_connected_joypads();
Expand Down
6 changes: 6 additions & 0 deletions core/input/input_map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,12 @@ bool InputMap::has_action(const StringName &p_action) const {
return input_map.has(p_action);
}

float InputMap::action_get_deadzone(const StringName &p_action) {
ERR_FAIL_COND_V_MSG(!input_map.has(p_action), 0.0f, "Request for nonexistent InputMap action '" + String(p_action) + "'.");

return input_map[p_action].deadzone;
}

void InputMap::action_set_deadzone(const StringName &p_action, float p_deadzone) {
ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'.");

Expand Down
1 change: 1 addition & 0 deletions core/input/input_map.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ class InputMap : public Object {
void add_action(const StringName &p_action, float p_deadzone = 0.5);
void erase_action(const StringName &p_action);

float action_get_deadzone(const StringName &p_action);
void action_set_deadzone(const StringName &p_action, float p_deadzone);
void action_add_event(const StringName &p_action, const Ref<InputEvent> &p_event);
bool action_has_event(const StringName &p_action, const Ref<InputEvent> &p_event);
Expand Down
40 changes: 40 additions & 0 deletions doc/classes/Input.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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].
</description>
</method>
<method name="get_action_raw_strength" qualifiers="const">
<return type="float">
</return>
<argument index="0" name="action" type="StringName">
</argument>
<description>
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.
</description>
</method>
<method name="get_action_strength" qualifiers="const">
<return type="float">
</return>
Expand All @@ -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.
</description>
</method>
<method name="get_axis" qualifiers="const">
<return type="float">
</return>
<argument index="0" name="negative_action" type="StringName">
</argument>
<argument index="1" name="positive_action" type="StringName">
</argument>
<description>
Get axis input by specifying two actions, one negative and one positive.
This is a horthand for writing [code]Input.get_action_strength("positive_action") - Input.get_action_strength("negative_action")[/code].
</description>
</method>
<method name="get_connected_joypads">
<return type="Array">
</return>
Expand Down Expand Up @@ -205,6 +226,25 @@
Returns the mouse mode. See the constants for more information.
</description>
</method>
<method name="get_vector" qualifiers="const">
<return type="Vector2">
</return>
<argument index="0" name="negative_x" type="StringName">
</argument>
<argument index="1" name="positive_x" type="StringName">
</argument>
<argument index="2" name="negative_y" type="StringName">
</argument>
<argument index="3" name="positive_y" type="StringName">
</argument>
<argument index="4" name="deadzone" type="float" default="-1.0">
</argument>
<description>
Get vector input by specifying four actions, two for the X axis and two for the Y axis, negative and positive.
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).
</description>
</method>
<method name="is_action_just_pressed" qualifiers="const">
<return type="bool">
</return>
Expand Down

0 comments on commit 4abf189

Please sign in to comment.