fix: playerinput mouse events now report correctly
parent
460dc9a1c6
commit
2a2214b487
|
@ -4,10 +4,14 @@
|
||||||
#include "godot_cpp/classes/input_event.hpp"
|
#include "godot_cpp/classes/input_event.hpp"
|
||||||
#include "godot_cpp/classes/input_event_mouse_motion.hpp"
|
#include "godot_cpp/classes/input_event_mouse_motion.hpp"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
namespace godot {
|
namespace godot {
|
||||||
void PlayerInput::_bind_methods() {}
|
void PlayerInput::_bind_methods() {}
|
||||||
|
|
||||||
|
Vector2 PlayerInput::lastMouseMotion{0.f, 0.f};
|
||||||
|
bool PlayerInput::primaryExists{false};
|
||||||
|
|
||||||
PlayerInput::Listener::Listener(String positive, String negative, Node *object, String method)
|
PlayerInput::Listener::Listener(String positive, String negative, Node *object, String method)
|
||||||
: actionNegative{negative}
|
: actionNegative{negative}
|
||||||
, actionPositive{positive}
|
, actionPositive{positive}
|
||||||
|
@ -15,40 +19,45 @@ PlayerInput::Listener::Listener(String positive, String negative, Node *object,
|
||||||
, object{object}
|
, object{object}
|
||||||
, isMouseEvent{positive.begins_with("_mouse_") || negative.begins_with("_mouse_")} {}
|
, isMouseEvent{positive.begins_with("_mouse_") || negative.begins_with("_mouse_")} {}
|
||||||
|
|
||||||
float PlayerInput::Listener::evaluate_event(Ref<InputEvent> const &event, String const &action) {
|
std::optional<float> PlayerInput::Listener::evaluate_action(String const &action) {
|
||||||
Input *input = Input::get_singleton();
|
Input *input = Input::get_singleton();
|
||||||
if(!action.begins_with("_mouse_")) {
|
if(action.begins_with("_mouse_")) {
|
||||||
return float(input->is_action_pressed(action));
|
Vector2 vector = PlayerInput::get_last_mouse_motion();
|
||||||
|
if(action.ends_with("_up"))
|
||||||
|
return vector.y > 0.f ? vector.y : 0.f;
|
||||||
|
else if(action.ends_with("_down"))
|
||||||
|
return vector.y < 0.f ? -vector.y : 0.f;
|
||||||
|
else if(action.ends_with("_right"))
|
||||||
|
return vector.x > 0.f ? vector.x : 0.f;
|
||||||
|
else if(action.ends_with("_left"))
|
||||||
|
return vector.x < 0.f ? -vector.x : 0.f;
|
||||||
|
else
|
||||||
|
return std::nullopt;
|
||||||
|
} else if(action.is_empty()) {
|
||||||
|
return 0.f;
|
||||||
} else {
|
} else {
|
||||||
InputEventMouseMotion *motion = Object::cast_to<InputEventMouseMotion>(*event);
|
return float(input->is_action_pressed(action));
|
||||||
if(motion == nullptr)
|
|
||||||
return 0.f;
|
|
||||||
if(action.ends_with("_up") || action.ends_with("_right")) {
|
|
||||||
return motion->get_relative().x;
|
|
||||||
} if(action.ends_with("_right") || action.ends_with("_left")) {
|
|
||||||
return motion->get_relative().y;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 0.f;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PlayerInput::Listener::has_changed(Ref<InputEvent> const &event) {
|
bool PlayerInput::Listener::has_changed(Ref<InputEvent> const &event) {
|
||||||
return (
|
return (
|
||||||
(!event->is_class("InputEventMouseMotion") ||
|
(!event->is_class("InputEventMouseMotion") ||
|
||||||
this->isMouseEvent
|
this->isMouseEvent) ||
|
||||||
) ||
|
|
||||||
event->is_action(this->actionNegative) ||
|
event->is_action(this->actionNegative) ||
|
||||||
event->is_action(this->actionPositive)
|
event->is_action(this->actionPositive)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
float PlayerInput::Listener::evaluate(Ref<InputEvent> const &event) {
|
float PlayerInput::Listener::evaluate(Ref<InputEvent> const &event) {
|
||||||
float positive = PlayerInput::Listener::evaluate_event(event, this->actionPositive);
|
std::optional<float> positive = PlayerInput::Listener::evaluate_action(this->actionPositive);
|
||||||
float negative = PlayerInput::Listener::evaluate_event(event, this->actionNegative);
|
std::optional<float> negative = PlayerInput::Listener::evaluate_action(this->actionNegative);
|
||||||
float newest = positive - negative;
|
if(!positive.has_value() || !negative.has_value())
|
||||||
if(lastCached != newest)
|
return 0.f;
|
||||||
|
float newest = positive.value() - negative.value();
|
||||||
|
if(this->lastCached != newest || this->isMouseEvent)
|
||||||
this->object->call(this->methodName, event, newest);
|
this->object->call(this->methodName, event, newest);
|
||||||
return (lastCached = newest);
|
return (this->lastCached = newest);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PlayerInput::Listener::operator==(godot::PlayerInput::Listener const& b) {
|
bool PlayerInput::Listener::operator==(godot::PlayerInput::Listener const& b) {
|
||||||
|
@ -58,8 +67,30 @@ bool PlayerInput::Listener::operator==(godot::PlayerInput::Listener const& b) {
|
||||||
&& this->actionPositive == b.actionPositive;
|
&& this->actionPositive == b.actionPositive;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vector2 PlayerInput::get_last_mouse_motion() {
|
||||||
|
return PlayerInput::lastMouseMotion;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerInput::_enter_tree() {
|
||||||
|
GDGAMEONLY();
|
||||||
|
if(!PlayerInput::primaryExists) {
|
||||||
|
this->isPrimary = true;
|
||||||
|
PlayerInput::primaryExists = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerInput::_exit_tree() {
|
||||||
|
GDGAMEONLY();
|
||||||
|
if(this->isPrimary) {
|
||||||
|
this->isPrimary = false;
|
||||||
|
PlayerInput::primaryExists = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PlayerInput::_unhandled_input(Ref<InputEvent> const &event) {
|
void PlayerInput::_unhandled_input(Ref<InputEvent> const &event) {
|
||||||
GDGAMEONLY();
|
GDGAMEONLY();
|
||||||
|
if(this->isPrimary && event->is_class("InputEventMouseMotion"))
|
||||||
|
PlayerInput::lastMouseMotion = Object::cast_to<InputEventMouseMotion>(*event)->get_relative();
|
||||||
for(Listener& listener: this->listeners) {
|
for(Listener& listener: this->listeners) {
|
||||||
if(listener.has_changed(event)) {
|
if(listener.has_changed(event)) {
|
||||||
listener.evaluate(event);
|
listener.evaluate(event);
|
||||||
|
@ -67,6 +98,10 @@ void PlayerInput::_unhandled_input(Ref<InputEvent> const &event) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PlayerInput::_process(double deltaTime) {
|
||||||
|
PlayerInput::lastMouseMotion = {0.f, 0.f};
|
||||||
|
}
|
||||||
|
|
||||||
void PlayerInput::listen_to(Listener const& listener) {
|
void PlayerInput::listen_to(Listener const& listener) {
|
||||||
this->listeners.push_back(listener);
|
this->listeners.push_back(listener);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#define PLAYER_INPUT_HPP
|
#define PLAYER_INPUT_HPP
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <optional>
|
||||||
#include "godot_cpp/classes/input.hpp"
|
#include "godot_cpp/classes/input.hpp"
|
||||||
#include "godot_cpp/classes/input_event.hpp"
|
#include "godot_cpp/classes/input_event.hpp"
|
||||||
#include "godot_cpp/classes/node.hpp"
|
#include "godot_cpp/classes/node.hpp"
|
||||||
|
@ -11,27 +12,61 @@ class PlayerInput : public Node {
|
||||||
GDCLASS(PlayerInput, Node)
|
GDCLASS(PlayerInput, Node)
|
||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
public:
|
public:
|
||||||
|
// a listener is a combination of a positive and negative action and a listener function.
|
||||||
|
// listener functions use godot's Object::call function.
|
||||||
|
// So they require a Node instance and a function name.
|
||||||
|
// The expected signature is void(Ref<InputEvent>, float)
|
||||||
|
// actions can also be "special" actions prefixed with _.
|
||||||
|
// special actions include _mouse_up, _mouse_down, _mouse_left and _mouse_right
|
||||||
|
// which rather than checking action_is_down,
|
||||||
|
// will use PlayerInput::get_last_mouse_motion() to poll the current state.
|
||||||
struct Listener {
|
struct Listener {
|
||||||
friend class PlayerInput;
|
friend class PlayerInput;
|
||||||
private:
|
private:
|
||||||
|
// the two actions, evaluated as positive - negative
|
||||||
String actionNegative{""};
|
String actionNegative{""};
|
||||||
String actionPositive{""};
|
String actionPositive{""};
|
||||||
|
// the last cached action, if the newest result matches this, the event will be considered
|
||||||
|
// duplicate and ignored (not passed to listener)
|
||||||
float lastCached{0.f};
|
float lastCached{0.f};
|
||||||
|
// name of the method to call, expected signature is void(Ref<InputEvent>, float)
|
||||||
String methodName{""};
|
String methodName{""};
|
||||||
|
// pointer to the node to call methodName on
|
||||||
Node *object{nullptr};
|
Node *object{nullptr};
|
||||||
|
// if either actionNegative or actionPositive is a _mouse_ event this will be true
|
||||||
bool isMouseEvent{false};
|
bool isMouseEvent{false};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Listener(String positive, String negative, Node *object, String method);
|
Listener(String positive, String negative, Node *object, String method);
|
||||||
static float evaluate_event(Ref<InputEvent> const &event, String const &action);
|
// evaluate the current state of an action.
|
||||||
|
static std::optional<float> evaluate_action(String const &action);
|
||||||
|
// check if this event has any chance to result in a trigger, does not evaluate the event or
|
||||||
|
// poll current input state
|
||||||
bool has_changed(Ref<InputEvent> const &event);
|
bool has_changed(Ref<InputEvent> const &event);
|
||||||
|
// evaluate the event for changes to either actionPositive or actionNegative
|
||||||
float evaluate(Ref<InputEvent> const &event);
|
float evaluate(Ref<InputEvent> const &event);
|
||||||
|
|
||||||
bool operator==(godot::PlayerInput::Listener const& b);
|
bool operator==(godot::PlayerInput::Listener const& b);
|
||||||
};
|
};
|
||||||
private:
|
private:
|
||||||
|
// the last mouse motion, updated by the primary instance
|
||||||
|
static Vector2 lastMouseMotion;
|
||||||
|
// does a primary instance exist
|
||||||
|
static bool primaryExists;
|
||||||
|
// is this the primary instance
|
||||||
|
// the primary instance is responsible for updating static
|
||||||
|
// variables like lastMouseMotion
|
||||||
|
bool isPrimary{false};
|
||||||
|
|
||||||
|
// current listeners for this instance
|
||||||
std::vector<Listener> listeners{};
|
std::vector<Listener> listeners{};
|
||||||
public:
|
public:
|
||||||
|
static Vector2 get_last_mouse_motion();
|
||||||
|
|
||||||
|
virtual void _enter_tree() override;
|
||||||
|
virtual void _exit_tree() override;
|
||||||
virtual void _unhandled_input(Ref<InputEvent> const &event) override;
|
virtual void _unhandled_input(Ref<InputEvent> const &event) override;
|
||||||
|
virtual void _process(double deltaTime) override;
|
||||||
|
|
||||||
void listen_to(Listener const& listener);
|
void listen_to(Listener const& listener);
|
||||||
void stop_listening(Node *node);
|
void stop_listening(Node *node);
|
||||||
|
|
Loading…
Reference in New Issue