6.8.6. Method updatePositions

Method updatePositions (Fig. 6.12) is called by the CannonThread’s run method (Section 6.8.14) to update the on-screen elements’ positions and to perform simple collision detection. The new locations of the game elements are calculated based on the elapsed time in milliseconds between the previous and current animation frames. This enables the game to update the amount by which each game element moves based on the device’s refresh rate. We discuss this in more detail when we cover game loops in Section 6.8.14.


209    // called repeatedly by the CannonThread to update game elements
210    private void updatePositions(double elapsedTimeMS)
211    {
212      double interval = elapsedTimeMS / 1000.0; // convert to seconds
213
214      if (cannonballOnScreen) // if there is currently a shot fired
215      {
216         // update cannonball position
217         cannonball.x += interval * cannonballVelocityX;
218         cannonball.y += interval * cannonballVelocityY;
219
220         // check for collision with blocker
221         if (cannonball.x + cannonballRadius > blockerDistance &&
222            cannonball.x - cannonballRadius < blockerDistance &&
223            cannonball.y + cannonballRadius > blocker.start.y &&
224            cannonball.y - cannonballRadius < blocker.end.y)
225         {
226            cannonballVelocityX *= -1; // reverse cannonball's direction
227            timeLeft -= MISS_PENALTY; // penalize the user
228
229            // play blocker sound
230            soundPool.play(soundMap.get(BLOCKER_SOUND_ID), 1, 1, 1, 0, 1f);
231         }
232         // check for collisions with left and right walls
233         else if (cannonball.x + cannonballRadius > screenWidth ||
234            cannonball.x - cannonballRadius < 0)
235         {
236            cannonballOnScreen = false; // remove cannonball from screen
237         }
238         // check for collisions with top and bottom walls
239         else if (cannonball.y + cannonballRadius > screenHeight ||
240            cannonball.y - cannonballRadius < 0)
241         {
242            cannonballOnScreen = false; // remove cannonball from screen
243         }
244         // check for cannonball collision with target
245         else if (cannonball.x + cannonballRadius > targetDistance &&
246            cannonball.x - cannonballRadius < targetDistance &&
247            cannonball.y + cannonballRadius > target.start.y &&
248            cannonball.y - cannonballRadius < target.end.y)
249         {
250            // determine target section number (0 is the top)
251            int section =
252               (int) ((cannonball.y - target.start.y) / pieceLength);
253
254            // check if the piece hasn't been hit yet
255            if ((section >= 0 && section < TARGET_PIECES ) &&
256               !hitStates[section])
257            {
258               hitStates[section] = true; // section was hit
259               cannonballOnScreen = false ; // remove cannonball
260               timeLeft += HIT_REWARD; // add reward to remaining time
261
262               // play target hit sound
263               soundPool.play(soundMap.get(TARGET_SOUND_ID), 1,
264                  1, 1, 0, 1f);                                
265
266               // if all pieces have been hit
267               if (++targetPiecesHit == TARGET_PIECES)
268               {
269                  cannonThread.setRunning(false); // terminate thread
270                  showGameOverDialog(R.string.win); // show winning dialog
271                  gameOver = true;
272               }
273            }
274         }
275      }
276
277      // update the blocker's position
278      double blockerUpdate = interval * blockerVelocity;
279      blocker.start.y += blockerUpdate;
280      blocker.end.y += blockerUpdate;
281
282      // update the target's position
283      double targetUpdate = interval * targetVelocity;
284      target.start.y += targetUpdate;
285      target.end.y += targetUpdate;
286
287      // if the blocker hit the top or bottom, reverse direction
288      if (blocker.start.y < 0 || blocker.end.y > screenHeight)
289         blockerVelocity *= -1;
290
291      // if the target hit the top or bottom, reverse direction
292      if (target.start.y < 0 || target.end.y > screenHeight)
293         targetVelocity *= -1;
294
295      timeLeft -= interval; // subtract from time left
296
297      // if the timer reached zero
298      if (timeLeft <= 0.0)
299      {
300         timeLeft = 0.0;
301         gameOver = true; // the game is over
302         cannonThread.setRunning(false); // terminate thread
303         showGameOverDialog(R.string.lose); // show the losing dialog
304      }
305   } // end method updatePositions
306


Fig. 6.12 | CannonView method updatePositions.

Elapsed Time Since the Last Animation Frame

Line 212 converts the elapsed time since the last animation frame from milliseconds to seconds. This value is used to modify the positions of various game elements.

Checking for Collisions with the Blocker

Line 214 checks whether the cannonball is on the screen. If it is, we update its position by adding the distance it should have traveled since the last timer event. This is calculated by multiplying its velocity by the amount of time that passed (lines 217–218). Lines 221–224 check whether the cannonball has collided with the blocker. We perform simple collision detection, based on the rectangular boundary of the cannonball. There are four conditions that must be met if the cannonball is in contact with the blocker:

• The cannonball’s x-coordinate plus the cannonball’s radius must be greater than the blocker’s distance from the left edge of the screen (blockerDistance) (line 221). This means that the cannonball has reached the blocker’s distance from the left edge of the screen.

• The cannonball’s x-coordinate minus the cannonball’s radius must also be less than the blocker’s distance from the left edge of the screen (line 222). This ensures that the cannonball has not yet passed the blocker.

• Part of the cannonball must be lower than the top of the blocker (line 223).

• Part of the cannonball must be higher than the bottom of the blocker (line 224).

If all these conditions are met, we reverse the cannonball’s direction on the screen (line 226), penalize the user by subtracting MISS_PENALTY from timeLeft, then call soundPool’s play method to play the blocker hit sound—BLOCKER_SOUND_ID is used as the soundMap key to locate the sound’s ID in the SoundPool.

Checking Whether the Cannonball Left the Screen

We remove the cannonball if it reaches any of the screen’s edges. Lines 233–237 test whether the cannonball has collided with the left or right wall and, if it has, remove the cannonball from the screen. Lines 239–243 remove the cannonball if it collides with the top or bottom of the screen.

Checking for Collisions with the Target

We then check whether the cannonball has hit the target (lines 245–248). These conditions are similar to those used to determine whether the cannonball collided with the blocker. If the cannonball hit the target, lines 251–252 determine which section has been hit—dividing the distance between the cannonball and the bottom of the target by the length of a piece. This expression evaluates to 0 for the topmost section and 6 for the bottommost. We check whether that section was previously hit, using the hitStates array (line 256). If it wasn’t, we set the corresponding hitStates element to true and remove the cannonball from the screen. We then add HIT_REWARD to timeLeft, increasing the game’s time remaining, and play the target hit sound (TARGET_SOUND_ID). We increment targetPiecesHit, then determine whether it’s equal to TARGET_PIECES (line 267). If so, the game is over, so we terminate the CannonThread by calling its setRunning method with the argument false, invoke method showGameOverDialog with the String resource ID representing the winning message and set gameOver to true.

Updating the Blocker and Target Positions

Now that all possible cannonball collisions have been checked, the blocker and target positions must be updated. Lines 278–280 change the blocker’s position by multiplying blockerVelocity by the amount of time that has passed since the last update, and adding that value to the current x- and y-coordinates. Lines 283–285 do the same for the target. If the blocker has collided with the top or bottom wall, its direction is reversed by multiplying its velocity by -1 (lines 288–289). Lines 292–293 perform the same check and adjustment for the full length of the target, including any sections that have already been destroyed.

Updating the Time Left and Determining Whether Time Ran Out

We decrease timeLeft by the time that has passed since the prior animation frame (line 295). If timeLeft has reached zero, the game is over—we set timeLeft to 0.0 just in case it was negative; otherwise, sometimes a negative final time would display on the screen). Then we set gameOver to true, terminate the CannonThread by calling its setRunning method with the argument false and call method showGameOverDialog with the String resource ID representing the losing message.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset