ControllerInputSystem
A downloadable game
What my project does
- My project reads Input from the Controller and provides easy to use functions for retrieving the button and stick states of the controller.
- Here is a list of functions from my interface.
bool IsConnected(); cResult LoadBindingsFromFile(const std::string& i_keyBindingsPath); void RumbleControllerForSeconds(float i_amount, float i_seconds); void UpdateState(); bool IsButtonPressed( ButtonCodes::eButtonKeyCodes i_buttonCode); float GetTriggerAnalogInput( ButtonCodes::eAnalogButtons i_trigger); float GetStickAnalogInputXAxis( ButtonCodes::eAnalogButtons i_stick); float GetStickAnalogInputYAxis( ButtonCodes::eAnalogButtons i_stick); double GetStickDirection( ButtonCodes::eAnalogButtons i_stick); bool SubscribeToKeyPressEvent( std::function<void( const eae6320::GamepadInput:: ButtonCodes::eButtonKeyCodes& i_button)> i_subscriber); void NotifyEvent( const ButtonCodes::eButtonKeyCodes& i_button); bool IsActionButtonPressed(std::string i_action); void UpdateBasedOnTime( const float i_elapsedSecondCount_sinceLastUpdate); cResult CleanUp();
As you can see most of them are self explainable with the function names. User can detect a button press by calling the IsButtonPressed function by sending the button code which are defined here
namespace ButtonCodes { enum eButtonKeyCodes { CONTROLLER_FACE_BUTTON_UP = 0, CONTROLLER_FACE_BUTTON_DOWN = 1, CONTROLLER_FACE_BUTTON_LEFT = 2, CONTROLLER_FACE_BUTTON_RIGHT = 3, CONTROLLER_DPAD_UP = 4, CONTROLLER_DPAD_DOWN = 5, CONTROLLER_DPAD_LEFT = 6, CONTROLLER_DPAD_RIGHT = 7, CONTROLLER_LEFT_BUMPER = 8, CONTROLLER_RIGHT_BUMPER = 9, CONTROLLER_LEFT_STICK_BUTTON = 10, CONTROLLER_RIGHT_STICK_BUTTON = 11, CONTROLLER_START_BUTTON = 12, CONTROLLER_BACK_BUTTON = 13, CONTROLLER_LEFT_TRIGGER_BUTTON = 14, CONTROLLER_RIGHT_TRIGGER_BUTTON = 15, }; enum eAnalogButtons { CONTROLLER_LEFT_TRIGGER_ANALOG = 0, CONTROLLER_RIGHT_TRIGGER_ANALOG = 1, CONTROLLER_LEFT_STICK_ANALOG = 2, CONTROLLER_RIGHT_STICK_ANALOG =3, }; }
I made different enums for clickable buttons and analog buttons( axis based buttons).
There are functions to read the analog input from triggers and both the sticks X and Y axis analog values.
There is also a subscibing functionality for the keypress events, you subscirbe to the keypress event by passing the function you want to be invoked when a keypress happens with the info of the key press, this function will be called when the corresponding event is triggered.
One more useful function is getting keypress based on the action. You can specify which actions to be mapped to which buttons based on a lua file with the extension .keybinding
Here are the guidelines to follow. This file is also available in the KeyBindingsBuilder project under the name readme.info
This document helps you understand the guidelines in using the .keybinding files Each button is mapped to a string which will be speccified below If you want to bind an action to a button, the button value must match one of the strings that are mentioned below Button strings to follow: "FaceUp", "FaceDown", "FaceRight", "FaceLeft", "DpadUp", "DpadDown", "DpadRight", "DpadLeft", "RightBumper", "LeftBumper", "LeftStick", "RightStick", "LeftTrigger", "RightTrigger", "Start", "Back" Sample usage of .keybinding files (you can also check my FirstDraft.keybinding file from the MyGame Project's content folder) return { { Action = "Up", Button = "DpadUp", }, }
Here is a sample
return { { Action = "Up", Button = "FaceUp", }, { Action = "Down", Button = "FaceDown", }, { Action = "Left", Button = "FaceLeft", }, { Action = "Right", Button = "FaceRight", }, }
Again The values for buttons are strings which need to be consistent with the strings specified above as in the readme.info.
One other function I’ve implemented is getting the direction of the stick
Pointed down will be 0 degrees, left will be 90 degrees, Up will be 180 and right will be 270, It rotates in clock wise direction from 0 to 360, with 0 and 360 pointing down.
There is also a function to rumble the controller, It takes two parameters one is the amount, and other one is time in simulation seconds it needs to rumble.
Here is a sample implementation of some functions from my cMyGame.cpp file
Button Press sample
specify the button using the enum eButtonKeyCodes
If(GamepadInput::IsButtonPressed(GamepadInput::ButtonCodes::CONTROLLER_FACE_BUTTON_LEFT)) { m_cameraToUse->m_rigidBodyState->velocity = Math::cMatrix_transformation::cMatrix_transformation( m_camera->m_rigidBodyState->orientation, m_camera->m_rigidBodyState->position ).GetRightDirection() * -moveSpeed; }
Trigger analog sample
Specify the trigger by using enum eAnalogButtons
if(GamepadInput::GetTriggerAnalogInput(GamepadInput::ButtonCodes::CONTROLLER_LEFT_TRIGGER_ANALOG)) { m_cameraToUse->m_rigidBodyState->velocity = Math::cMatrix_transformation::cMatrix_transformation( m_camera->m_rigidBodyState->orientation, m_camera->m_rigidBodyState->position ).GetBackDirection() * -moveSpeed; }
Action-based Input Sample
Pass the string action you specified in your lua file
if (GamepadInput::IsActionButtonPressed("Right")) { m_cameraToUse->m_rigidBodyState->velocity = Math::cMatrix_transformation::cMatrix_transformation( m_camera->m_rigidBodyState->orientation, m_camera->m_rigidBodyState->position) .GetRightDirection() * moveSpeed; }
Rumble Effects
The first argument to be passed is the strength of the rumble (Its a value between 0 to 1), and the second argument is the amount of time to rumble in seconds(not actual time but simulation time)
if (UserInput::IsKeyPressed('H')) { eae6320::GamepadInput::RumbleControllerForSeconds(0.5f, 5); }
Stick Input
Specify the stick by using the enum eAnalogButtons
float xInput = GamepadInput::GetStickAnalogInputXAxis(GamepadInput::ButtonCodes::CONTROLLER_LEFT_STICK_ANALOG); float yInput = GamepadInput::GetStickAnalogInputYAxis(GamepadInput::ButtonCodes::CONTROLLER_LEFT_STICK_ANALOG); float xValue = Math::cMatrix_transformation:: cMatrix_transformation( m_sphereGameobject->m_rigidBodyState->orientation, m_sphereGameobject->m_rigidBodyState->position ).GetRightDirection().x * xInput; float yValue = Math::cMatrix_transformation:: cMatrix_transformation( m_sphereGameobject->m_rigidBodyState->orientation, m_sphereGameobject->m_rigidBodyState->position ).GetUpDirection().y * yInput; m_sphereGameobject->m_rigidBodyState->velocity = Math::sVector(xValue, yValue, 0.0f) * moveSpeed;
Stick Direction
The direction returned will be in degrees, you can convert them to radians by using the formula I used below.
double angle = GamepadInput::GetStickDirection(GamepadInput::ButtonCodes::CONTROLLER_RIGHT_STICK_ANALOG); float angleInRadians = (float)(angle * 3.1415) / 180; if (angle != 0.0) { m_torusGameobject->m_rigidBodyState->orientation = eae6320::Math::cQuaternion( angleInRadians, eae6320::Math::sVector(0.0, 1.0, 0.0) ); }
Callback Function
void eae6320::cMyGame::ControllerKeyPressCallback( const eae6320::GamepadInput:: ButtonCodes::eButtonKeyCodes& i_button) { float angle = 90.0f; float angleInRadians = (float)(angle * 3.1415) / 180; switch (i_button) { case eae6320::GamepadInput:: ButtonCodes::eButtonKeyCodes::CONTROLLER_DPAD_UP: m_torusGameobject->m_rigidBodyState->orientation = m_torusGameobject->m_rigidBodyState->orientation * eae6320::Math:: cQuaternion( angleInRadians, eae6320::Math::sVector(0.0, 1.0, 0.0) ); break; …./ } }
Subscribing to call back function
std::function<void(const eae6320::GamepadInput::ButtonCodes::eButtonKeyCodes & i_button)> callback; callback = std::bind(&cMyGame::ControllerKeyPressCallback, this, std::placeholders::_1); eae6320::GamepadInput::SubscribeToKeyPressEvent(callback);
Things you need to do in order to use my project:
(Download the source code from below, add them to your project, The GamepadInput project is part of the Engine and KeyBindingsBuilder is part of the Tools)
- Add references first
References of my GamepadInput project
References of my KeyBindings Builder project
You also need to add the property sheets for both my projects the same way we did for all the other assignments. EngineDefaults first and then opengl for 32 bit direct3d for 64 ones.
- Modify the AssetsToBuild.lua to build the keybindings lua files
Add this to AssetsToBuild.lua. Make sure to have the extension as keybinding and the files will be in the folder named KeyBindings in Content folder
- Modify the AssetBuildFuncitons.lua to use KeyBindingsBuilder project to build the .keybindings files
This code is to be added to the AssetBuildFunctions.lua
- Make sure to add GamepadInput to the references list of Application project
- Make the BuildMyGameAssets depend on KeyBindingBuilder project
- More Stuff you need to do
You need to call two functions of my project from an Update function which is called periodically. These are important to keep track of the controller state and know how much time has been passed between update calls. That is everything you need to do in order to use my project.
This is from a source file which is executed periodically.
Calling UpdateState at the end of this Update function
Thats it you are good to go. (of course there is the adding of GamepadInput.h header file to wherever you wanna use the functions)
Key features:
- I created a builder project which reads a lua file and converts into binary format
- Implemented a platform-independent interface.
- Differentiated platform-specific code into seperate .cpp files.
- I made my interface as easy to use as possible.
- I’ve implemented many different functions thinking as a gameplay engineer.
Things I learned in this process
- One interesting thing which I already knew but never tried was passing functions as arguments to other functions. I used std::function to do this.
- I guess the most challenging part was to implement the keypress call back functions. First, I tried doing the windows implemented native c++ events. But that didn’t make any sense as I made progress. Then I implemented callbacks using the standard publisher observer pattern which was suggested by JP.
- Most of the things I did in my project were pretty straightforward, following the things I liked from my professor JP's class.
Leave a comment
Log in with itch.io to leave a comment.