Skip to content

Commit

Permalink
Merge pull request godotengine#36 from lawnjelly/plus_input_just_pressed
Browse files Browse the repository at this point in the history
Input - fix just pressed and released with short presses
  • Loading branch information
lawnjelly authored May 15, 2023
2 parents f84239c + c6fb694 commit 455ff1f
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 39 deletions.
4 changes: 2 additions & 2 deletions core/os/input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ void Input::_bind_methods() {
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", "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("is_action_just_pressed", "action", "exact", "current"), &Input::is_action_just_pressed, DEFVAL(false), DEFVAL(false));
ClassDB::bind_method(D_METHOD("is_action_just_released", "action", "exact", "current"), &Input::is_action_just_released, DEFVAL(false), 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_raw_strength, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_axis", "negative_action", "positive_action"), &Input::get_axis);
Expand Down
4 changes: 2 additions & 2 deletions core/os/input.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ class Input : public Object {
virtual bool is_mouse_button_pressed(int p_button) const = 0;
virtual bool is_joy_button_pressed(int p_device, int p_button) const = 0;
virtual bool is_action_pressed(const StringName &p_action, bool p_exact = false) const = 0;
virtual bool is_action_just_pressed(const StringName &p_action, bool p_exact = false) const = 0;
virtual bool is_action_just_released(const StringName &p_action, bool p_exact = false) const = 0;
virtual bool is_action_just_pressed(const StringName &p_action, bool p_exact = false, bool p_current = false) const = 0;
virtual bool is_action_just_released(const StringName &p_action, bool p_exact = false, bool p_current = false) const = 0;
virtual float get_action_strength(const StringName &p_action, bool p_exact = false) const = 0;
virtual float get_action_raw_strength(const StringName &p_action, bool p_exact = false) const = 0;

Expand Down
4 changes: 4 additions & 0 deletions doc/classes/Input.xml
Original file line number Diff line number Diff line change
Expand Up @@ -205,20 +205,24 @@
<return type="bool" />
<argument index="0" name="action" type="String" />
<argument index="1" name="exact" type="bool" default="false" />
<argument index="2" name="current" type="bool" default="false" />
<description>
Returns [code]true[/code] when the user starts pressing the action event, meaning it's [code]true[/code] only on the frame that the user pressed down the button.
This is useful for code that needs to run only once when an action is pressed, instead of every frame while it's pressed.
If [code]exact[/code] is [code]false[/code], it ignores additional input modifiers for [InputEventKey] and [InputEventMouseButton] events, and the direction for [InputEventJoypadMotion] events.
If [code]current[/code] is [code]false[/code], the function will return true even if the action is not currently pressed. This prevents missing input from actions that are quickly pressed and released on the same tick or frame.
[b]Note:[/b] Due to keyboard ghosting, [method is_action_just_pressed] may return [code]false[/code] even if one of the action's keys is pressed. See [url=$DOCS_URL/tutorials/inputs/input_examples.html#keyboard-events]Input examples[/url] in the documentation for more information.
</description>
</method>
<method name="is_action_just_released" qualifiers="const">
<return type="bool" />
<argument index="0" name="action" type="String" />
<argument index="1" name="exact" type="bool" default="false" />
<argument index="2" name="current" type="bool" default="false" />
<description>
Returns [code]true[/code] when the user stops pressing the action event, meaning it's [code]true[/code] only on the frame that the user released the button.
If [code]exact[/code] is [code]false[/code], it ignores additional input modifiers for [InputEventKey] and [InputEventMouseButton] events, and the direction for [InputEventJoypadMotion] events.
If [code]current[/code] is [code]false[/code], the function will return true even if the action is not currently released. This prevents missing input from actions that are quickly released and pressed again on the same tick or frame.
</description>
</method>
<method name="is_action_pressed" qualifiers="const">
Expand Down
57 changes: 30 additions & 27 deletions main/input_default.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ bool InputDefault::is_action_pressed(const StringName &p_action, bool p_exact) c
return action_state.has(p_action) && action_state[p_action].pressed && (p_exact ? action_state[p_action].exact : true);
}

bool InputDefault::is_action_just_pressed(const StringName &p_action, bool p_exact) const {
bool InputDefault::is_action_just_pressed(const StringName &p_action, bool p_exact, bool p_current) const {
ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), false, InputMap::get_singleton()->suggest_actions(p_action));
const Map<StringName, Action>::Element *E = action_state.find(p_action);
if (!E) {
Expand All @@ -121,13 +121,13 @@ bool InputDefault::is_action_just_pressed(const StringName &p_action, bool p_exa
}

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

bool InputDefault::is_action_just_released(const StringName &p_action, bool p_exact) const {
bool InputDefault::is_action_just_released(const StringName &p_action, bool p_exact, bool p_current) const {
ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), false, InputMap::get_singleton()->suggest_actions(p_action));
const Map<StringName, Action>::Element *E = action_state.find(p_action);
if (!E) {
Expand All @@ -139,9 +139,9 @@ bool InputDefault::is_action_just_released(const StringName &p_action, bool p_ex
}

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

Expand Down Expand Up @@ -492,19 +492,26 @@ void InputDefault::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool

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())) {
Action &action = action_state[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;
if (p_event->is_action_pressed(E->key())) {
action.pressed = true;
action.pressed_physics_frame = Engine::get_singleton()->get_physics_frames();
action.pressed_idle_frame = Engine::get_singleton()->get_idle_frames();
} else {
action.pressed = false;
action.released_physics_frame = Engine::get_singleton()->get_physics_frames();
action.released_idle_frame = Engine::get_singleton()->get_idle_frames();
}
action.strength = 0;
action.raw_strength = 0;
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());
action_state[E->key()].raw_strength = p_event->get_action_raw_strength(E->key());

action.strength = p_event->get_action_strength(E->key());
action.raw_strength = p_event->get_action_raw_strength(E->key());
}
}

Expand Down Expand Up @@ -625,29 +632,25 @@ void InputDefault::iteration(float p_step) {
}

void InputDefault::action_press(const StringName &p_action, float p_strength) {
Action action;
// Create or retrieve existing action.
Action &action = action_state[p_action];

action.physics_frame = Engine::get_singleton()->get_physics_frames();
action.idle_frame = Engine::get_singleton()->get_idle_frames();
action.pressed_physics_frame = Engine::get_singleton()->get_physics_frames();
action.pressed_idle_frame = Engine::get_singleton()->get_idle_frames();
action.pressed = true;
action.strength = p_strength;
action.raw_strength = p_strength;
action.exact = true;

action_state[p_action] = action;
}

void InputDefault::action_release(const StringName &p_action) {
Action action;
// Create or retrieve existing action.
Action &action = action_state[p_action];

action.physics_frame = Engine::get_singleton()->get_physics_frames();
action.idle_frame = Engine::get_singleton()->get_idle_frames();
action.released_physics_frame = Engine::get_singleton()->get_physics_frames();
action.released_idle_frame = Engine::get_singleton()->get_idle_frames();
action.pressed = false;
action.strength = 0.f;
action.raw_strength = 0.f;
action.exact = true;

action_state[p_action] = action;
}

void InputDefault::set_emulate_touch_from_mouse(bool p_emulate) {
Expand Down
18 changes: 10 additions & 8 deletions main/input_default.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,14 @@ class InputDefault : public Input {
MainLoop *main_loop;

struct Action {
uint64_t physics_frame;
uint64_t idle_frame;
bool pressed;
bool exact;
float strength;
float raw_strength;
uint64_t pressed_physics_frame = UINT64_MAX;
uint64_t pressed_idle_frame = UINT64_MAX;
uint64_t released_physics_frame = UINT64_MAX;
uint64_t released_idle_frame = UINT64_MAX;
bool pressed = false;
bool exact = true;
float strength = 0;
float raw_strength = 0;
};

Map<StringName, Action> action_state;
Expand Down Expand Up @@ -225,8 +227,8 @@ class InputDefault : public Input {
virtual bool is_mouse_button_pressed(int p_button) const;
virtual bool is_joy_button_pressed(int p_device, int p_button) const;
virtual bool is_action_pressed(const StringName &p_action, bool p_exact = false) const;
virtual bool is_action_just_pressed(const StringName &p_action, bool p_exact = false) const;
virtual bool is_action_just_released(const StringName &p_action, bool p_exact = false) const;
virtual bool is_action_just_pressed(const StringName &p_action, bool p_exact = false, bool p_current = false) const;
virtual bool is_action_just_released(const StringName &p_action, bool p_exact = false, bool p_current = false) const;
virtual float get_action_strength(const StringName &p_action, bool p_exact = false) const;
virtual float get_action_raw_strength(const StringName &p_action, bool p_exact = false) const;

Expand Down

0 comments on commit 455ff1f

Please sign in to comment.