Bindings and Controllers #106

Merged
mushgunAX merged 1 commits from SP3-10-input-management into main 2022-10-23 01:06:04 +08:00
2 changed files with 1055 additions and 11 deletions

View File

@ -19,6 +19,10 @@ namespace SHADE
/*------------------------------------------------------------------------*/
/* Static defines */
/*------------------------------------------------------------------------*/
bool SHInputManager::controllerInUse = false;
std::map<std::string, SHInputManager::SHLogicalBindingData> SHInputManager::bindings;
unsigned SHInputManager::keyCount = 0;
bool SHInputManager::keys[MAX_KEYS];
@ -41,6 +45,60 @@ namespace SHADE
int SHInputManager::mouseWheelVerticalDelta = 0;
int SHInputManager::mouseWheelVerticalDeltaPoll = 0;
unsigned char SHInputManager::controllersConnectedCount = 0;
unsigned SHInputManager::controllersInputCount[XUSER_MAX_COUNT];
unsigned SHInputManager::controllersButtonCount[XUSER_MAX_COUNT];
short SHInputManager::controllers[XUSER_MAX_COUNT][MAX_CONTROLLER_INPUT];
short SHInputManager::controllersLast[XUSER_MAX_COUNT][MAX_CONTROLLER_INPUT];
double SHInputManager::controllersHeldTime[XUSER_MAX_COUNT][MAX_CONTROLLER_INPUT];
double SHInputManager::controllersReleasedTime[XUSER_MAX_COUNT][MAX_CONTROLLER_INPUT];
//Internal helper function for splitting between inputs
bool SHInputManager::controllerConsideredHeld(size_t inputIdx, short value) noexcept
{
if (inputIdx >= MAX_CONTROLLER_INPUT) return false; //Bounds check
else
{
if (inputIdx < NUM_CONTROLLER_BUTTON)
{
return static_cast<bool>(value);
}
else if (inputIdx < NUM_CONTROLLER_BUTTON + NUM_CONTROLLER_TRIGGER)
{
return (value > XINPUT_GAMEPAD_TRIGGER_THRESHOLD);
}
else if (inputIdx < NUM_CONTROLLER_BUTTON + NUM_CONTROLLER_TRIGGER + NUM_CONTROLLER_TRIGGER)
{
return (std::abs(value) > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE);
}
else
{
return (std::abs(value) > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE);
}
}
}
//Internal helper function for getting normalised input value
double SHInputManager::controllerNormalisedValue(size_t inputIdx, short value) noexcept
{
if (inputIdx >= MAX_CONTROLLER_INPUT) return 0.0; //Bounds check
else
{
if (inputIdx < NUM_CONTROLLER_BUTTON)
{
return static_cast<double>(value);
}
else if (inputIdx < NUM_CONTROLLER_BUTTON + NUM_CONTROLLER_TRIGGER) //8-bit triggers, 0 to 255
{
return static_cast<double>(value) / static_cast<double>(UCHAR_MAX);
}
else //16-bit thumbsticks, -32768 to 32767
{
return static_cast<double>(value) / static_cast<double>(SHRT_MAX);
}
}
}
void SHInputManager::UpdateInput(double dt) noexcept
{
//Keyboard and Mouse Buttons////////////////////////////////////////////////
@ -120,6 +178,273 @@ namespace SHADE
mouseWheelVerticalDelta = 0;
mouseWheelVerticalDelta = mouseWheelVerticalDeltaPoll;
mouseWheelVerticalDeltaPoll = 0;
//Controllers//////////////////////////////////////////////////////////////
controllersConnectedCount = 0;
//Set last controller states
memcpy(controllersLast, controllers, sizeof(controllers));
//Reset controller states
SecureZeroMemory(&controllers, sizeof(controllers));
//https://learn.microsoft.com/en-us/windows/win32/xinput/getting-started-with-xinput#getting-controller-state
for (DWORD c = 0; c < XUSER_MAX_COUNT; ++c)
{
controllersInputCount[c] = 0;
controllersButtonCount[c] = 0;
XINPUT_STATE state;
SecureZeroMemory(&state, sizeof(XINPUT_STATE));
//Get the state of controller from XInput
DWORD result = XInputGetState(c, &state);
//Write gamepad data
if (result == ERROR_SUCCESS)
{
++controllersConnectedCount;
//DIGITAL BUTTONS///////////////////////////////////////
//DPAD UP
if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP)
{
controllers[c][static_cast<size_t>(SH_CONTROLLERCODE::DPAD_UP)] = 1;
++controllersInputCount[c];
++controllersButtonCount[c];
}
else
{
controllers[c][static_cast<size_t>(SH_CONTROLLERCODE::DPAD_UP)] = 0;
}
//DPAD DOWN
if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN)
{
controllers[c][static_cast<size_t>(SH_CONTROLLERCODE::DPAD_DOWN)] = 1;
++controllersInputCount[c];
++controllersButtonCount[c];
}
else
{
controllers[c][static_cast<size_t>(SH_CONTROLLERCODE::DPAD_DOWN)] = 0;
}
//DPAD LEFT
if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT)
{
controllers[c][static_cast<size_t>(SH_CONTROLLERCODE::DPAD_LEFT)] = 1;
++controllersInputCount[c];
++controllersButtonCount[c];
}
else
{
controllers[c][static_cast<size_t>(SH_CONTROLLERCODE::DPAD_LEFT)] = 0;
}
//DPAD RIGHT
if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT)
{
controllers[c][static_cast<size_t>(SH_CONTROLLERCODE::DPAD_RIGHT)] = 1;
++controllersInputCount[c];
++controllersButtonCount[c];
}
else
{
controllers[c][static_cast<size_t>(SH_CONTROLLERCODE::DPAD_RIGHT)] = 0;
}
//START
if (state.Gamepad.wButtons & XINPUT_GAMEPAD_START)
{
controllers[c][static_cast<size_t>(SH_CONTROLLERCODE::START)] = 1;
++controllersInputCount[c];
++controllersButtonCount[c];
}
else
{
controllers[c][static_cast<size_t>(SH_CONTROLLERCODE::START)] = 0;
}
//BACK
if (state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK)
{
controllers[c][static_cast<size_t>(SH_CONTROLLERCODE::BACK)] = 1;
++controllersInputCount[c];
++controllersButtonCount[c];
}
else
{
controllers[c][static_cast<size_t>(SH_CONTROLLERCODE::BACK)] = 0;
}
//LEFT THUMBSTICK BUTTON
if (state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB)
{
controllers[c][static_cast<size_t>(SH_CONTROLLERCODE::LEFT_THUMBSTICK)] = 1;
++controllersInputCount[c];
++controllersButtonCount[c];
}
else
{
controllers[c][static_cast<size_t>(SH_CONTROLLERCODE::LEFT_THUMBSTICK)] = 0;
}
//RIGHT THUMBSTICK BUTTON
if (state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB)
{
controllers[c][static_cast<size_t>(SH_CONTROLLERCODE::RIGHT_THUMBSTICK)] = 1;
++controllersInputCount[c];
++controllersButtonCount[c];
}
else
{
controllers[c][static_cast<size_t>(SH_CONTROLLERCODE::RIGHT_THUMBSTICK)] = 0;
}
//LEFT SHOULDER BUTTON
if (state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER)
{
controllers[c][static_cast<size_t>(SH_CONTROLLERCODE::LEFT_SHOULDER)] = 1;
++controllersInputCount[c];
++controllersButtonCount[c];
}
else
{
controllers[c][static_cast<size_t>(SH_CONTROLLERCODE::LEFT_SHOULDER)] = 0;
}
//RIGHT SHOULDER BUTTON
if (state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER)
{
controllers[c][static_cast<size_t>(SH_CONTROLLERCODE::RIGHT_SHOULDER)] = 1;
++controllersInputCount[c];
++controllersButtonCount[c];
}
else
{
controllers[c][static_cast<size_t>(SH_CONTROLLERCODE::RIGHT_SHOULDER)] = 0;
}
//A
if (state.Gamepad.wButtons & XINPUT_GAMEPAD_A)
{
controllers[c][static_cast<size_t>(SH_CONTROLLERCODE::A)] = 1;
++controllersInputCount[c];
++controllersButtonCount[c];
}
else
{
controllers[c][static_cast<size_t>(SH_CONTROLLERCODE::A)] = 0;
}
//B
if (state.Gamepad.wButtons & XINPUT_GAMEPAD_B)
{
controllers[c][static_cast<size_t>(SH_CONTROLLERCODE::B)] = 1;
++controllersInputCount[c];
++controllersButtonCount[c];
}
else
{
controllers[c][static_cast<size_t>(SH_CONTROLLERCODE::B)] = 0;
}
//X
if (state.Gamepad.wButtons & XINPUT_GAMEPAD_X)
{
controllers[c][static_cast<size_t>(SH_CONTROLLERCODE::X)] = 1;
++controllersInputCount[c];
++controllersButtonCount[c];
}
else
{
controllers[c][static_cast<size_t>(SH_CONTROLLERCODE::X)] = 0;
}
//Y
if (state.Gamepad.wButtons & XINPUT_GAMEPAD_Y)
{
controllers[c][static_cast<size_t>(SH_CONTROLLERCODE::Y)] = 1;
++controllersInputCount[c];
++controllersButtonCount[c];
}
else
{
controllers[c][static_cast<size_t>(SH_CONTROLLERCODE::Y)] = 0;
}
//8 BIT VALUES (0 - 255)///////////////////////////////////
//LEFT TRIGGER
controllers[c][static_cast<size_t>(SH_CONTROLLERCODE::LEFT_TRIGGER)] = state.Gamepad.bLeftTrigger;
if (state.Gamepad.bLeftTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
{
++controllersInputCount[c]; //Registered as held
}
//RIGHT TRIGGER
controllers[c][static_cast<size_t>(SH_CONTROLLERCODE::RIGHT_TRIGGER)] = state.Gamepad.bRightTrigger;
if (state.Gamepad.bRightTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
{
++controllersInputCount[c]; //Registered as held
}
//16 BIT VALUES (0 - 65535)////////////////////////////////
//LEFT THUMBSTICK X
controllers[c][static_cast<size_t>(SH_CONTROLLERCODE::LEFT_THUMBSTICK_X)] = state.Gamepad.sThumbLX;
if (std::abs(state.Gamepad.sThumbLX) > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE)
{
++controllersInputCount[c];
}
//LEFT THUMBSTICK Y
controllers[c][static_cast<size_t>(SH_CONTROLLERCODE::LEFT_THUMBSTICK_Y)] = state.Gamepad.sThumbLY;
if (std::abs(state.Gamepad.sThumbLY) > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE)
{
++controllersInputCount[c];
}
//RIGHT THUMBSTICK X
controllers[c][static_cast<size_t>(SH_CONTROLLERCODE::RIGHT_THUMBSTICK_X)] = state.Gamepad.sThumbRX;
if (std::abs(state.Gamepad.sThumbRX) > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE)
{
++controllersInputCount[c];
}
//RIGHT THUMBSTICK Y
controllers[c][static_cast<size_t>(SH_CONTROLLERCODE::RIGHT_THUMBSTICK_Y)] = state.Gamepad.sThumbRY;
if (std::abs(state.Gamepad.sThumbRY) > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE)
{
++controllersInputCount[c];
}
}
//Timers and updating if controller is presently in use
for (size_t i = 0; i < MAX_CONTROLLER_INPUT; ++i)
{
if (controllerConsideredHeld(i, controllers[c][i])) //Considered on
{
controllerInUse = true;
if (!controllerConsideredHeld(i, controllersLast[c][i])) //Just on
{
controllersHeldTime[c][i] = 0.0; //Reset timer
}
controllersHeldTime[c][i] += dt; //Tick up
}
else //Considered off
{
if (controllerConsideredHeld(i, controllersLast[c][i])) //Just off
{
controllersReleasedTime[c][i] = 0.0; //Reset timer
}
controllersReleasedTime[c][i] += dt; //Tick up
}
}
}
}
bool SHInputManager::AnyKeyDown(SH_KEYCODE* firstDetected) noexcept
@ -161,4 +486,336 @@ namespace SHADE
return false;
}
//Any controller input being held
//For analog, this means going being deadzone values
bool SHInputManager::AnyControllerInput(SH_CONTROLLERCODE* firstDetected, size_t cNum) noexcept
{
if (cNum >= XUSER_MAX_COUNT) return false;
for (size_t i = 0; i < MAX_CONTROLLER_INPUT; ++i)
{
if (controllerConsideredHeld(i, controllers[cNum][i]))
{
if (firstDetected) *firstDetected = static_cast<SH_CONTROLLERCODE>(i);
return true;
}
}
return false;
}
bool SHInputManager::AnyControllerInputDown(SH_CONTROLLERCODE* firstDetected, size_t cNum) noexcept
{
if (cNum >= XUSER_MAX_COUNT) return false;
for (size_t i = 0; i < MAX_CONTROLLER_INPUT; ++i)
{
if (controllerConsideredHeld(i, controllers[cNum][i]) && !controllerConsideredHeld(i, controllersLast[cNum][i]))
{
if (firstDetected) *firstDetected = static_cast<SH_CONTROLLERCODE>(i);
return true;
}
}
return false;
}
bool SHInputManager::AnyControllerInputUp(SH_CONTROLLERCODE* firstDetected, size_t cNum) noexcept
{
if (cNum >= XUSER_MAX_COUNT) return false;
for (size_t i = 0; i < MAX_CONTROLLER_INPUT; ++i)
{
if (!controllerConsideredHeld(i, controllers[cNum][i]) && controllerConsideredHeld(i, controllersLast[cNum][i]))
{
if (firstDetected) *firstDetected = static_cast<SH_CONTROLLERCODE>(i);
return true;
}
}
return false;
}
bool SHInputManager::AnyControllerButton(SH_CONTROLLERCODE* firstDetected, size_t cNum) noexcept
{
if (cNum >= XUSER_MAX_COUNT) return false;
for (size_t i = 0; i < NUM_CONTROLLER_BUTTON; ++i)
{
if (controllerConsideredHeld(i, controllers[cNum][i]))
{
if (firstDetected) *firstDetected = static_cast<SH_CONTROLLERCODE>(i);
return true;
}
}
return false;
}
bool SHInputManager::AnyControllerButtonDown(SH_CONTROLLERCODE* firstDetected, size_t cNum) noexcept
{
if (cNum >= XUSER_MAX_COUNT) return false;
for (size_t i = 0; i < NUM_CONTROLLER_BUTTON; ++i)
{
if (controllerConsideredHeld(i, controllers[cNum][i]) && !controllerConsideredHeld(i, controllersLast[cNum][i]))
{
if (firstDetected) *firstDetected = static_cast<SH_CONTROLLERCODE>(i);
return true;
}
}
return false;
}
bool SHInputManager::AnyControllerButtonUp(SH_CONTROLLERCODE* firstDetected, size_t cNum) noexcept
{
if (cNum >= XUSER_MAX_COUNT) return false;
for (size_t i = 0; i < NUM_CONTROLLER_BUTTON; ++i)
{
if (!controllerConsideredHeld(i, controllers[cNum][i]) && controllerConsideredHeld(i, controllersLast[cNum][i]))
{
if (firstDetected) *firstDetected = static_cast<SH_CONTROLLERCODE>(i);
return true;
}
}
return false;
}
//Only get of largest magnitude
double SHInputManager::GetBindingAxis(std::string bindingName, size_t cNum) noexcept
{
//Over keycodes, prioritise positive
for (SH_KEYCODE k : bindings[bindingName].positiveKeyCodes)
{
if (GetKey(k)) return 1.0;
}
for (SH_KEYCODE k : bindings[bindingName].negativeKeyCodes)
{
if (GetKey(k)) return -1.0;
}
double largestMagnitude = 0.0;
//Over controllerCodes
for (SH_CONTROLLERCODE c : bindings[bindingName].positiveControllerCodes)
{
double newValue = 0.0;
if (GetControllerInput(c, &newValue, nullptr, nullptr, cNum))
if (std::abs(newValue) > std::abs(largestMagnitude)) largestMagnitude = newValue;
}
for (SH_CONTROLLERCODE c : bindings[bindingName].negativeControllerCodes)
{
double newValue = 0.0;
if (GetControllerInput(c, &newValue, nullptr, nullptr, cNum))
if (std::abs(newValue) > std::abs(largestMagnitude)) largestMagnitude = -newValue;
}
return largestMagnitude;
}
bool SHInputManager::GetBindingPositiveButton(std::string bindingName, size_t cNum) noexcept
{
if (cNum >= XUSER_MAX_COUNT) return false;
//Over keycodes
for (SH_KEYCODE k : bindings[bindingName].positiveKeyCodes)
{
if (GetKey(k)) return true;
}
//Over controller buttons
for (SH_CONTROLLERCODE c : bindings[bindingName].positiveControllerCodes)
{
if (GetControllerInput(c, nullptr, nullptr, nullptr, cNum)) return true;
}
return false;
}
bool SHInputManager::GetBindingNegativeButton(std::string bindingName, size_t cNum) noexcept
{
if (cNum >= XUSER_MAX_COUNT) return false;
//Over keycodes
for (SH_KEYCODE k : bindings[bindingName].negativeKeyCodes)
{
if (GetKey(k)) return true;
}
//Over controller buttons
for (SH_CONTROLLERCODE c : bindings[bindingName].negativeControllerCodes)
{
if (GetControllerInput(c, nullptr, nullptr, nullptr, cNum)) return true;
}
return false;
}
bool SHInputManager::GetBindingPositiveButtonDown(std::string bindingName, size_t cNum) noexcept
{
if (cNum >= XUSER_MAX_COUNT) return false;
//Over keycodes
for (SH_KEYCODE k : bindings[bindingName].positiveKeyCodes)
{
if (GetKeyDown(k)) return true;
}
//Over controller buttons
for (SH_CONTROLLERCODE c : bindings[bindingName].positiveControllerCodes)
{
if (GetControllerInputDown(c, nullptr, cNum)) return true;
}
return false;
}
bool SHInputManager::GetBindingNegativeButtonDown(std::string bindingName, size_t cNum) noexcept
{
if (cNum >= XUSER_MAX_COUNT) return false;
//Over keycodes
for (SH_KEYCODE k : bindings[bindingName].negativeKeyCodes)
{
if (GetKeyDown(k)) return true;
}
//Over controller buttons
for (SH_CONTROLLERCODE c : bindings[bindingName].negativeControllerCodes)
{
if (GetControllerInputDown(c, nullptr, cNum)) return true;
}
return false;
}
bool SHInputManager::GetBindingPositiveButtonUp(std::string bindingName, size_t cNum) noexcept
{
if (cNum >= XUSER_MAX_COUNT) return false;
//Over keycodes
for (SH_KEYCODE k : bindings[bindingName].positiveKeyCodes)
{
if (GetKeyUp(k)) return true;
}
//Over controller buttons
for (SH_CONTROLLERCODE c : bindings[bindingName].positiveControllerCodes)
{
if (GetControllerInputUp(c, nullptr, cNum)) return true;
}
return false;
}
bool SHInputManager::GetBindingNegativeButtonUp(std::string bindingName, size_t cNum) noexcept
{
if (cNum >= XUSER_MAX_COUNT) return false;
//Over keycodes
for (SH_KEYCODE k : bindings[bindingName].negativeKeyCodes)
{
if (GetKeyUp(k)) return true;
}
//Over controller buttons
for (SH_CONTROLLERCODE c : bindings[bindingName].negativeControllerCodes)
{
if (GetControllerInputUp(c, nullptr, cNum)) return true;
}
return false;
}
//Fetches longest hold time
double SHInputManager::GetBindingPositiveHeldTime(std::string bindingName, size_t cNum) noexcept
{
if (cNum >= XUSER_MAX_COUNT) return 0.0;
double maxHeldTime = 0.0;
//Over keycodes
for (SH_KEYCODE k : bindings[bindingName].positiveKeyCodes)
{
if (GetKeyHeldTime(k) > maxHeldTime) maxHeldTime = GetKeyHeldTime(k);
}
//Over controller buttons
for (SH_CONTROLLERCODE c : bindings[bindingName].positiveControllerCodes)
{
if (GetControllerInputHeldTime(c, cNum) > maxHeldTime) maxHeldTime = GetControllerInputHeldTime(c);
}
return maxHeldTime;
}
double SHInputManager::GetBindingNegativeHeldTime(std::string bindingName, size_t cNum) noexcept
{
if (cNum >= XUSER_MAX_COUNT) return 0.0;
double maxHeldTime = 0.0;
//Over keycodes
for (SH_KEYCODE k : bindings[bindingName].negativeKeyCodes)
{
if (GetKeyHeldTime(k) > maxHeldTime) maxHeldTime = GetKeyHeldTime(k);
}
//Over controller buttons
for (SH_CONTROLLERCODE c : bindings[bindingName].negativeControllerCodes)
{
if (GetControllerInputHeldTime(c, cNum) > maxHeldTime) maxHeldTime = GetControllerInputHeldTime(c);
}
return maxHeldTime;
}
//Fetches shortest release time
double SHInputManager::GetBindingPositiveReleasedTime(std::string bindingName, size_t cNum) noexcept
{
if (cNum >= XUSER_MAX_COUNT) return 0.0;
double minReleaseTime = _HUGE_ENUF;
//Over keycodes
for (SH_KEYCODE k : bindings[bindingName].positiveKeyCodes)
{
if (GetKeyReleasedTime(k) < minReleaseTime) minReleaseTime = GetKeyReleasedTime(k);
}
//Over controller buttons
for (SH_CONTROLLERCODE c : bindings[bindingName].positiveControllerCodes)
{
if (GetControllerInputReleasedTime(c, cNum) < minReleaseTime) minReleaseTime = GetControllerInputReleasedTime(c);
}
return minReleaseTime;
}
double SHInputManager::GetBindingNegativeReleasedTime(std::string bindingName, size_t cNum) noexcept
{
if (cNum >= XUSER_MAX_COUNT) return 0.0;
double minReleaseTime = _HUGE_ENUF;
//Over keycodes
for (SH_KEYCODE k : bindings[bindingName].negativeKeyCodes)
{
if (GetKeyReleasedTime(k) < minReleaseTime) minReleaseTime = GetKeyReleasedTime(k);
}
//Over controller buttons
for (SH_CONTROLLERCODE c : bindings[bindingName].negativeControllerCodes)
{
if (GetControllerInputReleasedTime(c, cNum) < minReleaseTime) minReleaseTime = GetControllerInputReleasedTime(c);
}
return minReleaseTime;
}
//Only for mouse movement
//Get largest delta
double SHInputManager::GetBindingMouseVelocity(std::string bindingName, size_t cNum) noexcept
{
if (cNum >= XUSER_MAX_COUNT) return 0.0;
//Mouse velocity
double velX = 0.0;
double velY = 0.0;
GetMouseVelocity(&velX, &velY);
return bindings[bindingName].mouseXPositiveMultiplier * velX + bindings[bindingName].mouseYPositiveMultiplier * velY;
}
} //namespace SHADE

View File

@ -10,9 +10,12 @@
*********************************************************************/
#pragma once
//#include <Xinput.h>
//#include "../../SHADE_Managed/src/SHpch.h"
#include <Xinput.h>
#include <map>
#include <set>
#include "../../SHADE_Managed/src/SHpch.h"
#include "SH_API.h"
#pragma comment(lib, "xinput.lib")
namespace SHADE
{
@ -268,6 +271,58 @@ namespace SHADE
OEM_CLEAR
};
enum class SH_CONTROLLERCODE
{
//Digital
DPAD_UP,
DPAD_DOWN,
DPAD_LEFT,
DPAD_RIGHT,
START,
BACK,
LEFT_THUMBSTICK,
RIGHT_THUMBSTICK,
LEFT_SHOULDER,
RIGHT_SHOULDER,
A,
B,
X,
Y,
//1 Byte Unsigned Analog
LEFT_TRIGGER,
RIGHT_TRIGGER,
//2 Byte Signed Analog
LEFT_THUMBSTICK_X,
LEFT_THUMBSTICK_Y,
RIGHT_THUMBSTICK_X,
RIGHT_THUMBSTICK_Y
};
private:
/*------------------------------------------------------------------------*/
/* Struct for logical bindings */
/*------------------------------------------------------------------------*/
struct SH_API SHLogicalBindingData
{
//Key codes mapped to positive
std::set<SH_KEYCODE> positiveKeyCodes;
//Key codes mapped to negative
std::set<SH_KEYCODE> negativeKeyCodes;
//Controller Codes mapped to positive
std::set<SH_CONTROLLERCODE> positiveControllerCodes;
//Controller Codes mapped to negative
std::set<SH_CONTROLLERCODE> negativeControllerCodes;
//Mouse movement mapped to axes?
double mouseXPositiveMultiplier;
double mouseYPositiveMultiplier;
};
public:
//Updates current state of the input, with dt to be fetched from FRC
//TODO should dt be fixed or variable?
@ -392,7 +447,7 @@ namespace SHADE
keysToggleLast[static_cast<int>(key)]);
}
//Mouse/////////////
//Mouse/////////////////////////////////////////////////////
//Get the mouse location with respect to the screen
static inline void GetMouseScreenPosition (int* x = nullptr,
@ -428,7 +483,95 @@ namespace SHADE
return mouseWheelVerticalDelta;
}
//GET INPUT TIMINGS///////////////////////////////////////////////////////////
/*------------------------------------------------------------------------*/
/* Input state accessors (KB & M) */
/*------------------------------------------------------------------------*/
//How many controller inputs of any kind are being used now
static inline unsigned GetControllerInputCount(size_t cNum = 0) noexcept
{
if (cNum >= XUSER_MAX_COUNT) return 0;
return controllersInputCount[cNum];
}
//How many controller buttons are being pressed now
//Subtract from getControllerInputCount() for analog triggers / thumbsticks
static inline unsigned GetControllerButtonCount(size_t cNum = 0) noexcept
{
if (cNum >= XUSER_MAX_COUNT) return 0;
return controllersButtonCount[cNum];
}
//Any controller input being held
//For analog, this means going being deadzone values
//controllerNum should be between 0 and 3
static bool AnyControllerInput(SH_CONTROLLERCODE* firstDetected = nullptr, size_t controllerNum = 0) noexcept;
//Any controller input activated in THIS FRAME ONLY
//For analog, this means going being deadzone values
//controllerNum should be between 0 and 3
static bool AnyControllerInputDown(SH_CONTROLLERCODE* firstDetected = nullptr, size_t controllerNum = 0) noexcept;
//Any controller input deactivated in THIS FRAME ONLY
//For analog, this means going below deadzone values
//controllerNum should be between 0 and 3
static bool AnyControllerInputUp(SH_CONTROLLERCODE* firstDetected = nullptr, size_t controllerNum = 0) noexcept;
//Any DIGITAL controller buttons being held
//controllerNum should be between 0 and 3
static bool AnyControllerButton(SH_CONTROLLERCODE* firstDetected = nullptr, size_t controllerNum = 0) noexcept;
//Any DIGITAL controller button activated in THIS FRAME ONLY
//controllerNum should be between 0 and 3
static bool AnyControllerButtonDown(SH_CONTROLLERCODE* firstDetected = nullptr, size_t controllerNum = 0) noexcept;
//Any DIGITAL controller button deactivated in THIS FRAME ONLY
//controllerNum should be between 0 and 3
static bool AnyControllerButtonUp(SH_CONTROLLERCODE* firstDetected = nullptr, size_t controllerNum = 0) noexcept;
//If controller input is being held in the current frame
//normalisedValue is the value of the input between 0 and 1, relevant for trigger and thumbstick data
//controllerNum should be between 0 and 3
static inline bool GetControllerInput(SH_CONTROLLERCODE input,
double* normalisedValue = nullptr,
double* heldTime = nullptr,
double* releasedTime = nullptr,
size_t controllerNum = 0) noexcept
{
if (controllerNum >= XUSER_MAX_COUNT) return false;
if (normalisedValue) *normalisedValue = controllerNormalisedValue(static_cast<size_t>(input), controllers[controllerNum][static_cast<size_t>(input)]);
if (heldTime) *heldTime = controllersHeldTime[controllerNum][static_cast<size_t>(input)];
if (releasedTime) *releasedTime = controllersReleasedTime[controllerNum][static_cast<size_t>(input)];
return controllerConsideredHeld(static_cast<size_t>(input), controllers[controllerNum][static_cast<size_t>(input)]);
}
//If controller input was considered to be held down in THIS FRAME ONLY
//controllerNum should be between 0 and 3
static inline bool GetControllerInputDown(SH_CONTROLLERCODE input,
double* releasedTime = nullptr,
size_t controllerNum = 0) noexcept
{
if (controllerNum >= XUSER_MAX_COUNT) return false;
if (releasedTime) *releasedTime = controllersReleasedTime[controllerNum][static_cast<size_t>(input)];
return (controllerConsideredHeld(static_cast<size_t>(input), controllers[controllerNum][static_cast<size_t>(input)]) &&
!controllerConsideredHeld(static_cast<size_t>(input), controllersLast[controllerNum][static_cast<size_t>(input)]));
}
//If controller input was considered to be released in THIS FRAME ONLY
//controllerNum should be between 0 and 3
static inline bool GetControllerInputUp(SH_CONTROLLERCODE input,
double* releasedTime = nullptr,
size_t controllerNum = 0) noexcept
{
if (controllerNum >= XUSER_MAX_COUNT) return false;
if (releasedTime) *releasedTime = controllersReleasedTime[controllerNum][static_cast<size_t>(input)];
return (!controllerConsideredHeld(static_cast<size_t>(input), controllers[controllerNum][static_cast<size_t>(input)]) &&
controllerConsideredHeld(static_cast<size_t>(input), controllersLast[controllerNum][static_cast<size_t>(input)]));
}
/*------------------------------------------------------------------------*/
/* Timing accessors */
/*------------------------------------------------------------------------*/
//Keyboard/////////////
@ -456,6 +599,191 @@ namespace SHADE
return keysToggleOffTime[static_cast<int>(key)];
}
//Controller//////////////////////
//How long has this controller input been considered to be held down for
static inline double GetControllerInputHeldTime(SH_CONTROLLERCODE code,
size_t controllerNum = 0) noexcept
{
if (controllerNum >= XUSER_MAX_COUNT) return 0.0;
return controllersHeldTime[controllerNum][static_cast<size_t>(code)];
}
//How long has this controller input been considered to be released for
static inline double GetControllerInputReleasedTime(SH_CONTROLLERCODE code,
size_t controllerNum = 0) noexcept
{
if (controllerNum >= XUSER_MAX_COUNT) return 0.0;
return controllersReleasedTime[controllerNum][static_cast<size_t>(code)];
}
/*------------------------------------------------------------------------*/
/* Binding Functions */
/*------------------------------------------------------------------------*/
//Add a new binding to the map
static inline void BindingsAdd(std::string newBindingName) noexcept
{
bindings.insert({ newBindingName, SHLogicalBindingData() });
}
//Remove a binding from the map
//Returns 1 if found and removed, 0 if not found
static inline size_t BindingsRemove(std::string targetBindingName) noexcept
{
return bindings.erase(targetBindingName);
}
//Clears all bindings from the list
static inline void BindingsClear() noexcept
{
bindings.clear();
}
//Get the number of bindings present
static inline size_t BindingsCount() noexcept
{
return bindings.size();
}
//Check positive keycodes to binding
static inline std::set<SH_KEYCODE> const& BindingsGetPositiveKeyCodes(std::string bindingName) noexcept
{
return bindings[bindingName].positiveKeyCodes;
}
//Add positive SH_KEYCODE to binding
static inline void BindingsAddPositiveKeyCode(std::string targetBindingName,
SH_KEYCODE toAdd) noexcept
{
bindings[targetBindingName].positiveKeyCodes.insert(toAdd);
}
//Remove positive SH_KEYCODE from binding
//If toRemove found and removed, returns 1. Otherwise, 0.
static inline size_t BindingsRemovePositiveKeyCode(std::string targetBindingName,
SH_KEYCODE toRemove) noexcept
{
return bindings[targetBindingName].positiveKeyCodes.erase(toRemove);
}
//Check negative keycodes to binding
static inline std::set<SH_KEYCODE> const& BindingsGetNegativeKeyCodes(std::string bindingName) noexcept
{
return bindings[bindingName].negativeKeyCodes;
}
//Add negative SH_KEYCODE to binding
static inline void BindingsAddNegativeKeyCode(std::string targetBindingName,
SH_KEYCODE toAdd) noexcept
{
bindings[targetBindingName].negativeKeyCodes.insert(toAdd);
}
//Remove negative SH_KEYCODE from binding
//If toRemove found and removed, returns 1. Otherwise, 0.
static inline size_t BindingsRemoveNegativeKeyCode(std::string targetBindingName,
SH_KEYCODE toRemove) noexcept
{
return bindings[targetBindingName].negativeKeyCodes.erase(toRemove);
}
//Check positive controllercodes to binding
static inline std::set<SH_CONTROLLERCODE> const& BindingsGetPositiveControllerCodes(std::string bindingName) noexcept
{
return bindings[bindingName].positiveControllerCodes;
}
//Add positive SH_CONTROLLERCODE to binding
static inline void BindingsAddPositiveControllerCode(std::string targetBindingName,
SH_CONTROLLERCODE toAdd) noexcept
{
bindings[targetBindingName].positiveControllerCodes.insert(toAdd);
}
//Remove positive SH_CONTROLLERCODE from binding
//If toRemove found and removed, returns 1. Otherwise, 0.
static inline size_t BindingsRemovePositiveControllerCode(std::string targetBindingName,
SH_CONTROLLERCODE toRemove) noexcept
{
return bindings[targetBindingName].positiveControllerCodes.erase(toRemove);
}
//Check negative controllercodes to binding
static inline std::set<SH_CONTROLLERCODE> const& BindingsGetNegativeControllerCodes(std::string bindingName) noexcept
{
return bindings[bindingName].negativeControllerCodes;
}
//Add negative SH_CONTROLLERCODE to binding
static inline void BindingsAddNegativeControllerCode(std::string targetBindingName,
SH_CONTROLLERCODE toAdd) noexcept
{
bindings[targetBindingName].negativeControllerCodes.insert(toAdd);
}
//Remove negative SH_CONTROLLERCODE from binding
//If toRemove found and removed, returns 1. Otherwise, 0.
static inline size_t BindingsRemoveNegativeControllerCode(std::string targetBindingName,
SH_CONTROLLERCODE toRemove) noexcept
{
return bindings[targetBindingName].negativeControllerCodes.erase(toRemove);
}
//Mouse movement bindings
static inline double const BindingsGetMouseXPositiveMultiplier(std::string bindingName) noexcept
{
return bindings[bindingName].mouseXPositiveMultiplier;
}
static inline void BindingsSetMouseXPositiveMultiplier(std::string bindingName, double newValue) noexcept
{
bindings[bindingName].mouseXPositiveMultiplier = newValue;
}
static inline double const BindingsGetMouseYPositiveMultiplier(std::string bindingName) noexcept
{
return bindings[bindingName].mouseXPositiveMultiplier;
}
static inline void BindingsSetMouseYPositiveMultiplier(std::string bindingName, double newValue) noexcept
{
bindings[bindingName].mouseXPositiveMultiplier = newValue;
}
//Get the axis value of binding, between -1 and 1
static double GetBindingAxis(std::string bindingName, size_t controllerNumber = 0) noexcept;
//Whether binding is being held or not
//Does not work for mouse movement
static bool GetBindingPositiveButton(std::string bindingName, size_t controllerNumber = 0) noexcept;
static bool GetBindingNegativeButton(std::string bindingName, size_t controllerNumber = 0) noexcept;
//Whether binding is pressed down IN THIS FRAME ONLY
//Does not work for mouse movement
static bool GetBindingPositiveButtonDown(std::string bindingName, size_t controllerNumber = 0) noexcept;
static bool GetBindingNegativeButtonDown(std::string bindingName, size_t controllerNumber = 0) noexcept;
//Whether binding is released IN THIS FRAME ONLY
//Does not work for mouse movement
static bool GetBindingPositiveButtonUp(std::string bindingName, size_t controllerNumber = 0) noexcept;
static bool GetBindingNegativeButtonUp(std::string bindingName, size_t controllerNumber = 0) noexcept;
//Binding times
//Does not work for mouse movement
static double GetBindingPositiveHeldTime(std::string bindingName, size_t controllerNumber = 0) noexcept;
static double GetBindingNegativeHeldTime(std::string bindingName, size_t controllerNumber = 0) noexcept;
//Does not work for mouse movement
static double GetBindingPositiveReleasedTime(std::string bindingName, size_t controllerNumber = 0) noexcept;
static double GetBindingNegativeReleasedTime(std::string bindingName, size_t controllerNumber = 0) noexcept;
//Binding mouse velocity
//Only for mouse movement
static double GetBindingMouseVelocity(std::string bindingName, size_t controllerNumber = 0) noexcept;
/*------------------------------------------------------------------------*/
/* Other Functions */
/*------------------------------------------------------------------------*/
@ -482,10 +810,36 @@ namespace SHADE
/*------------------------------------------------------------------------*/
static constexpr size_t MAX_KEYS = UCHAR_MAX + 1;
//How many recognised controller inputs there are
//To see the list, see the enum class in this file
static constexpr size_t MAX_CONTROLLER_INPUT = 20;
//On/off button count
static constexpr size_t NUM_CONTROLLER_BUTTON = 14;
//8-bit trigger values
static constexpr size_t NUM_CONTROLLER_TRIGGER = 2;
//16-bit thumbstick values
static constexpr size_t NUM_CONTROLLER_THUMBSTICK = 4;
/*------------------------------------------------------------------------*/
/* Data Members */
/*------------------------------------------------------------------------*/
//If the last input is from controller(s) or KB/M
//True if from controller(s), False if from KB/M
//Useful for switching control hints between controllers and KB/M
static bool controllerInUse;
//BINDINGS//////////////////////////////////////////////////////////////////
//Key is for binding names, they must be unique
//Multiple physical inputs per virtual/logical input are to be added to
//sets inside the logicalBinding values
//TODO make this an array of 4 / 5 users for multiplayer support
static std::map<std::string, SHLogicalBindingData> bindings;
//KEYBOARD AND MOUSE BUTTONS////////////////////////////////////////////////
//How many keys are presently being pressed
@ -558,15 +912,48 @@ namespace SHADE
//CONTROLLER VARIABLES//////////////////////////////////////////////////////
//OTHER VARIABLES///////////////////////////////////////////////////////////
//Count how many controllers are presently connected
//Useful for if the game is to decide to take in controller or KB/M input
//Between 0 and 4 (inclusive)
static unsigned char controllersConnectedCount;
//Axis bindings
//X
//Y
//How many inputs (of any kind) on the controller are being used now
//Includes analog triggers and thumbsticks
static unsigned controllersInputCount[XUSER_MAX_COUNT];
//Other mappings
//How many DIGITAL buttons on the controller are being pressed now
static unsigned controllersButtonCount[XUSER_MAX_COUNT];
//Buffer
//Current state of controllers
//First index is for controller number
//Second index is for input type
static short controllers[XUSER_MAX_COUNT][MAX_CONTROLLER_INPUT];
//State of controllers in the last frame
//First index is for controller number
//Second index is for input type
static short controllersLast[XUSER_MAX_COUNT][MAX_CONTROLLER_INPUT];
//Held and released times
//Controller held durations
//Stops ticking up when released
//Will be reset when held again
static double controllersHeldTime[XUSER_MAX_COUNT][MAX_CONTROLLER_INPUT];
//Key released durations
//Stops ticking up when held
//Will be reset when off again
static double controllersReleasedTime[XUSER_MAX_COUNT][MAX_CONTROLLER_INPUT];
/*------------------------------------------------------------------------*/
/* Internal Helper Functions */
/*------------------------------------------------------------------------*/
//Internal helper function for checking if input is considered held or not
static bool controllerConsideredHeld(size_t inputIdx, short value) noexcept;
//Internal helper function for getting normalised controller value
static double controllerNormalisedValue(size_t inputIdx, short value) noexcept;
};
}