Real Time User Input and Advanced Timing
Prior to actually getting into this, you should know the basics of doing “animation” and timing as shown in the previous tutorial.
Getting our program loop going
So let’s go ahead and get ourselves a simple program loop going, inside our main function, lets put this:
//Create our circle Circle circ = Circle(Point(1, 5), 1, BLUE, true); //Draw the first frame win << circ; win.output_buffer(); while(true) { //Do a basic pause to attempt to get 60 FPS wait_for(16); //Update everything in our program //Draw everything in our program win.clear_buffer(); win << circ; win.output_buffer(); }
So we have ourselves a running program loop that draws blue circle for us with a simple pause to have our program run at about 60 FPS (1000ms / 16ms is about 60).
Getting Input from the User
Now that we have a little program going with a stationary ball, let’s move the ball using real time input from the user. We are going to use the function “bool win.is_key_down(string s);“
Example:
- if(win.is_key_down(“a”))
- if(win.is_key_down(“left”))
- bool speed_up = win.is_key_down(“rshift”) || win.is_key_down(“lshift”);
This function will return true if the passed in key is being pressed, and false if it is not. A list of what keys you can check is on the Instinct reference sheet.
Moving the Circle Using Input
Let’s go ahead and use this keyboard checking function to move our circle around when pressing the left and right keys. So let’s go back to our original code and add in two lines that will check if left is pressed, and then move the circle left if so.
//Create our circle Circle circ = Circle(Point(1, 5), 1, BLUE, true); //Draw the first frame win << circ; win.output_buffer(); while(true) { //Do a basic pause to attempt to get 60 FPS wait_for(16); //Update everything in our program if(win.is_key_down("left")) circ.move(-0.1, 0);
//Draw everything in our program win.clear_buffer(); win << circ; win.output_buffer(); }
This code now will move our circle -0.1 in the x every time the “left” key is pressed. We can change how fast the circle moves by just changing the value -0.1 to a larger or smaller number. We can continue to write the rest of the directions the circle can move by just adding more if statements like so:
//Update everything in our program if(win.is_key_down("left")) circ.move(-0.1, 0); if(win.is_key_down("right")) circ.move(0.1, 0); if(win.is_key_down("up")) circ.move(0, 0.1); if(win.is_key_down("down")) circ.move(0, -0.1);
Now we have code that will move the circle in all four directions depending on what is pressed. Now you will notice we did not use an else if; this was intentional. The reason we used ifs was that we may want to execute different moves in the same frame, for example if I want to move diagonally. Then I can move the circle left, and up in the same frame creating diagonal movement. These is just the basics of what you can do using real time input, what your program does is entirely up to you.
Other Forms of Real Time User Input
Besides getting if a key on the keyboard is being pressed, we can also get information about the mouse. There are five functions we can use for seeing how the mouse is being used:
- Point win.get_mouse_loc();
- double win.get_mouse_x();
- double win.get_mouse_y();
- bool win.left_mouse_down();
- bool win.right_mouse_down();
The first three functions help us figure out where the mouse is in our programs coordinate system. win.get_mouse_loc() will give us a Point object that holds where in the coordinate system the mouse is. win.get_mouse_x() and win.get_mouse_y() are just helping functions that get the x and y locations of the mouse. The last two functions tell us whether or not the left or right buttons on the mouse are being pressed. win.left_mouse_down() will return true if the user is pressing the left mouse button and win.right_mouse_down() will return true if the user is pressing the right mouse button. These functions coupled with the keyboard functions should give you all the info you need about what the user is doing to create very interactive programs and games.
Advanced Timing
As you saw above, we were using the most simplest form of timing control by just calling a wait_for(16); every time. This does allow us to create some kind of uniform timing across computers, however, it assumes the rest of the code we are executing takes 0 milliseconds.
You may wonder what exactly I am talking about, well let’s just say that one computer takes 1 millisecond to run the code that moves and draws the circle. Then the entire time it takes for one frame is 1 + 16 = 17 milliseconds. A slower computer may take 5 milliseconds to run the code that moves and draws the circle. Then the entire time it takes for one frame is 5 + 16 = 21 milliseconds. Now you see that the game is still not synced well on varying systems.
So a better way at handling timing (still not the best way but better ways are more complex) is to time how much time it takes the loop of your code to execute and then wait for 16 minus that time. Therefore, if on one computer the loop takes 1 millisecond to run, it will only call a wait_for(15). This would cause the entire frame to be 1 + 15 = 16 milliseconds. If a slower computer takes 5 milliseconds to run the loop, then it will only call a wait_for(11). This would cause the entire frame to be 5 + 11 = 16 milliseconds. Now both computers are synced well. The reason this isn’t perfect is it does not take into account the case if the loop takes more than 16 milliseconds to execute, but that will likely not be the case in simple programs.
So the code that handles this more exact timing will look like this:
Timer loop_time;
loop_time.start();
while(true) { //Attempt to get 60 FPS by waiting for 16 minus the loop execution time wait_for(16 - loop_time.get_time()); loop_time.reset(); //Update everything in our program if(win.is_key_down("left")) circ.move(-0.1, 0);
//Draw everything in our program win.clear_buffer(); win << circ; win.output_buffer(); }
The reason this works better is because the timer will reset back to zero after the wait_for() call. Then the loop will execute and we will eventually loop back to the wait_for() call. Then we can ask the timer how long it took to get back and wait_for 16 minus that time. Then the timer resets back to zero and this cycle continues.