new input binding/mapping implementation

This commit is contained in:
Noel Berry
2021-03-20 19:23:20 -07:00
parent d73241e8fe
commit 3f07c03fa5
13 changed files with 869 additions and 805 deletions

View File

@ -28,9 +28,8 @@
#include "blah/images/packer.h"
#include "blah/input/input.h"
#include "blah/input/virtual_stick.h"
#include "blah/input/virtual_button.h"
#include "blah/input/virtual_axis.h"
#include "blah/input/binding.h"
#include "blah/input/binding_registry.h"
#include "blah/math/calc.h"
#include "blah/math/circle.h"

View File

@ -0,0 +1,261 @@
#pragma once
#include <blah/input/input.h>
#include <blah/containers/stackvector.h>
#include <blah/math/point.h>
#include <blah/math/vec2.h>
namespace Blah
{
// Represents a Controller Trigger or a single direction of a Controller Axis.
// Used in the Binding implementation.
struct BoundTrigger
{
// Controller Index we're bound to
int controller = 0;
// The Axis we're bound to
Axis axis = Axis::None;
// Minimum value of the axis
float threshold = 0.01f;
// requires a positive value
// otherwise requires a negative value
bool positive = true;
BoundTrigger() = default;
BoundTrigger(Axis axis);
BoundTrigger(int controller, Axis axis, float threshold, bool positive);
bool is_down(float axis_value) const;
// helper functions for frequent use cases
static BoundTrigger left_stick_left(int controller, float threshold);
static BoundTrigger left_stick_right(int controller, float threshold);
static BoundTrigger left_stick_up(int controller, float threshold);
static BoundTrigger left_stick_down(int controller, float threshold);
static BoundTrigger right_stick_left(int controller, float threshold);
static BoundTrigger right_stick_right(int controller, float threshold);
static BoundTrigger right_stick_up(int controller, float threshold);
static BoundTrigger right_stick_down(int controller, float threshold);
static BoundTrigger left_trigger(int controller, float threshold);
static BoundTrigger right_trigger(int controller, float threshold);
};
struct BoundButton
{
// Controller Index we're bound to
int controller = 0;
// Button we're bound to
Button button = Button::None;
BoundButton() = default;
BoundButton(Button button);
BoundButton(int controller, Button button);
};
// Single input Binding
class Binding
{
public:
// Input Buffer for press events
float press_buffer = 0;
// Input Buffer for release events
float release_buffer = 0;
// List of bound Keys
StackVector<Key, 16> keys;
// List of bound Buttons
StackVector<BoundButton, 16> buttons;
// List of bound Triggers / Axis
StackVector<BoundTrigger, 16> triggers;
// List of bound Mouse buttons
StackVector<MouseButton, 16> mouse;
// if the binding has been pressed
bool pressed() const;
// if the binding has been released
bool released() const;
// if the binding is currently held
bool down() const;
// returns the binding's value from 0-1
float value() const;
// returns the bindings signed value (0 or 1)
int sign() const;
// returns the timestamp of the last time the binding was pressed
double timestamp() const;
// updates the binding state
void update();
// consumes the current press, and pressed() will return false until the next press
void consume_press();
// consumes the current release, and released() will return false until the next release
void consume_release();
// adds a key to the binding
void add(Key key);
// adds a button to the binding
void add(BoundButton button);
// adds an trigger to the binding
void add(BoundTrigger trigger);
// adds a mouse button to the binding
void add(MouseButton mouse);
// adds an input to the binding
template<typename T, typename T2, typename ... Args>
void add(T first, T2 second, const Args&... args)
{
add(first);
add(second, args...);
}
// assigns all the bindings to the specific controller
void set_controller(int index);
// removes all bindings
void clear();
private:
double m_last_timestamp = 0;
double m_last_press_time = -1;
double m_last_release_time = -1;
float m_value = 0.0f;
bool m_pressed = false;
bool m_released = false;
bool m_down = false;
bool m_press_consumed = false;
bool m_release_consumed = false;
bool get_pressed() const;
bool get_released() const;
bool get_down() const;
float get_value() const;
};
// Represents a Bound Axis (ex. Left/Right movement, or a Trigger)
class AxisBinding
{
public:
enum class Overlap
{
Newer,
Older,
Cancel
};
// Negative Value Binding
Binding negative;
// Positive Value Binding
Binding positive;
// How to handle overlaps (ex. Left and Right are both held)
Overlap overlap = Overlap::Newer;
// Current Value from -1 to 1
float value() const;
// Current value, either -1, 0, or 1
int sign() const;
// updates the Binding
void update();
// consumes the press buffer
void consume_press();
// consumes the release buffer
void consume_release();
// Adds a negative & positive binding pair
template<typename NegativeT, typename PositiveT>
void add(NegativeT negative, PositiveT positive)
{
this->negative.add(negative);
this->positive.add(positive);
}
// Adds a Stick binding
void add_left_stick_x(int controller, float threshold);
void add_left_stick_y(int controller, float threshold);
void add_right_stick_x(int controller, float threshold);
void add_right_stick_y(int controller, float threshold);
// assigns all the bindings to the specific controller
void set_controller(int index);
// Clears all Bindings
void clear();
};
class StickBinding
{
public:
// An optional threshold for circular thresholds
float round_threshold = 0.0f;
// X Axis Binding
AxisBinding x;
// Y Axis Binding
AxisBinding y;
// Current Value, -1 to 1
Vec2 value() const;
// Current value, either -1, 0, or 1
Point sign() const;
// Updates the Binding
void update();
// Consumes the Press Buffer
void consume_press();
// Consumes the Release Buffer
void consume_release();
// Adds directional bindings
template<typename LeftT, typename RightT, typename UpT, typename DownT>
void add(LeftT left, RightT right, UpT up, DownT down)
{
x.negative.add(left);
x.positive.add(right);
y.negative.add(up);
y.positive.add(down);
}
// Adds the dpad binding
void add_dpad(int controller);
// Adds the left stick binding
void add_left_stick(int controller, float threshold);
// Adds the right stick binding
void add_right_stick(int controller, float threshold);
// assigns all the bindings to the specific controller
void set_controller(int index);
// Clears all the bindings
void clear();
};
}

View File

@ -0,0 +1,37 @@
#pragma once
#include <blah/input/binding.h>
#include <blah/containers/vector.h>
#include <memory>
namespace Blah
{
using BindingRef = std::shared_ptr<Binding>;
using AxisBindingRef = std::shared_ptr<AxisBinding>;
using StickBindingRef = std::shared_ptr<StickBinding>;
// Optional registry to automatically update bindings.
// You can register different types of bindings here and until they are
// no longer used, they will be updated without having to explicitely call
// their update methods
class BindingRegistry
{
public:
// registers a new binding
static BindingRef register_binding();
// registers a new axis binding
static AxisBindingRef register_axis();
// registers a new stick binding
static StickBindingRef register_stick();
// updates all the bindings. This is called
// automatically by the App loop.
static void update();
private:
static Vector<std::weak_ptr<Binding>> bindings;
static Vector<std::weak_ptr<AxisBinding>> axes;
static Vector<std::weak_ptr<StickBinding>> sticks;
};
}

View File

@ -1,86 +0,0 @@
#pragma once
#include <blah/input/input.h>
namespace Blah
{
// A virtual controller axis, which can be used to map multiple
// inputs to an axis. Note that you must call `update` every frame!
class VirtualAxis
{
private:
struct KeysNode
{
Key positive = Key::Unknown;
Key negative = Key::Unknown;
int value = 0;
void init(Key negative, Key positive);
void update();
};
struct ButtonsNode
{
int gamepad_id = 0;
Button positive = Button::None;
Button negative = Button::None;
int value = 0;
void init(int gamepad_id, Button negative, Button positive);
void update();
};
struct AxisNode
{
int gamepad_id = 0;
Axis axis = Axis::None;
float deadzone = 0;
float value = 0;
void init(int gamepad_id, Axis axis, float deadzone);
void update();
};
KeysNode m_keys[Input::max_virtual_nodes];
ButtonsNode m_buttons[Input::max_virtual_nodes];
AxisNode m_axes[Input::max_virtual_nodes];
int m_keys_len = 0;
int m_buttons_len = 0;
int m_axes_len = 0;
float m_press_buffer = 0;
float m_release_buffer = 0;
float m_repeat_delay = 0;
float m_repeat_interval = 0;
float m_value = 0;
int m_value_i = 0;
float m_last_value = 0;
int m_last_value_i = 0;
bool m_pressed = false;
bool m_released = false;
double m_last_press_time = -1;
double m_last_release_time = -1;
double m_repeat_press_time = -1;
public:
VirtualAxis& add_keys(Key negative, Key positive);
VirtualAxis& add_buttons(int gamepad_id, Button negative, Button positive);
VirtualAxis& add_axis(int gamepad_id, Axis axis, float deadzone);
VirtualAxis& repeat(float m_repeat_delay, float m_repeat_interval);
VirtualAxis& press_buffer(float duration);
VirtualAxis& release_buffer(float duration);
void update();
float value() const { return m_value; }
int value_i() const { return m_value_i; }
float last_value() const { return m_last_value; }
int last_value_i() const { return m_last_value_i; }
bool pressed() const { return m_pressed; }
bool released() const { return m_released; }
void clear_press_buffer() { m_last_press_time = -1; m_pressed = false; }
void clear_release_buffer() { m_last_release_time = -1; m_released = false; }
};
}

View File

@ -1,83 +0,0 @@
#pragma once
#include <blah/input/input.h>
namespace Blah
{
class VirtualButton
{
private:
struct KeyNode
{
Key key = Key::Unknown;
bool down = false;
bool pressed = false;
bool released = false;
void init(Key key);
void update();
};
struct ButtonNode
{
int gamepad_id = 0;
Button button = Button::None;
bool down = false;
bool pressed = false;
bool released = false;
void init(int gamepad_id, Button button);
void update();
};
struct AxisNode
{
int gamepad_id = 0;
Axis axis = Axis::None;
float threshold = 0;
bool greater_than = false;
bool down = false;
bool pressed = false;
bool released = false;
void init(int gamepad_id, Axis axis, float threshold, bool greater_than);
void update();
};
KeyNode m_keys[Input::max_virtual_nodes];
ButtonNode m_buttons[Input::max_virtual_nodes];
AxisNode m_axes[Input::max_virtual_nodes];
int m_keys_len = 0;
int m_buttons_len = 0;
int m_axes_len = 0;
float m_press_buffer = 0;
float m_release_buffer = 0;
float m_repeat_delay = 0;
float m_repeat_interval = 0;
bool m_down = false;
bool m_pressed = false;
bool m_released = false;
double m_last_press_time = -1;
double m_last_release_time = -1;
double m_repeat_press_time = -1;
public:
VirtualButton& add_key(Key key);
VirtualButton& add_button(int gamepad_id, Button button);
VirtualButton& add_axis(int gamepad_id, Axis axis, float threshold, bool greater_than);
VirtualButton& repeat(float m_repeat_delay, float m_repeat_interval);
VirtualButton& press_buffer(float duration);
VirtualButton& release_buffer(float duration);
void update();
bool down() const { return m_down; }
bool pressed() const { return m_pressed; }
bool released() const { return m_released; }
void clear_press_buffer() { m_last_press_time = -1; m_pressed = false; }
void clear_release_buffer() { m_last_release_time = -1; m_released = false; }
};
}

View File

@ -1,97 +0,0 @@
#pragma once
#include <blah/input/input.h>
#include <blah/math/vec2.h>
#include <blah/math/point.h>
namespace Blah
{
// A virtual controller stick, which can be used to map multiple
// inputs to a stick. Note that you must call `update` every frame!
class VirtualStick
{
private:
struct KeysNode
{
Key left;
Key right;
Key up;
Key down;
Point value;
void init(Key left, Key right, Key up, Key down);
void update();
};
struct ButtonsNode
{
int gamepad_id;
Button left;
Button right;
Button up;
Button down;
Point value;
void init(int gamepad_id, Button left, Button right, Button up, Button down);
void update();
};
struct AxesNode
{
int gamepad_id;
Axis horizontal;
Axis vertical;
float deadzone;
Vec2 value;
void init(int gamepad_id, Axis horizontal, Axis vertical, float deadzone);
void update();
};
KeysNode m_keys[Input::max_virtual_nodes];
ButtonsNode m_buttons[Input::max_virtual_nodes];
AxesNode m_axes[Input::max_virtual_nodes];
int m_keys_len = 0;
int m_buttons_len = 0;
int m_axes_len = 0;
float m_press_buffer = 0;
float m_release_buffer = 0;
float m_repeat_delay = 0;
float m_repeat_interval = 0;
Vec2 m_value = Vec2();
Point m_value_i = Point();
Vec2 m_last_value = Vec2();
Point m_last_value_i = Point();
bool m_pressed = false;
bool m_released = false;
float m_i_deadzone;
double m_last_press_time = -1;
double m_last_release_time = -1;
double m_repeat_press_time = -1;
public:
VirtualStick();
VirtualStick(float iDeadzone);
VirtualStick& add_keys(Key left, Key right, Key up, Key down);
VirtualStick& add_buttons(int gamepad_id, Button left, Button right, Button up, Button down);
VirtualStick& add_axes(int gamepad_id, Axis horizontal, Axis vertical, float deadzone);
VirtualStick& repeat(float m_repeat_delay, float m_repeat_interval);
VirtualStick& press_buffer(float duration);
VirtualStick& release_buffer(float duration);
void update();
const Vec2& value() const { return m_value; }
const Point& value_i() const { return m_value_i; }
const Vec2& last_value() const { return m_last_value; }
const Point& last_value_i() const { return m_last_value_i; }
bool pressed() const { return m_pressed; }
bool released() const { return m_released; }
void clear_press_buffer() { m_last_press_time = 0; }
void clear_release_buffer() { m_last_release_time = 0; }
};
}