Input handling

We have now got our objects moving based on velocity and acceleration, so next we must introduce some way of controlling this movement through user input. SDL supports a number of different types of user interface devices including joysticks, gamepads, mouse, and keyboard, all of which will be covered in this chapter, along with how to add them into our framework implementation.

Creating our input handler class

We will create a class that handles all device input, whether it is from controllers, keyboard, or mouse. Let's start with a basic class and build from there. First we need a header file, InputHandler.h.

#include "SDL.h"
class InputHandler
{
public:
  static InputHandler* Instance()
  {
    if(s_pInstance == 0)
    {
      s_pInstance = new InputHandler();
    }

    return s_pInstance;
  }

  void update();
  void clean();

private:

  InputHandler();
  ~InputHandler() {}

  static InputHandler* s_pInstance;
};
typedef InputHandler TheInputHandler;

This is our singleton InputHandler. So far we have an update function which will poll for events and update our InputHandler accordingly, and a clean function which will clear any devices we have initialized. As we start adding device support we will flesh this out a lot more.

Handling joystick/gamepad input

There are tons of joysticks and gamepads out there, often with different amounts of buttons and analog sticks amongst other things. PC game developers have a lot to do when trying to support all of these different gamepads. SDL has good support for joysticks and gamepads, so we should be able to come up with a system that would not be difficult to extend for different gamepad support.

SDL joystick events

There are a few different structures for handling joystick events in SDL. The table below lists each one and their purpose.

SDL joystick event

Purpose

SDL_JoyAxisEvent

Axis motion information

SDL_JoyButtonEvent

Button press and release information

SDL_JoyBallEvent

Trackball event motion information

SDL_JoyHatEvent

Joystick hat position change

The events we are most interested in are the axis motion and the button press events. Each of these events also has an enumerated type that we can check for in our event loop to ensure we are only handling the events we want to handle. The table below shows the type value for each of the above events.

SDL joystick event

Type value

SDL_JoyAxisEvent

SDL_JOYAXISMOTION

SDL_JoyButtonEvent

SDL_JOYBUTTONDOWN or SDL_JOYBUTTONUP

SDL_JoyBallEvent

SDL_JOYBALLMOTION

SDL_JoyHatEvent

SDL_JOYHATMOTION

Note

It's a good idea to use the Joystick Control Panel property in Windows or JoystickShow on OSX to find out which button numbers you will need to use in SDL for a specific button. These applications are invaluable for finding out things about your joystick/gamepad so you can support them properly.

The code we will put in place will assume we are using a Microsoft Xbox 360 controller (which can be used on PC or OSX), as this is an extremely popular controller for PC gaming. Other controllers, such as the PS3 controller, could possibly have different values for buttons and axes. The Xbox 360 controller consists of the following:

  • Two analog sticks
  • Analog sticks press as buttons
  • Start and Select buttons
  • Four face buttons: A, B, X, and Y
  • Four triggers: two digital and two analog
  • A digital directional pad

Initializing joysticks

  1. To use gamepads and joysticks in SDL we first need to initialize them. We are going to add a new public function to the InputHandler class. This function will find out how many joysticks SDL has access to and then initialize them.
    void initialiseJoysticks();
    bool joysticksInitialised() { 
    return m_bJoysticksInitialised; }
  2. We will also declare some private member variables that we will need.
    std::vector<SDL_Joystick*> m_joysticks;
    bool m_bJoysticksInitialised;
  3. The SDL_Joystick* is a pointer to the joystick we will be initializing. We won't actually need these pointers when using the joysticks, but we do need to close them after we are done, so it is helpful for us to keep a list of them for later access. We will now define our initialiseJoysticks function and then go through it.
    void InputHandler::initialiseJoysticks()
    {
      if(SDL_WasInit(SDL_INIT_JOYSTICK) == 0)
      {
        SDL_InitSubSystem(SDL_INIT_JOYSTICK);
      }
    
      if(SDL_NumJoysticks() > 0)
      {
        for(int i = 0; i < SDL_NumJoysticks(); i++)
        {
          SDL_Joystick* joy = SDL_JoystickOpen(i);
          if(SDL_JoystickOpened(i) == 1)
          {
            m_joysticks.push_back(joy);
          }
          else
          {
            std::cout << SDL_GetError();
          }
        }
        SDL_JoystickEventState(SDL_ENABLE);
        m_bJoysticksInitialised = true;
    
        std::cout << "Initialised "<< m_joysticks.size() << " 
        joystick(s)";
      }
      else
      {
        m_bJoysticksInitialised = false;
      }
    }
  4. Let's go through this line-by-line. First we check whether the joystick subsystem has been initialized using SDL_WasInit. If it has not been initialized we then initialize it using SDL_InitSubSystem.
    if(SDL_WasInit(SDL_INIT_JOYSTICK) == 0)
    {
      SDL_InitSubSystem(SDL_INIT_JOYSTICK);
    }
  5. Next is the opening of each available joystick. Before we attempt to open the objects, we use SDL_NumJoysticks to make sure there are some joysticks available. We can then loop through the number of joysticks, opening them in turn with SDL_JoystickOpen. They can then be pushed into our array for closing later.
    if(SDL_NumJoysticks() > 0)
    {
      for(int i = 0; i < SDL_NumJoysticks(); i++)
      {
        SDL_Joystick* joy = SDL_JoystickOpen(i);
        if(SDL_JoystickOpened(i))
        {
          m_joysticks.push_back(joy);
        }
        else
        {
          std::cout << SDL_GetError();
        }
      }
    }
  6. Finally, we tell SDL to start listening for joystick events by enabling SDL_JoystickEventState. We also set our m_bJoysticksEnabled member variable according to how our initialization went.
    SDL_JoystickEventState(SDL_ENABLE);
    m_bJoysticksInitialised = true;
    
    std::cout << "Initialised " << m_joysticks.size() << " joystick(s)";
    
    }
    else
    {
      m_bJoysticksInitialised = false;
    }
  7. So, we now have a way to initialize our joysticks. We have two other functions to define, the update and clean functions. The clean function will loop through our SDL_Joystick* array and call SDL_JoystickClose on each iteration.
    void InputHandler::clean()
    {
      if(m_bJoysticksInitialised)
      {
        for(unsigned int i = 0; i < SDL_NumJoysticks(); i++)
        {
          SDL_JoystickClose(m_joysticks[i]);
        }
      }
    }
  8. The update function will be called in each frame in the main game loop to update the event state. For now though it will simply listen for a quit event and call the game's quit function (this function simply calls SDL_Quit()).
    void InputHandler::update()
    {
      SDL_Event event;
      while(SDL_PollEvent(&event))
      {
        if(event.type == SDL_QUIT)
        {
          TheGame::Instance()->quit();
        }
      }
    }
  9. Now we will use this InputHandler in our Game class functions. First we call initialiseJoysticks in the Game::init function.
    TheInputHandler::Instance()->initialiseJoysticks();

    And we will update it in the Game::handleEvents function, clearing out anything we had before:

    void Game::handleEvents()
    {
      TheInputHandler::Instance()->update();
    }
  10. The clean function can also be added to our Game::clean function.
    TheInputHandler::Instance()->clean();
  11. We can now plug in a pad or joystick and run the build. If everything is working according to plan we should get the following output, with x being the number of joysticks you have plugged in:
    Initialised x joystick(s)
  12. Ideally we want to easily use one or more controllers with no change to our code. We already have a way to load in and open as many controllers that are plugged in, but we need to know which event corresponds to which controller; we do this using some information stored in the event. Each joystick event will have a which variable stored within it. Using this will allow us to find out which joystick the event came from.
    if(event.type == SDL_JOYAXISMOTION) // check the type value
    {
      int whichOne = event.jaxis.which; // get which controller

Listening for and handling axis movement

We are not going to handle the analog sticks in an analog way. Instead they will be handled as digital information, that is, they are either on or off. Our controller has four axes of motion, two for the left analog stick and two for the right.

We will make the following assumptions about our controller (you can use an external application to find out the specific values for your controller):

  • Left and right movement on stick one is axis 0
  • Up and down movement on stick one is axis 1
  • Left and right movement on stick two is axis 3
  • Up and down movement on stick two is axis 4

The Xbox 360 controller uses axes 2 and 5 for the analog triggers. To handle multiple controllers with multiple axes we will create a vector of pairs of Vector2D*, one for each stick.

std::vector<std::pair<Vector2D*, Vector2D*>> m_joystickValues;

We use the Vector2D values to set whether a stick has moved up, down, left, or right. Now when we initialize our joysticks we need to create a pair of Vector2D* in the m_joystickValues array.

for(int i = 0; i < SDL_NumJoysticks(); i++)
{
  SDL_Joystick* joy = SDL_JoystickOpen(i);
  if(SDL_JoystickOpened(i))
  {
    m_joysticks.push_back(joy);
    m_joystickValues.push_back(std::make_pair(new 
    Vector2D(0,0),new Vector2D(0,0))); // add our pair
  }
  else
  {
    std::cout << SDL_GetError();
  }
}

We need a way to grab the values we need from this array of pairs; we will declare two new functions to the InputHandler class:

int xvalue(int joy, int stick);
int yvalue(int joy, int stick);

The joy parameter is the identifier (ID) of the joystick we want to use, and the stick is 1 for the left stick and 2 for the right stick. Let's define these functions:

int InputHandler::xvalue(int joy, int stick);
{
  if(m_joystickValues.size() > 0)
  {
    if(stick == 1)
    {
      return m_joystickValues[joy].first->getX();
    }
    else if(stick == 2)
    {
      return m_joystickValues[joy].second->getX();
    }
  }
  return 0;
}

int InputHandler::yvalue(int joy, int stick)
{
  if(m_joystickValues.size() > 0)
  {
    if(stick == 1)
    {
      return m_joystickValues[joy].first->getY();
    }
    else if(stick == 2)
    {
      return m_joystickValues[joy].second->getY();
    }
  }
  return 0;
}

So we grab the x or y value based on the parameters passed to each function. The first and second values are the first or second objects of the pair in the array, with joy being the index of the array. We can now set these values accordingly in the event loop.

SDL_Event event;
while(SDL_PollEvent(&event))
{
  if(event.type == SDL_QUIT)
  {
    TheGame::Instance()->quit();
  }

  if(event.type == SDL_JOYAXISMOTION)
  {
    int whichOne = event.jaxis.which;

    // left stick move left or right
    if(event.jaxis.axis == 0)
    {
      if (event.jaxis.value > m_joystickDeadZone)
      {
        m_joystickValues[whichOne].first->setX(1);
      }
      else if(event.jaxis.value < -m_joystickDeadZone)
      {
        m_joystickValues[whichOne].first->setX(-1);
      }
      else
      {
        m_joystickValues[whichOne].first->setX(0);
      }
    }

    // left stick move up or down
    if(event.jaxis.axis == 1)
    {
      if (event.jaxis.value > m_joystickDeadZone)
      {
        m_joystickValues[whichOne].first->setY(1);
      }
      else if(event.jaxis.value < -m_joystickDeadZone)
      {
        m_joystickValues[whichOne].first->setY(-1);
      }
      else
      {
        m_joystickValues[whichOne].first->setY(0);
      }
    }

    // right stick move left or right
    if(event.jaxis.axis == 3)
    {
      if (event.jaxis.value > m_joystickDeadZone)
      {
        m_joystickValues[whichOne].second->setX(1);
      }
      else if(event.jaxis.value < -m_joystickDeadZone)
      {
        m_joystickValues[whichOne].second->setX(-1);
      }
      else
      {
        m_joystickValues[whichOne].second->setX(0);
      }
    }

    // right stick move up or down
    if(event.jaxis.axis == 4)
    {
      if (event.jaxis.value > m_joystickDeadZone)
      {
        m_joystickValues[whichOne].second->setY(1);
      }
      else if(event.jaxis.value < -m_joystickDeadZone)
      {
        m_joystickValues[whichOne].second->setY(-1);
      }
      else
      {
        m_joystickValues[whichOne].second->setY(0);
      }
    }
  }
}

That is a big function! It is, however, relatively straightforward. We first check for an SDL_JOYAXISMOTION event and we then find out which controller the event came from using the which value.

int whichOne = event.jaxis.which;

From this we know which joystick the event came from and can set a value in the array accordingly; for example:

m_joystickValues[whichOne]

First we check the axis the event came from:

if(event.jaxis.axis == 0) // …1,3,4

If the axis is 0 or 1, it is the left stick, and if it is 3 or 4, it is the right stick. We use first or second of the pair to set the left or right stick. You may also have noticed the m_joystickDeadZone variable. We use this to account for the sensitivity of a controller. We can set this as a constant variable in the InputHandler header file:

const int m_joystickDeadZone = 10000;

The value 10000 may seem like a big value to use for a stick at rest, but the sensitivity of a controller can be very high and so requires a value as large as this. Change this value accordingly for your own controllers.

Just to solidify what we are doing here, let's look closely at one scenario.

// left stick move left or right
{
  if (event.jaxis.value > m_joystickDeadZone)
  {
    m_joystickValues[whichOne].first->setX(1);
  }
  else if(event.jaxis.value < -m_joystickDeadZone)
  {
    m_joystickValues[whichOne].first->setX(-1);
  }
  else
  {
    m_joystickValues[whichOne].first->setX(0);
  }
}

If we get to the second if statement, we know that we are dealing with a left or right movement event on the left stick due to the axis being 0. We have already set which controller the event was from and adjusted whichOne to the correct value. We also want first of the pair to be the left stick. So if the axis is 0, we use the first object of the array and set its x value, as we are dealing with an x movement event. So why do we set the value to 1 or -1? We will answer this by starting to move our Player object.

Open up Player.h and we can start to use our InputHandler to get events. First we will declare a new private function:

private:

void handleInput();

Now in our Player.cpp file we can define this function to work with the InputHandler.

void Player::handleInput()
{
  if(TheInputHandler::Instance()->joysticksInitialised())
  {
    if(TheInputHandler::Instance()->xvalue(0, 1) > 0 || 
    TheInputHandler::Instance()->xvalue(0, 1) < 0)
    {
      m_velocity.setX(1 * TheInputHandler::Instance()->xvalue(0, 
      1));
    }

    if(TheInputHandler::Instance()->yvalue(0, 1) > 0 || 
    TheInputHandler::Instance()->yvalue(0, 1) < 0)
    {
      m_velocity.setY(1 * TheInputHandler::Instance()->yvalue(0, 
      1));
    }

    if(TheInputHandler::Instance()->xvalue(0, 2) > 0 || 
    TheInputHandler::Instance()->xvalue(0, 2) < 0)
    {
      m_velocity.setX(1 * TheInputHandler::Instance()->xvalue(0, 
      2));
    }

    if(TheInputHandler::Instance()->yvalue(0, 2) > 0 || 
    TheInputHandler::Instance()->yvalue(0, 2) < 0)
    {
      m_velocity.setY(1 * TheInputHandler::Instance()->yvalue(0, 
      2));
    }

  }
}

Then we can call this function in the Player::update function.

void Player::update()
{
  m_velocity.setX(0);
  m_velocity.setY(0);

  handleInput(); // add our function

  m_currentFrame = int(((SDL_GetTicks() / 100) % 6));

  SDLGameObject::update();
}

Everything is in place now, but first let's go through how we are setting our movement.

if(TheInputHandler::Instance()->xvalue(0, 1) > 0 || TheInputHandler::Instance()->xvalue(0, 1) < 0)
{
  m_velocity.setX(1 * TheInputHandler::Instance()->xvalue(0, 1));
}

Here, we first check whether xvalue of the left stick is more than 0 (that it has moved). If so, we set our Player x velocity to be the speed we want multiplied by xvalue of the left stick, and we know this is either 1 or -1. As you will know, multiplying a positive number by a negative number results in a negative number, so multiplying the speed we want by -1 will mean we are setting our x velocity to a minus value (move left). We do the same for the other stick and also the y values. Build the project and start moving your Player object with a gamepad. You could also plug in another controller and update the Enemy class to use it.

Dealing with joystick button input

Our next step is to implement a way to handle button input from our controllers. This is actually a lot simpler than handling axes. We need to know the current state of each button so that we can check whenever one has been pressed or released. To do this, we will declare an array of Boolean values, so each controller (the first index into the array) will have an array of Boolean values, one for each button on the controller.

std::vector<std::vector<bool>> m_buttonStates;

We can grab the current button state with a function that looks up the correct button from the correct joystick.

bool getButtonState(int joy, int buttonNumber)
{
  return m_buttonStates[joy][buttonNumber];
}

The first parameter is the index into the array (the joystick ID), and the second is the index into the buttons. Next we are going to have to initialize this array for each controller and each of its buttons. We will do this in the initialiseJoysticks function.

for(int i = 0; i < SDL_NumJoysticks(); i++)
{
  SDL_Joystick* joy = SDL_JoystickOpen(i);
  if(SDL_JoystickOpened(i))
  {
    m_joysticks.push_back(joy);
    m_joystickValues.push_back(std::make_pair(new 
    Vector2D(0,0),new Vector2D(0,0)));

    std::vector<bool> tempButtons;

    for(int j = 0; j < SDL_JoystickNumButtons(joy); j++)
    {
      tempButtons.push_back(false);
    }

    m_buttonStates.push_back(tempButtons);
  }
}

We use SDL_JoystickNumButtons to get the number of buttons for each of our joysticks. We then push a value for each of these buttons into an array. We push false to start, as no buttons are pressed. This array is then pushed into our m_buttonStates array to be used with the getButtonState function. Now we must listen for button events and set the value in the array accordingly.

if(event.type == SDL_JOYBUTTONDOWN)  
{
  int whichOne = event.jaxis.which;

  m_buttonStates[whichOne][event.jbutton.button] = true;
}

if(event.type == SDL_JOYBUTTONUP)
{
  int whichOne = event.jaxis.which;

  m_buttonStates[whichOne][event.jbutton.button] = false;
}

When a button is pressed (SDL_JOYBUTTONDOWN) we get to know which controller it was pressed on and use this as an index into the m_buttonStates array. We then use the button number (event.jbutton.button) to set the correct button to true; the same applies when a button is released (SDL_JOYBUTTONUP). That is pretty much it for button handling. Let's test it out in our Player class.

if(TheInputHandler::Instance()->getButtonState(0, 3))
{
  m_velocity.setX(1);
}

Here we are checking if button 3 has been pressed (Yellow or Y on an Xbox controller) and setting our velocity if it has. That is everything we will cover about joysticks in this book. You will realize that supporting many joysticks is very tricky and requires a lot of tweaking to ensure each one is handled correctly. However, there are ways through which you can start to have support for many joysticks; for example, through a configuration file or even by the use of inheritance for different joystick types.

Handling mouse events

Unlike joysticks, we do not have to initialize the mouse. We can also safely assume that there will only be one mouse plugged in at a time, so we will not need to handle multiple mouse devices. We can start by looking at the available mouse events that SDL covers:

SDL Mouse Event

Purpose

SDL_MouseButtonEvent

A button on the mouse has been pressed or released

SDL_MouseMotionEvent

The mouse has been moved

SDL_MouseWheelEvent

The mouse wheel has moved

Just like the joystick events, each mouse event has a type value; the following table shows each of these values:

SDL Mouse Event

Type Value

SDL_MouseButtonEvent

SDL_MOUSEBUTTONDOWN or SDL_MOUSEBUTTONUP

SDL_MouseMotionEvent

SDL_MOUSEMOTION

SDL_MouseWheelEvent

SDL_MOUSEWHEEL

We will not implement any mouse wheel movement events as most games will not use them.

Using mouse button events

Implementing mouse button events is as straightforward as joystick events, more so even as we have only three buttons to choose from: left, right, and middle. SDL numbers these as 0 for left, 1 for middle, and 2 for right. In our InputHandler header, let's declare a similar array to the joystick buttons, but this time a one-dimensional array, as we won't handle multiple mouse devices.

std::vector<bool> m_mouseButtonStates;

Then in the constructor of our InputHandler we can push our three mouse button states (defaulted to false) into the array:

for(int i = 0; i < 3; i++)
{
  m_mouseButtonStates.push_back(false);
}

Back in our header file, let's create an enum attribute to help us with the values of the mouse buttons. Put this above the class so that other files that include our InputHandler.h header can use it too.

enum mouse_buttons
{
    LEFT = 0,
    MIDDLE = 1,
    RIGHT = 2
};

Now let's handle mouse events in our event loop:

if(event.type == SDL_MOUSEBUTTONDOWN)
{
  if(event.button.button == SDL_BUTTON_LEFT)
  {
    m_mouseButtonStates[LEFT] = true;
  }

  if(event.button.button == SDL_BUTTON_MIDDLE)
  {
    m_mouseButtonStates[MIDDLE] = true;
  }

  if(event.button.button == SDL_BUTTON_RIGHT)
  {
    m_mouseButtonStates[RIGHT] = true;
  }
}

if(event.type == SDL_MOUSEBUTTONUP)
{
  if(event.button.button == SDL_BUTTON_LEFT)
  {
    m_mouseButtonStates[LEFT] = false;
  }

  if(event.button.button == SDL_BUTTON_MIDDLE)
  {
    m_mouseButtonStates[MIDDLE] = false;
  }

  if(event.button.button == SDL_BUTTON_RIGHT)
  {
    m_mouseButtonStates[RIGHT] = false;
  }
}

We also need a function to access our mouse button states. Let's add this public function to the InputHandler header file:

bool getMouseButtonState(int buttonNumber)
{
  return m_mouseButtonStates[buttonNumber];
}

That is everything we need for mouse button events. We can now test it in our Player class.

if(TheInputHandler::Instance()->getMouseButtonState(LEFT))
{
  m_velocity.setX(1);
}

Handling mouse motion events

Mouse motion events are very important, especially in big 3D first or third person action titles. For our 2D games, we might want our character to follow the mouse as a way to control our objects, or we might want objects to move to where the mouse was clicked (for a strategy game perhaps). We may even just want to know where the mouse was clicked so that we can use it for menus. Fortunately for us, mouse motion events are relatively simple. We will start by creating a private Vector2D* in the header file to use as the position variable for our mouse:

Vector2D* m_mousePosition;

Next, we need a public accessor for this:

Vector2D* getMousePosition()
{
  return m_mousePosition;
}

And we can now handle this in our event loop:

if(event.type == SDL_MOUSEMOTION)
{
  m_mousePosition->setX(event.motion.x);
  m_mousePosition->setY(event.motion.y);
}

That is all we need for mouse motion. So let's make our Player function follow the mouse position to test this feature:

Vector2D* vec = TheInputHandler::Instance()->getMousePosition();

m_velocity = (*vec - m_position) / 100;

Here we have set our velocity to a vector from the player's current position to the mouse position. You can get this vector by subtracting the desired location from the current location; we already have a vector subtract overloaded operator so this is easy for us. We also divide the vector by 100; this just dampens the speed slightly so that we can see it following rather than just sticking to the mouse position. Remove the / to have your object follow the mouse exactly.

Implementing keyboard input

Our final method of input, and the simplest of the three, is keyboard input. We don't have to handle any motion events, we just want the state of each button. We aren't going to declare an array here because SDL has a built-in function that will give us an array with the state of every key; 1 being pressed and 0 not pressed.

SDL_GetKeyboardState(int* numkeys)

The numkeys parameter will return the number of keys available on the keyboard (the length of the keystate array). So in our InputHandler header we can declare a pointer to the array that will be returned from SDL_GetKeyboardState.

Uint8* m_keystate;

When we update our event handler we can also update the state of the keys; put this at the top of our event loop.

m_keystates = SDL_GetKeyboardState(0);

We will now need to create a simple function that checks whether a key is down or not.

bool InputHandler::isKeyDown(SDL_Scancode key)
{
  if(m_keystates != 0)
  {
    if(m_keystates[key] == 1)
    {
      return true;
    }
    else
    {
      return false;
    }
  }

  return false;
}

This function takes SDL_SCANCODE as a parameter. The full list of SDL_SCANCODE values can be found in the SDL documentation at http://wiki.libsdl.org/moin.cgi.

We can test the keys in our Player class. We will use the arrow keys to move our player.

if(TheInputHandler::Instance()->isKeyDown(SDL_SCANCODE_RIGHT))
{
  m_velocity.setX(2);
}

if(TheInputHandler::Instance()->isKeyDown(SDL_SCANCODE_LEFT))
{
  m_velocity.setX(-2);
}

if(TheInputHandler::Instance()->isKeyDown(SDL_SCANCODE_UP))
{
  m_velocity.setY(-2);
}

if(TheInputHandler::Instance()->isKeyDown(SDL_SCANCODE_DOWN))
{
  m_velocity.setY(2);
}

We now have key handling in place. Test as many keys as you can and look up the SDL_Scancode for the keys you are most likely to want to use.

Wrapping things up

We have now implemented all of the devices we are going to handle, but at the moment our event loop is in a bit of a mess. We need to break it up into more manageable chunks. We will do this with the use of a switch statement for event types and some private functions, within our InputHandler. First let's declare our functions in the header file:

// private functions to handle different event types

// handle keyboard events
void onKeyDown();
void onKeyUp();

// handle mouse events
void onMouseMove(SDL_Event& event);
void onMouseButtonDown(SDL_Event& event);
void onMouseButtonUp(SDL_Event& event);

// handle joysticks events
void onJoystickAxisMove(SDL_Event& event);
void onJoystickButtonDown(SDL_Event& event);
void onJoystickButtonUp(SDL_Event& event);

We pass in the event from the event loop into each function (apart from keys) so that we can handle them accordingly. We now need to create our switch statement in the event loop.

void InputHandler::update()
{
  SDL_Event event;
  while(SDL_PollEvent(&event))
  {
    switch (event.type)
    {
    case SDL_QUIT:
      TheGame::Instance()->quit();
    break;

    case SDL_JOYAXISMOTION:
      onJoystickAxisMove(event);
    break;

    case SDL_JOYBUTTONDOWN:
      onJoystickButtonDown(event);
    break;

    case SDL_JOYBUTTONUP:
      onJoystickButtonUp(event);
    break;

    case SDL_MOUSEMOTION:
      onMouseMove(event);
    break;

    case SDL_MOUSEBUTTONDOWN:
      onMouseButtonDown(event);
    break;

    case SDL_MOUSEBUTTONUP:
      onMouseButtonUp(event);
    break;

    case SDL_KEYDOWN:
      onKeyDown();
    break;

    case SDL_KEYUP:
      onKeyUp();
    break;

    default:
    break;
    }
  }
}

As you can see, we now break up our event loop and call the associated function depending on the type of the event. We can now split all our previous work into these functions; for example, we can put all of our mouse button down handling code into the onMouseButtonDown function.

void InputHandler::onMouseButtonDown(SDL_Event& event)
{
  if(event.button.button == SDL_BUTTON_LEFT)
  {
    m_mouseButtonStates[LEFT] = true;
  }

  if(event.button.button == SDL_BUTTON_MIDDLE)
  {
    m_mouseButtonStates[MIDDLE] = true;
  }

  if(event.button.button == SDL_BUTTON_RIGHT)
  {
    m_mouseButtonStates[RIGHT] = true;
  }
}

The rest of the code for the InputHandler is available within the source code downloads.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.86.211