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 is a sample

return
{
    {
        Action = "Up",
        Button = 0,
    },
    {
        Action = "Down",
        Button = 1,
    },
    {
        Action = "Left",
        Button = 2,
    },
    {
        Action = "Right",
        Button = 3,
    },
}
//Initialize the Keybindings
 {
 std::string keyBindingsPath = "data/KeyBindings/firstdraft.keybinding";
 if (!(result = eae6320::GamepadInput::Initialize(keyBindingsPath)))
 {
     EAE6320_ASSERTF(false, "Can't initialize Game without the Keybindings");
     return result;
 }
 }


The values for buttons are integers which need to be consistent with the enum specified above.

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

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

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

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

if (UserInput::IsKeyPressed('H')) {
    eae6320::GamepadInput::RumbleControllerForSeconds(0.5f, 5);
}

 

Stick Input

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

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:

Apart from the usual referencing and stuff, you need to call two functions of my project from cbApplication.cpp’s UpdateUntilExit function. These functions are UpdateState() and UpdateBasedOnTime(i_secondsElapsedSinceLastSimulationUpdate). 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 my cbApplication.cpp calling UpdateBasedOnTime

 

 

 

Calling UpdateState at the end of the UpdateUntilExit function

 You need to add the lua files to the AssetsToBuild.Lua 

return
{
 shaders =
 {
     { path = "Shaders/Vertex/vertexInputLayout_3dObject.shader", arguments = { "vertex" } },
 },
 geometries = 
 {
     "Geometries/plane.geometry",
     "Geometries/sphere.geometry",
     "Geometries/torus.geometry",
 },
 effects = 
 {
     "Effects/ColorAnimation.effect",
     "Effects/standard.effect",
 },
 keyBindings =
 {
     "keyBindings/FirstDraft.keybinding"
 }
}

 

 

 

Things I used that I’ve learned this semester:

  • 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. 


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 JP class.

Download

Download
Download Controller Input System here 239 kB

Leave a comment

Log in with itch.io to leave a comment.