Play the Game Pong, Part I
The PongTimerTask class is used as the timer class for another class that plays the simple video game known as Pong.
The timer class extends the built-in class TimerTask. The base class has an abstract method run() that must be implemented by the class. In turn, the implementation of this method calls the updatePosition() method as long as the applet has focus. The updatePosition() method moves the ball as appropriate after one timer tick.
In the game, a ball moves down the screen toward a paddle that the player controls ("down the screen" means from lower to higher y coordinates). If the player can move the paddle sideways so the ball hits it, the ball bounces back up, possibly with a change in the angle at which it moves. The ball bounces off the edges and top of the applet window until it moves back toward the paddle. If the ball misses the paddle, the ball goes to the bottom of the applet window and the game ends.
Note that Random.nextInt(n) returns a number between 0 (inclusive) and n (exclusive).
The variables' names should be self explanatory, although of course, you should check them. Assume for now that the variables are initialized with reasonable values. The paint() method of the applet is included to provide some clarification. The first two parameters to both fillRect() and fillOval() are the x and y coordinates of the upper-left corner. The second two parameters are the width and height.
In the next example, this function is expanded into a class that plays a complete Pong game.
Source Code
1. import java.util.Timer; 2. import java.util.TimerTask; 3. import java.util.Random; 4. 5. public class Pong extends java.applet.Applet { 6. 7. private int paddleX, paddleY, maxX, maxY; 8. private int paddleWidth, paddleHeight; 9. private int ballX, ballY; 10. private int ballWidth, ballHeight; 11. private int ballMoveX, ballMoveY; 12. private Dimension size; 13. boolean focus; 14. Random random; 15. 16. class PongTimerTask extends TimerTask { 17. 18. void updatePosition() { 19. 20. int highestAllowedX = maxX - ballWidth; 21. 22. ballX += ballMoveX; 23. if (ballX < 0) { 24. ballX = -ballX; 25. ballMoveX = -ballMoveX; 26. } else if (ballX > highestAllowedX) { 27. ballX = (highestAllowedX * 2 ) - ballX; 28. ballMoveX = - ballMoveX; 29. } 30. 31. ballY += ballMoveY; 32. if (ballY < 0) { 33. ballY = -ballY; 34. ballMoveY = -ballMoveY; 35. } else if ((ballY + ballHeight) >= paddleY) { 36. 37. if ((ballY + ballHeight - ballMoveY) < 38. paddleY) { 39. 40. // Just hit the paddle in the Y 41. // direction -- now check if 42. // the middle of the ball intersects 43. // the paddle in the X direction 44. // (this check isn't perfect since 45. // ballMove has already been added 46. // to ballX, but it is good enough). 47. 48. int ballMiddleX = 49. ballX + (ballWidth / 2); 50. 51. if ((ballMiddleX >= paddleX) || 52. (ballMiddleX <= (paddleX + 53. paddleWidth))) { 54. ballY = 55. ((paddleY - ballHeight) * 2) - 56. ballY; 57. ballMoveY = -ballMoveY; 58. int newX = 59. random.nextInt(7) + 7; 60. // keep moving in same X dir 61. ballMoveX = (ballMoveX > 0) ? 62. newX : -newX; 63. } 64. } 65. } 66. } 67. 68. public void run() { 69. 70. if (focus) { 71. updatePosition(); 72. repaint(); 73. } 74. if (ballY > maxY) { 75. timer.cancel(); 76. } 77. } 78. } 79. public void paint(Graphics g) { 80. 81. g.setColor(Color.BLACK); 82. g.fillRect(paddleX, paddleY, 83. paddleWidth, paddleHeight); 84. g.setColor(Color.RED); 85. g.fillOval(ballX, ballY, ballWidth, ballHeight); 86. 87. } 88. 89. }
Suggestions
Which member variables in the Pong class are potentially updated by the updatePosition() method, as opposed to only being used?
What do the calculation on line 27 and the similar calculation on lines 5456 accomplish? What are some "inputs" that could test these single lines of code?
How are ballX and ballY restricted? Verify any modifications to these variables to ensure that the restrictions are honored.
Hints
Walk through updatePosition() with the following inputs. (Before you do, imagine each scenario visually. For example, the first one represents the ball bouncing off the left wall while moving downward.)
ballX == 4 ballY == 50 ballMoveX == -6 ballMoveY == 10
ballX == 146 ballY == 2 ballMoveX == 7 ballMoveY == -6 highestAllowedX == 150
ballX == 50 ballY == 176 ballMoveX == 10 ballMoveY == 8 ballWidth == 10 ballHeight == 10 paddleX == 45 paddleY == 190 paddleWidth == 30
Explanation of the Bug
The check on lines 5153
if ((ballMiddleX >= paddleX) || (ballMiddleX <= (paddleX + paddleWidth))) {
has a B.expression error. The logical operator || is incorrect. As written, the paddle is "unmissable"; the ball always bounces up when it reaches the level of the paddle, even if the paddle is nowhere near it. If the ball is to the right of the paddle, the first part of the || expression will be true; if it is to the left of the paddle, the second part will be true; and if it is hitting the paddle, both parts will be true. In all cases the overall expression will be true.
The logical operator should be && instead, so the code should read as follows:
if ((ballMiddleX >= paddleX) && (ballMiddleX <= (paddleX + paddleWidth))) {