Part 4: Handling Input
Setting up our InputHandler class for Mouse and Keyboard Input
Now that we have our game loop up and running, using our Game class as a singleton instance, we can move on to a crucial element of making a game; handling user input.
We'll be making our input handler class a singleton, but first let's go ahead and create the class and call it "InputHandler". Remember, how I told you to create a class in the previous part of this tutorial.
Once we've got our two files in open, InputHandler.h and InputHandler.cpp we can get to work on first establishing it as a singleton, I will go through this again to reiterate as it isn't very simple. Firstly, in InputHandler.h we want to be working with the file looking like this:
We'll be making our input handler class a singleton, but first let's go ahead and create the class and call it "InputHandler". Remember, how I told you to create a class in the previous part of this tutorial.
Once we've got our two files in open, InputHandler.h and InputHandler.cpp we can get to work on first establishing it as a singleton, I will go through this again to reiterate as it isn't very simple. Firstly, in InputHandler.h we want to be working with the file looking like this:
#pragma once
class InputHandler { public: private: }; |
Once we have our InputHandler.h file looking like so, a pretty-much empty header file, we to add the private parts (lol) of our singleton. If you remember rightly, that is to add a private constructor and deconstructor and then a static variable called instance of which the type will be the class we're in; InputHandler in this case:
#pragma once
class InputHandler { public: private: InputHandler() {} ~InputHandler() {} static InputHandler* instance; }; |
Once we've done with our private parts we can move onto our public function "Instance()", so let's do that and hopefully it will jog your memory as to how we do so:
#pragma once
class InputHandler { public: static InputHandler* Instance() { if (instance == 0) { instance = new InputHandler(); } return instance; } private: InputHandler() {} ~InputHandler() {} static InputHandler* instance; }; |
Quick recap of this function - we call it to get the one and only (static) instance of InputHandler and if that doesn't already exist, we create it.
The next step is to "typedef" our InputHandler class like so:
#pragma once
class InputHandler { public: static InputHandler* Instance() { if (instance == 0) { instance = new InputHandler(); } return instance; } private: InputHandler() {} ~InputHandler() {} static InputHandler* instance; }; typedef InputHandler _InputHandler; |
Using "typedef" allows us to give a unique call name for our InputHandler class to be used so we can identify it as a singleton class.
The final stage of making our class a singleton is to define the private instance variable in our .cpp files to avoid any undeclared variable errors as mentioned before when making Game a singleton. Go into our InputHandler.cpp file, clear all of it's contents apart from the #include line, leave a line below it and add this line:
The final stage of making our class a singleton is to define the private instance variable in our .cpp files to avoid any undeclared variable errors as mentioned before when making Game a singleton. Go into our InputHandler.cpp file, clear all of it's contents apart from the #include line, leave a line below it and add this line:
InputHandler* InputHandler::instance = 0;
|
InputHandler is now usable as a singleton in other classes anywhere it is included in our program, huzzah!
For our next trick we're going to add a few functions to our InputHandler class which will be used for all handling, not just specific devices like keyboard, mouse or joystick, but all three. These three functions are "init()", "update()" and "clean()"; let's add them to our header file now in public below the Instance getter:
For our next trick we're going to add a few functions to our InputHandler class which will be used for all handling, not just specific devices like keyboard, mouse or joystick, but all three. These three functions are "init()", "update()" and "clean()"; let's add them to our header file now in public below the Instance getter:
#pragma once
#include "SDL.h" class InputHandler { public: static InputHandler* Instance() { if (instance == 0) { instance = new InputHandler(); } return instance; } void init(); void update(); void clean(); private: InputHandler() {} ~InputHandler() {} static InputHandler* instance; }; typedef InputHandler _InputHandler; |
You can see I also added the include line in bold to include SDL.h because we'll be using it in our class and so we should include it now.
Now that we've added those functions to our header file, we can go ahead and implement them in our InputHandler.cpp file:
Now that we've added those functions to our header file, we can go ahead and implement them in our InputHandler.cpp file:
#include "InputHandler.h"
#include "Game.h" InputHandler* InputHandler::instance = 0; void InputHandler::init() { } void InputHandler::update() { SDL_Event event; while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_QUIT: _Game::Instance()->quit(); break; default: break; } } } void InputHandler::clean() { } |
First things first, we want to include our game.h file so we can use our Game singleton to access some public functions of Game. Once we've done this we can add in our three functions, init(), update() and clean(). As you can see, the update function has some contents which you may recognise from the game class itself, but ever so slightly different. In our game class' update function we have the following lines of code checking for events:
SDL_Event event;
while (SDL_PollEvent(&event)) { if (event.type == SDL_QUIT) { quit(); } } |
We'll come back to the game class in a bit, but as you can see it is very similar except in our dedicated InputHandler class we're going to use a switch statement. Instead of having an if statement for every event, having to type if (event.type == "INSERT SDL EVENT HERE"), instead we can have a switch statement, have it check for the value of event.type and then add a condition for every possible event with one line... case "INSERT EVENT HERE": followed by the code you want to execute, and then the line "break;" which means to simply exit the switch statement (in broader circumstances, break; can be used to exit any "tier" of code, like an if statement or an entire function). We'll use this while loop to check for input of all kinds. At the moment we have nothing to initialise and nothing to clean at the end, so let's get down to adding our first input method to handle; keyboard handling!
The first thing we're going to want to do is add a few private attributes to our InputHandler.h file.
The first thing we're going to want to do is add a few private attributes to our InputHandler.h file.
private:
InputHandler() {} ~InputHandler() {} static InputHandler* instance; // Keyboard // const Uint8* keystates; void onKeyDown(SDL_Event* event); void onKeyUp(SDL_Event* event); }; typedef InputHandler _InputHandler; |
Let's first address the line, "// Keyboard //". This is a comment, you can comment out any line using the syntax "//". Comments are ignored by the compiler so you can put anything here, to remind you about something for example. Wherever you put these slashes in the line will be the point on form which it is commented out, anything on the line before that won't be commented out. Let it be noted that using this style of commenting syntax means the entire rest of the line will be commented out no matter what. You can also use "/*" at the beginning of what you want to comment out and then the corresponding "*/" to stop commenting out lines. This works on multiple lines, so if you want to comment out a block of code (for say, debugging purposes) then you put "/*" at the beginning and "*/" at the end. Anyway, on with the actual keyboard related private attribute and functions.
The first private variable is a constant Uint8 pointer variable called keystates; Uint8 is an integer variable with a width of exactly 8 bits, we use an 8 bit integer to store the state of each key on the keyboard (pressed or not) because we only need 8 bits and it serves best to use as little memory as possible. We'll cover more on how we use this variable when we declare the public functions in a moment.
Next we declare two private functions. These are private because they're only going to be called by a public function in our class and we don't want them, as individual functions, to be directly accessible outside the class. We pass a pointer to an SDL_Event which we will use to get information about the input such as the key that is being pressed.
Now we can add our sole public keyboard function:
The first private variable is a constant Uint8 pointer variable called keystates; Uint8 is an integer variable with a width of exactly 8 bits, we use an 8 bit integer to store the state of each key on the keyboard (pressed or not) because we only need 8 bits and it serves best to use as little memory as possible. We'll cover more on how we use this variable when we declare the public functions in a moment.
Next we declare two private functions. These are private because they're only going to be called by a public function in our class and we don't want them, as individual functions, to be directly accessible outside the class. We pass a pointer to an SDL_Event which we will use to get information about the input such as the key that is being pressed.
Now we can add our sole public keyboard function:
public:
static InputHandler* Instance() { if (instance == 0) { instance = new InputHandler(); } return instance; } void init(); void update(); void clean(); // Keyboard // bool isKeyDown(SDL_Scancode key); |
This function will be used as a getter for our private keystates variable to return whether or not a specified key (SDL_Scancode key) is currently being pressed.
The private functions are useful because for example, if we want to run some code when the use releases a key, if we did it by checking if the key state was not pressed then it would repeat the code the entire time it wasn't pressed. However, using the onKeyUp() function we can perform code only once when the key is initially released.
So now let's move into the InputHandler.cpp file and add our keyboard functionality. We don't have anything to initialise for the keyboard so we're just going to focus on the previously created update() function and the functions we declared in the .h file above. Let's start by first setting up our keyboard handling in the update function:
The private functions are useful because for example, if we want to run some code when the use releases a key, if we did it by checking if the key state was not pressed then it would repeat the code the entire time it wasn't pressed. However, using the onKeyUp() function we can perform code only once when the key is initially released.
So now let's move into the InputHandler.cpp file and add our keyboard functionality. We don't have anything to initialise for the keyboard so we're just going to focus on the previously created update() function and the functions we declared in the .h file above. Let's start by first setting up our keyboard handling in the update function:
void InputHandler::update()
{ keystates = SDL_GetKeyboardState(0); SDL_Event event; while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_QUIT: _Game::Instance()->quit(); break; case SDL_KEYDOWN: onKeyDown(&event); break; case SDL_KEYUP: onKeyUp(&event); break; default: break; } } } |
As you can see, the first thing we do here is call the SDL function "SDL_GetKeyboardState(0);" which returns the state for the keys belonging to the primary keyboard plugged into the computer (if for some reason you have more than one you can use more than one...). We store that returned result in the private variable keystates for use elsewhere in the class and we do so every update cycle otherwise if we did it in the init function, it would take the state of every key at the beginning of the program and forever stay the same (i.e. never take input).
Next we add another two cases to our switch statement which handles the occurrence of a key being pressed (SDL_KEYDOWN) and a key being released (SDL_KEYUP), we've called the two functions for each event respectively in these cases, which we will define now. We'll place all our functions after the update function but before the clean function, and we'll add them both together like so:
Next we add another two cases to our switch statement which handles the occurrence of a key being pressed (SDL_KEYDOWN) and a key being released (SDL_KEYUP), we've called the two functions for each event respectively in these cases, which we will define now. We'll place all our functions after the update function but before the clean function, and we'll add them both together like so:
void InputHandler::onKeyDown(SDL_Event* event)
{ } void InputHandler::onKeyUp(SDL_Event* event) { } |
For now we have nothing to put in here but later on we can add code in here to execute when a key is pressed (we'll cover this later to test input).
Next we add the getter function isKeyDown(SDL_Scancode key) to get the state of a defined key (pressed or not pressed). The function will be define as follows below the above two functions:
Next we add the getter function isKeyDown(SDL_Scancode key) to get the state of a defined key (pressed or not pressed). The function will be define as follows below the above two functions:
bool InputHandler::isKeyDown(SDL_Scancode key)
{ if (keystates != 0) { if (keystates[key] == 1) { return true; } else { return false; } } return false; } |
First we check to see if the pointer array holding the states of our keys is equal to something (the keyboard exists and the keystates have been updated) then we can proceed to check the status of the key defined in the function's parameters. We create an if statement that says "if the value of keystates at position key is equal to 1 (pressed) then return true, else return false". We also return false if the keyboard's states haven't been initialised by simply putting a return false; statement at the end which it will not reach if it does exist. Once again, this is just a simple method to get the state of a key defined by the function's parameters.
Testing Phase 1
Now we're going to start using InputHandler outside of itself (whoa...), more specifically in our Game class, to initialise it (no initialisation content yet but there will be) and update it so we can get some input and test what we've just done. First thing's first while we're still in the InputHandler.cpp file, update the onKeyDown() and onKeyUp() functions to print to the console if it receives input:
Testing Phase 1
Now we're going to start using InputHandler outside of itself (whoa...), more specifically in our Game class, to initialise it (no initialisation content yet but there will be) and update it so we can get some input and test what we've just done. First thing's first while we're still in the InputHandler.cpp file, update the onKeyDown() and onKeyUp() functions to print to the console if it receives input:
void InputHandler::onKeyDown(SDL_Event* event)
{ std::cout << "Key Pressed: " << SDL_GetKeyName(event->key.keysym.sym) << std::endl; } void InputHandler::onKeyUp(SDL_Event* event) { std::cout << "Key Released: " << SDL_GetKeyName(event->key.keysym.sym) << std::endl; } |
Let's analyse the first line to see what's going on; firstly you can see that we're printing to the console using "std::cout" (if you are getting errors on that then you need to "#include <iostream>"). We print the words "Key Pressed: " first of all so we understand the purpose of the output and then you can see this strange function you've never seen before (ooOOOOOooooooo):
SDL_GetKeyName(event->key.keysym.sym)
Now, you're probably wondering "why all the way into the key of the event, why not just pass event->key into the function?" and that's because you can do that if you wish but it will return the key's ID number not the name of the key (like "W" or "Return"). Now, event->key.keysym.sym returns the name of the key and so we will use this in testing to see which key is being pressed and see that the program is indeed picking it up.
To have this work we need to now go into our Game.cpp file and implement our Input Handler. First things first we need to initialise it so go into the init function in the Game.cpp file and add the following line before the line "running = true;":
SDL_GetKeyName(event->key.keysym.sym)
Now, you're probably wondering "why all the way into the key of the event, why not just pass event->key into the function?" and that's because you can do that if you wish but it will return the key's ID number not the name of the key (like "W" or "Return"). Now, event->key.keysym.sym returns the name of the key and so we will use this in testing to see which key is being pressed and see that the program is indeed picking it up.
To have this work we need to now go into our Game.cpp file and implement our Input Handler. First things first we need to initialise it so go into the init function in the Game.cpp file and add the following line before the line "running = true;":
_InputHandler::Instance()->init();
|
Once we've done that we need to edit the Game class' handleEvents() function, start by emptying it so you just have the function itself with no content and the add the following line like so:
void Game::handleEvents()
{ _InputHandler::Instance()->update(); } |
Now we're ready to test so go ahead and run this you should see the output to the console something like this if you press and release a few keys, so go ahead, hammer some gibberish into it:
So, that's our keyboard handling done with if that was successful (if it wan't, and you know me, get in touch I may be able to help). Let's move onto handling mouse input!
We start a little differently to the way we did with the keyboard, private functions and attributes will follow soon but first, above the class but below the include, go ahead and add the following to the InputHandler.h file:
We start a little differently to the way we did with the keyboard, private functions and attributes will follow soon but first, above the class but below the include, go ahead and add the following to the InputHandler.h file:
enum mouse_buttons
{ LEFT = 0, MIDDLE = 1, RIGHT = 2 }; |
This is an enum type or an enumerated type; in basic terms it creates a new variable type (in this case mouse_buttons) and you then declare the different values of said type seperated by commas (we make these values equal to an integer in this case so we can compare them with the input data we get later, but in general enum values don't need to be equal to anything and can be used purely for comparison or selection). We'll be using this enum type to enable us to use the names "LEFT", "MIDDLE" and "RIGHT" instead of 0, 1 and 2 so that it's more easily identifiable when we're coding, or looking back over code, to tell which button we are calling for.
Now, on with the private functions and attributes, add the following to the private section of the InputHandler.h file below the keyboard content:
Now, on with the private functions and attributes, add the following to the private section of the InputHandler.h file below the keyboard content:
// Mouse //
std::vector<bool> mouseButtonStates; Vector2D* mPos; void onMouseMove(SDL_Event& e); void onMouseButtonDown(SDL_Event& e); void onMouseButtonUp(SDL_Event& e); |
Like with the keyboard, we've added a comment "// Mouse //" to distinguish the mouse related content from the other content.
First thing you see is an std::vector (add "#include <vector>" to the includes at the top if this brings an error and doesn't show up in the intelli sense, which is a form of array in c++ which has a value, a position and many functions with which to act on it with. It is of type "bool" and so will hold only true or false values, and we have named it mouseButtonStates, so seeing as a mouse has only 3 buttons then this vector will be 3 large in size (not that it matters, c++ vectors aren't fixed length arrays).
The next value, Vector2D* mPos, is a pointer variable, but it should bring up an error for you (I'd be surprised if it doesn't, but even if it doesn't still follow these steps). To get rid of this error, we need to create and include the Vector2D.h file for our project, so go ahead and create the empty .h file named Vector2D and go into it, and you're going to want to copy this code into it (don't worry too much about how it all works just yet):
First thing you see is an std::vector (add "#include <vector>" to the includes at the top if this brings an error and doesn't show up in the intelli sense, which is a form of array in c++ which has a value, a position and many functions with which to act on it with. It is of type "bool" and so will hold only true or false values, and we have named it mouseButtonStates, so seeing as a mouse has only 3 buttons then this vector will be 3 large in size (not that it matters, c++ vectors aren't fixed length arrays).
The next value, Vector2D* mPos, is a pointer variable, but it should bring up an error for you (I'd be surprised if it doesn't, but even if it doesn't still follow these steps). To get rid of this error, we need to create and include the Vector2D.h file for our project, so go ahead and create the empty .h file named Vector2D and go into it, and you're going to want to copy this code into it (don't worry too much about how it all works just yet):
#pragma once
#include <math.h> class Vector2D { public: Vector2D(float X, float Y) : x(X), y(Y) {} float getX() { return x; } float getY() { return y; } void setX(float X) { x = X; } void setY(float Y) { y = Y; } float length() { return sqrt(x * x + y * y); } Vector2D operator+(const Vector2D& v2) const { return Vector2D(x + v2.x, y + v2.y); } friend Vector2D& operator+=(Vector2D& v1, const Vector2D& v2) { v1.x += v2.x; v1.y += v2.y; return v1; } Vector2D operator*(float scalar) { return Vector2D(x * scalar, y * scalar); } Vector2D& operator*=(float scalar) { x *= scalar; y *= scalar; return *this; } Vector2D operator-(const Vector2D& v2) const { return Vector2D(x - v2.x, y - v2.y); } friend Vector2D& operator-=(Vector2D& v1, const Vector2D& v2) { v1.x -= v2.x; v1.y -= v2.y; return v1; } Vector2D operator/(float scalar) { return Vector2D(x / scalar, y / scalar); } Vector2D& operator/=(float scalar) { x /= scalar; y /= scalar; return *this; } void normalise() { float l = length(); if (l > 0) { (*this) *= 1 / 1; } } private: float x, y; }; |
This is basically a class which we will use to store and manipulate Vector variables. Unlike the aforementioned std::vector this vector is the maths version of a vector, a point more or less with an x and a y value; all this class does is hold both the variables x and y but also defines some operators which allow and dictate how our Vector2Ds can be acted upon, like adding Vectors together and multiplying them by a scalar (whole number). That's all you need to know for now.
Now the next part of the private section for the Mouse input is about declaring private functions. These functions are used to run code when certain mouse events happen (in the event loop switch statement, we'll get to that soon).
Next up we need to add our public functions to handle mouse input, add this below the keyboard section in the public section of the InputHandler.h file:
Now the next part of the private section for the Mouse input is about declaring private functions. These functions are used to run code when certain mouse events happen (in the event loop switch statement, we'll get to that soon).
Next up we need to add our public functions to handle mouse input, add this below the keyboard section in the public section of the InputHandler.h file:
bool getMouseButtonState(int buttonNum)
{ return mouseButtonStates[buttonNum]; } Vector2D* getMousePos() { return mPos; } void reset(); |
Here we have three functions, two of which are delared and instantiated in the .h file meaning we can leave them out of the .cpp file when we come to it. The first function gets the current state of a specified mouse button. Here we can pass our enum type in the parameters when using it to get the state of each key (e.g. getMouseButtonState(LEFT)).
The second function gets the vector position of the cursor in game; this is just a simple getter function, you've encountered them plenty.
The third will be defined in the .cpp file and is used to reset all mouse values.
Now, let us move into InputHandler.cpp and deal with our non-instantiated functions and such.
First and foremost there are things we have to initiate in the init function. Add the following code to the init function like so:
The second function gets the vector position of the cursor in game; this is just a simple getter function, you've encountered them plenty.
The third will be defined in the .cpp file and is used to reset all mouse values.
Now, let us move into InputHandler.cpp and deal with our non-instantiated functions and such.
First and foremost there are things we have to initiate in the init function. Add the following code to the init function like so:
void InputHandler::init()
{ for (int i = 0; i < 3; i++) { mouseButtonStates.push_back(false); } mPos = new Vector2D(0, 0); } |
All this does is loop three times (the amount of buttons we're using on the mouse) and adding a false value to the vector array we declared in the header file. In the end once the loop is finished we will have a vector with three false values meaning that the program thinks all the mouse buttons are in the not pressed state.
We also initialise the mouse position variable so that it is not equal to NULL, avoiding errors down the line.
Next we move into the update function and add the cases for our mouse much like we did with the keyboard. We have three of these event cases; SDL_MOUSEMOTION, SDL_MOUSEBUTTONDOWN and SDL_MOUSEBUTTONUP. So let's go ahead and add these cases, calling the relevant private functions from our header file and then breaking from the switch statement to start the loop again:
We also initialise the mouse position variable so that it is not equal to NULL, avoiding errors down the line.
Next we move into the update function and add the cases for our mouse much like we did with the keyboard. We have three of these event cases; SDL_MOUSEMOTION, SDL_MOUSEBUTTONDOWN and SDL_MOUSEBUTTONUP. So let's go ahead and add these cases, calling the relevant private functions from our header file and then breaking from the switch statement to start the loop again:
switch (event.type)
{ case SDL_QUIT: _Game::Instance()->quit(); break; case SDL_KEYDOWN: onKeyDown(&event); break; case SDL_KEYUP: onKeyUp(&event); break; case SDL_MOUSEMOTION: onMouseMove(event); break; case SDL_MOUSEBUTTONDOWN: onMouseButtonDown(event); break; case SDL_MOUSEBUTTONUP: onMouseButtonUp(event); break; default: break; } |
Now that we've added these (there maybe some errors but we'll clear them up in a moment) we can instantiate the functions from our header file. First up are the three private functions; add these after the update function but before the keyboard functions:
void InputHandler::onMouseButtonDown(SDL_Event& e)
{ if (e.button.button == SDL_BUTTON_LEFT) { mouseButtonStates[LEFT] = true; } else if (e.button.button = SDL_BUTTON_MIDDLE) { mouseButtonStates[MIDDLE] = true; } else if (e.button.button = SDL_BUTTON_RIGHT) { mouseButtonStates[RIGHT] = true; } } void InputHandler::onMouseButtonUp(SDL_Event& e) { if (e.button.button == SDL_BUTTON_LEFT) { mouseButtonStates[LEFT] = false; } else if (e.button.button == SDL_BUTTON_MIDDLE) { mouseButtonStates[MIDDLE] = false; } else if (e.button.button == SDL_BUTTON_RIGHT) { mouseButtonStates[RIGHT] = false; } } void InputHandler::onMouseMove(SDL_Event& e) { mPos->setX(e.motion.x); mPos->setY(e.motion.y); } void InputHandler::reset() { for (int i = 0; i < mouseButtonStates.size(); i++) { mouseButtonStates[i] = false; } } |
Let's analyse this code function by function, starting with onMouseDown().
In onMouseButtonDown() there are a few if statements which are checking to see which mouse button is being pressed in this certain event. It checks every possible button and if it is pressed then it stores a true value at that mouse button's position in the moue button state vector.
Quite simply explained, the second function onMouseButtonUp() does the exact opposite of onMouseButtonDown(). It uses the same if statements but this time if it returns true it mean that that button has been released and so we go ahead and store a false value at that mouse button's position in the mouse button state vector.
The final function, onMouseMove(), is called when the mouse is moved and so for now what we want to do is to tell the program where the mouse is whenever it moves. We do this by using the SDL_Event pointer we put in the parameters to get the position of the mouse and store it in a more conveniently accessible private variable in our InputHandler.
The final function, reset(), simply reset all the button states to false in case we need to.
Now that is done so we have handled both keyboard and mouse input in it's simplest form, but we will revisit and revise this in later tutorials and you will see why when we cross that hurdle, for now you're going to want to test that mouse input.
Testing Phase 2
To test the mouse function we're going to edit the above three functions slightly. In the onMouseButtonDown() function, in each of the if statements, add this line, changing which mouse button it is depending on the if statement:
std::cout << "Left Mouse Button Pressed" << std::endl;
You can also do the same in the onMouseButtonUp() function except change "pressed" for "released".
To test the mouse movement we're going to want to add the following line in the onMouseMove() function after the two pre-existing lines from before:
std::cout << "Mouse position = x: " << mPos->getX() << ", y:" << mPos->getY() << std::endl;
You can probably tell what this does by now, but explanation incoming anyway. Using the Vector2D we assigned to keep track of the mouse position we get the x and the y when the mouse is moved and print it to the screen, with a few extra words to make it look nicer and easier to read. Now that we've done that we can run it again, click all our mouse buttons and move the mouse about a bit:
In onMouseButtonDown() there are a few if statements which are checking to see which mouse button is being pressed in this certain event. It checks every possible button and if it is pressed then it stores a true value at that mouse button's position in the moue button state vector.
Quite simply explained, the second function onMouseButtonUp() does the exact opposite of onMouseButtonDown(). It uses the same if statements but this time if it returns true it mean that that button has been released and so we go ahead and store a false value at that mouse button's position in the mouse button state vector.
The final function, onMouseMove(), is called when the mouse is moved and so for now what we want to do is to tell the program where the mouse is whenever it moves. We do this by using the SDL_Event pointer we put in the parameters to get the position of the mouse and store it in a more conveniently accessible private variable in our InputHandler.
The final function, reset(), simply reset all the button states to false in case we need to.
Now that is done so we have handled both keyboard and mouse input in it's simplest form, but we will revisit and revise this in later tutorials and you will see why when we cross that hurdle, for now you're going to want to test that mouse input.
Testing Phase 2
To test the mouse function we're going to edit the above three functions slightly. In the onMouseButtonDown() function, in each of the if statements, add this line, changing which mouse button it is depending on the if statement:
std::cout << "Left Mouse Button Pressed" << std::endl;
You can also do the same in the onMouseButtonUp() function except change "pressed" for "released".
To test the mouse movement we're going to want to add the following line in the onMouseMove() function after the two pre-existing lines from before:
std::cout << "Mouse position = x: " << mPos->getX() << ", y:" << mPos->getY() << std::endl;
You can probably tell what this does by now, but explanation incoming anyway. Using the Vector2D we assigned to keep track of the mouse position we get the x and the y when the mouse is moved and print it to the screen, with a few extra words to make it look nicer and easier to read. Now that we've done that we can run it again, click all our mouse buttons and move the mouse about a bit:
With that done we have finished the InputHandler! We can now move onto the texture manager, learn how to draw images to the screen and create the library in which to store and retrieve them. You'll find that tutorial via the second button below. The first button will take you to the source code for this tutorial:
NOTE: If you cannot find a certain file for the part you're doing, it is because all files are only uploaded when change, look for the latest part version of said file in the pastebin
NOTE: If you cannot find a certain file for the part you're doing, it is because all files are only uploaded when change, look for the latest part version of said file in the pastebin