Merge pull request #106 from SHADE-DP/SP3-10-input-management

Bindings and Controllers
Logical bindings are stored in a map of strings (keys) and logical binding data (values). Logical binding data includes:

Positive Key Codes
Negative Key Codes
Positive Controller Codes
Negative Controller Codes
Mouse X Positive Multiplier
Mouse Y Positive Multiplier
Bindings can only handle for one player for now. Up to four users can be implemented with relative ease, but that depends on whether we want to make the game multiplayer for some reason.

Controller inputs are also handled, with 20 different inputs for the controller.
This commit is contained in:
XiaoQiDigipen 2022-10-23 01:06:04 +08:00 committed by GitHub
commit a484cb8e22
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 1055 additions and 11 deletions

View File

@ -20,6 +20,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];
bool SHInputManager::keysLast[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
//How many inputs (of any kind) on the controller are being used now
//Includes analog triggers and thumbsticks
static unsigned controllersInputCount[XUSER_MAX_COUNT];
//Y
//How many DIGITAL buttons on the controller are being pressed now
static unsigned controllersButtonCount[XUSER_MAX_COUNT];
//Other mappings
//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];
//Buffer
//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;
};
}