Play the Game Pong, Part II
This applet plays a game of Pong using the PongTimerTask class from the previous example.
The applet class implements two interfaces:
KeyListener receives keystrokes. The user presses 'Z' to go left and 'M' to go right. (The code uses "key typed" events rather than the lower-level "key pressed" and "key released" events.)
FocusListener determines if the applet's window has the focus, so that the game can be paused when the focus is lost.
The schedule() method on the Timer class, as called here, sets up a recurring task, and takes three parameters: an implementation of the TimerTask interface, a delay in milliseconds until the first execution of the task, and a delay in milliseconds between subsequent executions of the task. The implementation of PongTimerTask is not shown. Assume that it uses the code from the previous example (with the bug fixed). Recall that the timer calls the run() method of PongTimerTask, which calls updatePosition() if focus is true.
By design, the applet does not deal with the size of the applet window changing in the middle. (If you want to enhance it to handle this, applets can implement the ComponentListener interface to receive notifications of resizing.)
Source Code
1. import java.awt.event.*; 2. import java.awt.*; 3. import java.util.Timer; 4. import java.util.TimerTask; 5. import java.util.Random; 6. 7. public class Pong extends java.applet.Applet 8. implements KeyListener, FocusListener { 9. 10. private int paddleX, paddleY, maxX, maxY; 11. private int paddleWidth, paddleHeight; 12. private int ballX, ballY; 13. private int ballMoveX, ballMoveY; 14. private int ballWidth, ballHeight; 15. boolean focus; 16. Random random; 17. 18. class PongTimerTask extends TimerTask { 19. // Implementation is in previous example. 20. } 21. 22. PongTimerTask timerTask; 23. Timer timer; 24. public void init() { 25. 26. random = new Random(System.currentTimeMillis()); 26. 27. Dimension size = getSize(); 28. maxX = size.width; 29. maxY = size.height; 30. paddleWidth = 80; 31. paddleHeight = 20; 32. ballX = 0; 33. ballY = 0; 34. ballMoveX = random.nextInt(7) + 7; 35. ballMoveY = 10; 36. ballWidth = 20; 37. ballHeight = 20; 38. 39. addKeyListener(this); 40. addFocusListener(this); 41. 42. focus = hasFocus(); 43. 44. timerTask = new PongTimerTask(); 45. timer = new Timer(); 46. // schedule it ten times per second 47. timer.schedule(timerTask, 100, 100); 48. 49. } 50. 51. public void paint(Graphics g) { 52. 53. g.setColor(Color.BLACK); 54. g.fillRect(paddleX, paddleY, 55. paddleWidth, paddleHeight); 56. g.setColor(Color.RED); 57. g.fillOval(ballX, ballY, ballWidth, ballHeight); 58. 59. } 60. 61. public void destroy() { 62. timer.cancel(); 63. removeKeyListener(this); 64. removeFocusListener(this); 65. } 66. 67. // KeyListener methods 68. 69. public void keyPressed(KeyEvent e) { 70. 71. } 72. 73. public void keyReleased(KeyEvent e) { 74. 75. } 76. 77. public void keyTyped(KeyEvent e) { 78. 79. char c = e.getKeyChar(); 80. 81. if ((c == 'z') || (c == 'Z')) { 82. paddleX = 83. (paddleX > 10) ? (paddleX - 10) : 0; 84. } else if ((c == 'm') || (c == 'M')) { 85. paddleX = 86. (paddleX < (maxX - (paddleWidth + 10))) ? 87. (paddleX + 10) : (maxX - paddleWidth); 88. } 89. repaint(); 90. 91. } 92. 93. // FocusListener methods 94. 95. public void focusGained(FocusEvent e) { 96. focus = true; 97. } 98. 99. public void focusLost(FocusEvent e) { 100. focus = false; 101. } 102. }
Suggestions
Describe the exact meaning of each variable declared on lines 1014.
Look at lines 8390. What is the goal of this section?
Based on your understanding of updatePosition() and how positions are stored, is 0 a valid initialization value for ballX and ballY?
Hints
With the values initialized as they are in init():
Walk through keyTyped() for three different values for e.keyGetChar() : 'z', 'M', and 'a'.
Walk through the first iteration of updatePosition() (refer to previous example), assuming that focus is true.
Explanation of the Bug
There is an F.init error. paddleX and paddleY are never initialized.
The paddle position can be initialized as desired. The following code, added anywhere in the init() method (although between lines 31 and 32 would be the logical place), puts the paddle in the middle of the applet (left-to-right), and the bottom of the paddle 50 pixels from the bottom:
paddleX = (size.width - paddleWidth) / 2; paddleY = size.height - (paddleHeight + 50);