[ LiB ] |
You use the keyboard every time you use your computer; heck, I am using it to type these words right now. So, of course, the keyboard is probably going to be a common source of input for most games you play. We better get crackin' if we want to figure out what the player wants to do.
We have gone over the keyboard in a very limited way thus far: basically, you know how to check if the Esc key and a few select others have been pressed. We are going to review what we know and then learn a bit more. So, let's begin by reviewing the functions KeyDown() and KeyHit() .
We have been using this function throughout the book, so you most likely already know what it does. First off, let's go over the declaration of KeyDown() .
KeyDown (scancode)
Memories, huh? Anyway, KeyDown() tests the keyboard to see if the scan code has been pressed. (If you flip to Appendix A, you will see a full list of scan codes. By the way, I might use the word "key code" every once in a while. Key code is just a synonym for scan code they mean exactly the same thing.)
If the key has been pressed, KeyDown() returns 1. If a key was not pressed, KeyDown() returns 0.
So how do we use this? Well, usually we test KeyDown() with an If statement. For example, if we wanted to test if the user presses the spacebar, we would write something like this.
If KeyDown(28) ;Do Something Endif
Let's go through this code in depth. As you know, the If statement performs the following actions if what it tests amounts to true. If you remember, in computer speak, 1 is equal to true, and 0 is equal to false. KeyDown() returns 1 if the key specified by its scan code is pressed on the keyboard. Therefore, the statements inside the If Endif block are executed if and only if the spacebar has been pressed.
Let's write a program around this. The following program, demo10-01.bb, moves an outer-space background when the up, down, left, and right keys are pressed.
;demo10-01.bb - Demonstrates usage of KeyDown() ;Initialize Graphics Graphics 800,600 ;Load the background image backgroundimage = LoadImage("stars.bmp") ;CONSTANTS ;The following constants are used for testing key presses Const ESCKEY = 1, UPKEY = 200, LEFTKEY = 203, RIGHTKEY = 205, DOWNKEY = 208 ;scrollx and scrolly define how much the image should be moved scrollx = 0 scrolly = 0 ;MAIN LOOP While Not KeyDown(ESCKEY) ;If the player hits up, we will scroll the background up If KeyDown(UPKEY) scrolly = scrolly - 5 ;scroll background 5 pixels up EndIf ;End of UPKEY test ;If the player hits left, we will scroll the background left If KeyDown(LEFTKEY) scrollx = scrollx - 5 ;scroll background 5 pixels left EndIf ;End LEFTKEY test ;If player hits right, we will scroll the background right If KeyDown(RIGHTKEY) scrollx = scrollx + 5 ;scroll background 5 pixels right EndIf ;End RIGHTKEY test ;If player hits down, we will scroll the background down If KeyDown(DOWNKEY) scrolly = scrolly + 5 ;Scroll background 5 pixels down EndIf ;End of DOWNKEY test ;Tile the background image on the screen so it looks like actual outer space TileBlock backgroundimage,scrollx,scrolly ;Wait a fraction of a second. Delay 35 Wend ;END OF MAIN LOOP
This program demonstrates the concepts of KeyDown() quite well. Let's begin at the top. I created a section of constants that define the keys that will be used throughout the program. Constants, as you probably remember, are variables whose values cannot be changed; thus, they are perfectly suited to hold the scan code numbers . Because the scan codes of each key never change, you should always create constants for your keys. Believe me, they will help you in many ways. For one, you will know, just by looking at your constants section, which keys are used throughout your program. For two, the following code:
If KeyDown(DOWNKEY) ;Move Player Down Endif
is a lot easier to understand than this
If KeyDown(208) ;Move Player Down Endif
Also, you won't have to memorize the scan codes for each key used in your program. Listen carefully : a good rule to live your life by is that if there is a way to make something easier, do it. Work is hard, and memorization is work. Using constants allows you to forget about the individual code and just remember the key that you are testing. Anyway, back to the code. We move on to the main loop, and as you can see, the main loop only functions as long as the Esc key is not pressed, as seen by this line of code.
While Not KeyDown(ESCKEY)
Like the If statement, While only functions as long as the following statements are true, or equal to 1. Since KeyDown() returns 0 unless the key is pressed, and Not flips 0 into 1 and 1 into 0, Not KeyDown(ESCKEY) is 1 (true) as long as the key is not being pressed. Therefore, the main loop executes only as long as the Esc key is not pressed.
We then move into the actual key tests. Following is the test for the up key.
;If the player hits up, we will scroll the background up If KeyDown(UPKEY) scrolly = scrolly - 5 ;scroll background 5 pixels up EndIf ;End of UPKEY test
Here, the statements execute as long as UPKEY is pressed. The statements change the value of the scrolly variable, and the background scrolls up a little.
The previous test is repeated 3 more times to test all four arrow keys: up, down, left, and right. Notice how when the map scrolls left, it seems like you are moving right, and vice versa. The same happens when you scroll up. It's a cool effect, don't you think?
Okay, I think you get the gist of that. However, I want to go over one problem with using KeyDown() . Sometimes when you type something on your keyboard, KeyDown() believes that you held the key down for longer than one frame. This happens because the game loop iterates extremely fast, and you may be holding the key down for more than one frame at a time. Of course, this is what you want to happen on some games, especially with movement. When you are performing an action like moving a spaceship around the screen, you want the player to be able to simply hold down the key to move the character around. However, every once in a while, you will have a case where you don't want the keys to be able to be held down for more than one frame.
Take this, for example: when you are creating a game, you usually want the player to be able to quit the game by pressing Esc. Now, maybe you want to show something on the screen before the game actually closes , so you print "Press any key to exit" on the screen. The program then waits for a keypress by using the function WaitKey , which pauses the program until a key is pressed. WaitKey has no parameters; it just stops a program's execution. Here is the problem, though: when the player presses Esc, the key is carried over to the WaitKey statement and the program exits immediately.
You have to find a way to halt the program from retrieving the key immediately. There is one easy way to do this.
What we need to do is clear the computer's memory of what keys have been pressed. This will cause any previously held down keys to be forgotten about by the computer. To perform this action, we use the function FlushKeys . FlushKeys 's declaration is extremely simple:
FlushKeys
There are no parametersjust call the function by itself. Anyway, by calling FlushKeys , you clear the key input memory. Thus, any key that was held down previously is deleted.
Let's see the difference in a sample program. The following demo, demo10-02.bb, demonstrates what will happen when you don't use FlushKeys .
;demo10-02.bb - Demonstrates problem with not using FlushKeys Graphics 800,600 ;Create the background image that will be scrolled backgroundimage = LoadImage ("stars.bmp") ;Create variable that determines how much background has scrolled scrolly = 0 ;MAIN LOOP While Not KeyDown(1) ;Scroll background a bit by incrementing scrolly scrolly = scrolly + 1 ;Tile the background TileBlock backgroundimage,0,scrolly ;Reset scrolly if the number grows too large If scrolly > ImageHeight(backgroundimage) scrolly = 0 EndIf ;Print necessary text Locate 0,0 ;Locate text to top left corner of screen Print "When you want to quit, press Esc." Print "Hopefully, a message stating 'Press any key to exit' will appear after you hit Esc." ;Delay the program for a fraction of a second Delay 25 Wend ;END OF MAIN LOOP Print "Press any key to exit" ;Wait for player to press a key WaitKey
Figure 10.1 is a screenshot taken from the program.
NOTE
If you decide to open up the program code in your Blitz Basic compiler and go to Program->Run Program, you will notice a peculiar occurrence. Unlike what we have been talking about here, the statement "Press any key to exit" will be shown on the screen. This only happens because of the dialog box that pops up when you run the program out of your compiler. If you want to see what would happen if you compiled the program using the full version of Blitz Basic, add the command End directly after WaitKey .
Looks good, huh? However, try running the executable file, demo10-02.exe, from the CD. Notice the problem? When the player presses Esc, the game exits immediately. This, obviously, is not what we want.
Now, you know that to fix this problem, we need to use FlushKeys . demo10-03.bb uses FlushKeys beautifully. The main section that has been changed takes place directly after the main loop. Following is the source of the changed code from demo10-03.bb.
;Flush the memory that holds the previous keypresses FlushKeys Print "Press any key to exit" ;Wait for player to press a key WaitKey
Figure 10.2 is a screenshot taken from the program.
As you can see, the one major change is the addition of the FlushKeys function. This statement clears the memory and allows the Print statement following it to be shown.
Let's move on to the next keyboard input function: KeyHit() .
This is the last function that we will be going over for keyboard input. KeyHit() acts an awful lot like KeyDown() , except for a small but important difference. Whereas KeyDown() allows the player to hold down a key, KeyHit() only lets the player strike the keyboard. Thus, you can only read which key the player hit one time. Take, for example, demo 10-04.bb. This program draws a spaceship on a tiled space background. It allows the player to move the spaceship using KeyHit() .
In demo10-04.bb, you will find the KeyHit() command nested in If statements. Following is the source from the program that uses KeyHit() .
;If the player hits up, move player up If KeyHit(UPKEY) y = y - 5 ;move player 5 pixels up EndIf ;If the player hits left, move player left If KeyHit(LEFTKEY) x = x - 5 ;move player 5 pixels left EndIf ;If player hits right, move player right If KeyHit(RIGHTKEY) x = x + 5 ;move player 5 pixels right EndIf ;If player hits down, move player down If KeyHit(DOWNKEY) y = y + 5 ;move player 5 pixels down EndIf
By the way, KeyHit() 's declaration is exactly the same as KeyDown 's declaration.
KeyHit (scancode)
If you run the program, you will notice that you can only move the player by pressing the arrow keys multiple times. Usually, you would rather allow the player to move around by holding down the arrow keys, but sometimes you may prefer to only let the player do something by striking the key over and over again.
Let's take a space simulation game, for example. We want to allow the player to be able to move around the screen and fire bullets. To do this, we will allow the player to hold down the arrow keys for movement, but he will have to strike the spacebar to produce a bullet.
Following is the initialization section from demo10-05.bb.
;demo10-05.bb - A Space Simulation with KeyHit() Graphics 800,600 ;Set automidhandle to true AutoMidHandle True ;Set up Backbuffer SetBuffer BackBuffer() ;TYPES ;Bullet type = holds the information for each bullet Type bullet Field x,y ;the coordinates of the bullet End Type ;Player type - holds the actual player Type player Field x,y ;the coordinates of the player End Type ;Create player and initialize field Global player.player = New player player\x = 400 player\y = 500 ;CONSTANTS ;The following constants are used for testing key presses Const ESCKEY = 1, UPKEY = 200, LEFTKEY = 203, RIGHTKEY = 205, DOWNKEY = 208, SPACEBAR = 57 ;IMAGES playerimage = LoadImage("ship.bmp") Global bulletimage = LoadImage("bullet.bmp") backgroundimage = LoadImage("stars.bmp") ;Create a scrolling indicator variable scrolly = 0
The initialization section acts pretty much how you would expect. It begins by setting the graphics mode and setting AutoMidHandle to True . After that, it sets the starting buffer to be the back buffer. Next, it creates the types that are used in the program.
The first type is the bullet type. Every bullet that is to be created uses this type. The next type is the player type. Both bullet and player have the same fields: x and y. As you probably have guessed, x and y define the coordinates for the bullet and player's coordinate positions .
After creating the types, the program initializes the player type. Of course, there is only one player, so a single player is created. The player's beginning x and y coordinates are defined at (400,500), which starts the player roughly in the middle of the screen near the bottom.
The next two sections define the constants and the images. The constants are the scan codes for each of the keys that are used in the game. The program loads three images: playerimage , bulletimage , and backgroundimage . Notice that bulletimage is global, implying that it is used in other functions than the main function.
The final section of the initialization creates scrolly . This indicator variable defines how far the background should scroll at any instant.
Next up, we move on to the main loop.
;MAIN LOOP While Not KeyDown(ESCKEY) ;Increment scrolling variable scrolly = scrolly + 1 ;Tile the background TileBlock backgroundimage,0,scrolly ;Reset the scrolling variable when it grows too large If scrolly > ImageHeight(backgroundimage) scrolly = 0 EndIf ;Test input keys TestKeys() ;Update (move) each bullet UpdateBullets() ;Draw the player DrawImage playerimage, player\x, player\y ;Flip the front and back buffers Flip Wend ;END OF MAIN LOOP
The main loop begins by tiling the background. It increments the indicator variable, scrolly , and then tiles the background. When scrolly grows too large, its value is reset to 0. Following that, the program calls two user-defined functions: TestKeys() and UpdateBullets() . The first function tests the keyboard to see if any input has occurred, and the second function moves and updates each bullet on the screen.
The main loop ends by drawing the player's ship and his current position. It then flips the front and back buffers using the command Flip .
The rest of the program lists the two user-defined functions: TestKeys() and UpdateBullets() . Following is the source for TestKeys() .
;FUNCTIONS ;Function TestKeys() - Tests what buttons have been pressed by player Function TestKeys() ;If the player hits up, we move him 5 pixels up If KeyDown(UPKEY) player\y = player\y - 5 ;move player 5 pixels up EndIf ;If the player hits left, we move him 5 pixels left If KeyDown(LEFTKEY) player\x = player\x - 5 ;move player 5 pixels left EndIf ;If player hits right, we move him 5 pixels right If KeyDown(RIGHTKEY) player\x = player\x + 5 ;move player 5 pixels right EndIf ;If player hits down, we move him 5 pixels down If KeyDown(DOWNKEY) player\y = player\y + 5 ;move player 5 pixels down EndIf ;If player hits spacebar, we will create a new bullet at the player's current position If KeyHit(SPACEBAR) bullet.bullet = New bullet bullet\x = player\x bullet\y = player\y EndIf End Function
The TestKeys() function, while not short, is pretty easy. The function tests each key to see if it has been pressed, and if so, it changes something in the program. Table 10.1 explains what each key does when pressed.
Key | Function |
---|---|
Up arrow | Moves the player 5 pixels up. |
Left arrow | Moves the player 5 pixels left. |
Right arrow | Moves the player 5 pixels right. |
Down arrow | Moves the player 5 pixels down. |
Spacebar | Creates a new bullet to be drawn onscreen. |
As you can see, the arrow keys do pretty much what you expect them to. The only new key is the spacebar.
When the player hits the spacebar, a new bullet is created. The code that performs this action follows .
If KeyHit(SPACEBAR) bullet.bullet = New bullet
Notice that the function uses KeyHit() instead of KeyDown() for the creation of new bullets. This prevents the player from holding down the spacebar and creating hundreds of bullets quickly. See Figure 10.3 to see what happens if you exchange KeyDown() with KeyHit() .
By creating a new bullet, the program adds a new bullet to the bullet type. If you remember, when creating multiple members of the same type, the most recent one becomes active. Thus, the following lines
bullet\x = player\x bullet\y = player\y
only relate to the most recent bullet (the one that was just created a few milliseconds earlier). The new bullet is created at the player's current position.
Okay, the next and final function updates each bullet. Following is the source for
UpdateBullets(). ;Function UpdateBullets() - Moves each bullet on screen Function UpdateBullets() ;For every bullet, move it up 5 pixels. If it goes offscreen, delete it, otherwise, draw it For bullet.bullet = Each bullet bullet\y = bullet\y - 5 ;Move bullet up ;If bullet moves offscreen, delete it, otherwise, draw it onscreen If bullet\y < 0 Delete bullet Else DrawImage bulletimage, bullet\x, bullet\y ;Draw the bullet EndIf Next ;move to next bullet End Function
The function begins with a For Each loop that tests every created bullet. The function moves each existing bullet 5 pixels up. The program then tests to see if the bullet is off-screen . If so, the bullet is deleted. If not, the bullet is drawn onscreen.
The function ends by moving on to the next bullet, and returning to the main function after every bullet has been processed .
Well that is it for demo10-05.bb. The screenshot in Figure 10.4 was taken from that program.
By the way, try changing KeyHit() into KeyDown() on the TestKeys() function. Seriously, it can provide hours of funespecially for those who like shiny and fast-moving objects. (Like me!)
Before we move mouse input, I want to explain one thing about KeyHit() . KeyHit() does provide a return value. The function returns the number of times a key has been hit since the previous KeyHit() call (or since the beginning of the program if there are no earlier KeyHit() calls). The following demo, demo10-06.bb, demonstrates what it can do.
;demo10-06.bb - Demonstrates the return value of KeyHit() ;Set up graphics so that you can read all of the text, make it windowed Graphics 800,600,0,2 ;Begin introductory text Print "You know what's cool? Game Programming." Print "Although Maneesh ain't that uncool, either." ;Print blank line Print "" ;Continue text Print "Anyway, press Esc as many times as you can in the Next 5 seconds." Print "At the end of the program, the number of times will be printed." ;Allow the player 5 seconds to hit Esc as many times as possible Delay 5000 ;Set numberofhits equal to the amount of times Esc was hit (return value of KeyHit(1) ) numberofhits = KeyHit(1) ;Print the number of times Esc was hit Print "Esc was hit " + numberofhits + " times." Print "You gotta love KeyHit(), huh?" ;Hold on a sec so the player can see the final text Delay 1000 ;Make sure the key input memory is cleared by calling FlushKeys FlushKeys ;Wait for user to press a key before exiting Print "Press any key to Esc." WaitKey
The main part of the program sets numberofhits to KeyHit(1) the number of times Esc was hit since the beginning of the program.
Well, that is all for keyboard input. Now, we move on to mouse input.
[ LiB ] |