fix: playerinput mouse events now report correctly
This commit is contained in:
		
							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 a new issue