Lesson 23: Checking for Collisions: the Hit Test
Grab a hot tea, or a cafinated drink, or something... this lesson is going to take quite a bit of time and some serious concentration.
In many games, detecting when objects collide is a key part of the gameplay. For instance, in Pacman, pacman collides with walls, ghosts, dots, and fruit. A common strategy for detecting collisions is to use a hittest() function.
1. Add your own hittest() to the bottom of loop(). You will need to continuously be testing to see if pacman is colliding with anything as he moves around. Note that hittest() must return a value of true or false so that it may be used as the condition of an if statement.
Some programming languages, like ActionScript, have their own hittest() function. Unfortunately, JavaScript does not so you will need to create your own.
2. Create a hittest(). For now, have the function always return a value of true. The variables a and b are used to receive the information (arguments) that are passed to the function in the function call. In this case, a = wall_1 and b = pacman.
Because hittest() always returns true, "collision" is always displayed in the output div.
Your hittest() will be using the left, top, width, and height properties of both elements being tested. You just set these properties for wall_1 in the last lesson. However, for pacman you only set the left and top properties in JavaScript.
3. In the loadComplete() function, set the width and height of pacman to 40px.
4. In the CSS, remove the #pacman styles for width and height.
5. Look over the completed hittest() code. You do not need to copy the code.
If you understand all of this then you are a mathematical genius! Here is a quick overview of what is going on:
In the first 8 lines, variables are used to save the coordinates for the four corners of element a. For instance, the top left corner of a is at (aX1, aY1).
In the second 8 lines, variables are used to save the coordinates for the four corners of element b.
The variable hOverlap is used to indicate whether or not the two elements are taking up the same space horizontally. The value of hOverlap is initally true, assuming that the elements are in the same horizontal space. If the top two points of object A are to the left of object B, or to the right of object B, than hOverlap is changed to false. Similarly, the value of vOverlap is determined to be true or false, indicating whether the two objects are taking up the same vertical space.
If the two objects are overlapping both vertically and horizontally, then the objects are colliding, and the function returns true. Otherwise there is no collison, and the function returns false.
The hittest() function here is not unique to pacman. This same function can be used in any game that requires a Hit Test. Therefore, it makes sense to put this function in a separate JavaScript file that can be used in other games as well.
6. Right-click on myGameLibrary.js and save the file in the same location as your pacman files.
Important note: you cannot have two functions with the same name. Since you already have a hittest() in the myGameLibrary.js, you should delete the hittest() function that is in your pacman.js, if you have not already done so.
7. Add a link to myGameLibrary.js in your HTML, just as you linked to pacman.js.
Now the output div should display "collision" only when pacman and wall_1 are actually colliding.
To stop pacman from going through wall_1, you can just move pacman back to his previous position anytime a hit is detected.
8. At the top of loop(), declare originalLeft and originalTop and set them equal to pacman's left and top values. Then modify the hittest() conditional statement so that, if the elements are hitting after pacman moves, pacman is reset to his original position.
CONGRATULATIONS! You should now have the hittest working to prevent pacman from running through the wall.
While holding down arrow keys to move the player character (PC) will work well in many games, that is not really how pacman moves. Pacman continues moving forward until he runs into something, regardless of wheter or not an arrow key is being pressed. To do this, you will need to base movement on the direction pacman is facing instead of what arrow keys are being pressed.
9. Comment out or delete the variables that track which arrow keys are being pressed, and declare direction as a global variable to keep track of pacman's current direction. Because pacman initially is facing to the right, the initial value of direction should be 'right'.
10. Comment out or delete the 'keyup' event listener.
11. In the 'keydown' event listener, replace the assignments for the variables for tracking which arrow keys are being pressed with the appropriate assignments for direction.
12. In loop(), modify the conditions in the conditional statement to check the value of direction.
This should get pacman moving continuously even when no arrow key is being pressed. Try it out.
The only remaining issue with pacman's motion, which would be more apparent if there were more walls, is that pacman should not be able to turn into a wall and stop when he is directly along side of it.
One way to solve this problem is to temporarily move pacman in the direction an arrow key is being pressed to determine whether he would hit a wall or not. Only if this does not cause pacman to hit a wall, then the direction and orientation could be changed. In either case, pacman would move back to his original position because the temporary movement was only intended to determine if pacman could change direction or not. The code for actually moving pacman is based on the current direction, but takes place in the main Game Loop.
13. Update the 'keydown' event listener.
Declare originalLeft and originalTop and set them equal to pacman's left and top values. At the bottom of the 'keydown' event listener, change pacman's left and top back to these original values. This will allow you to move pacman anywhere for testing purposes, knowing he will be reset to his original position.
Add a line of code in each if block to move pacman in the direction of the arrow key being pressed. Then add an embedded if statement to change pacman's direction and orientation only if no collision is detected. Because the exclamation point in JavaScript means not,
if( ! hittest(wall_1, pacman) ){
means "if wall_1 and pacman are not hitting each other, then..."
Now pacman should not be able to turn into a wall when he is moving directly along side it. This will work very nicely once we have many walls... which happens to be what we will be doing in the next lesson!
One last note, the hittest() in this lesson works wonderfully for detecting collisions between rectangularly shaped elements. You may need to create other hittest() functions to use for elements with non-rectangular shapes. For instance, if you were testing two circular objects to see if they are hitting, you may want to check the distance between their center points with their radius. The complexity never ends!