Chapter 13. Sprite Animations and Special Effects

Sprites often animate through a temporary sequence of images; for example, Figure 13.1 shows, from top to bottom, the explosion animation that Snail Bait displays when the runner runs into a bee.

Figure 13.1. The runner exploding after a collision

Image

In addition to special effects, such as exploding bees, that affect one or more sprites at a time, games often implement special effects that affect the game as a whole. When the runner loses her life for example, Snail Bait transitions to the next life by slowing time and fading the game’s canvas almost entirely from view.

In this chapter you will learn how to do the following:

• Implement temporary sprite animations (Section 13.1 on p. 323)

• Create special effects for the game as a whole (Section 13.2 on p. 329)

• Transition between lives (Section 13.2.2 on p. 332)

• Choreograph effects between sprites by making bees explode when the runner lands on a button (Section 13.3 on p. 338)

The online examples for this chapter are listed in Table 13.1.

Table 13.1. Sprite animations and special effects examples online

Image

13.1. Implement Sprite Animations

Snail Bait implements sprite animations, such as the one shown in Figure 13.1, with cell switch behaviors. A cell switch behavior, when triggered, temporarily changes the cells that a sprite’s artist draws. For example, when the runner explodes, a cell switch behavior changes her animation cells to those shown in Figure 13.2 for 0.5 seconds.

Figure 13.2. The explosion images from Snail Bait’s sprite sheet (shown with a gray background to make the white parts of the explosion visible).

Image

The CellSwitchBehavior constructor is shown in Example 13.1.

Example 13.1. CellSwitchBehavior constructor


var CellSwitchBehavior = function (
       cells,      // An array of rectangles in the game's sprite sheet
       duration,   // The duration of the animation in milliseconds
       trigger,    // A function that triggers the behavior
       callback) { // A function the behavior invokes after the animation

   this.cells    = cells;
   this.duration = duration || 1000;
   this.trigger  = trigger;
   this.callback = callback;
};


The CellSwitchBehavior constructor takes four arguments. The first is an array of bounding boxes in Snail Bait’s spritesheet. Those bounding boxes are the temporary animation cells, and the argument is mandatory. The remaining arguments are optional. The second argument is the duration of the cell switch in milliseconds. If you don’t specify a duration, it defaults to one second.

The last two arguments are functions. The trigger function returns a boolean value; when it returns a truthy value, the cell switch behavior takes action. If the trigger function returns a value that does not evaluate to true, then the cell switch behavior does nothing.

Cell switch behaviors invoke their callback function when the cell switch is complete, passing a reference to the associated sprite and the cell switch behavior itself.

If you don’t specify a trigger function, the associated cell switch behavior never switches cells, and if you don’t specify a callback function you won’t receive any notification when the animation is complete.

Like all behaviors, cell switch behaviors implement an execute() method that Snail Bait invokes every animation frame. That method is shown in Example 13.2.

Example 13.2. The cell switch behavior’s execute() method


CellSwitchBehavior.prototype = {
   ...

   execute: function(sprite, now, fps, lastAnimationFrameTime) {
      if (this.trigger &&
          this.trigger(sprite, now, fps, lastAnimationFrameTime)) {

         if (sprite.artist.cells !== this.cells) {
            this.switch(sprite, now);
         }
         else if (now - sprite.switchStartTime > this.duration) {
            this.revert(sprite, now);
         }
      }
   },
   ...
};


The cell switch behavior takes action only when the behavior has a trigger function and that function returns true. In that case, if the switch hasn’t taken place yet, the behavior switches the sprite’s animation cells. If the switch has taken place and the elapsed time since the switch took place is greater than the animation’s duration, then the cell switch behavior restores the cells to their original state.

The behavior’s switch() and revert() methods are listed in Example 13.3.

The cell switch behavior’s switch() method switches cells by saving the original cells and the index pointing to the current cell, and replacing them with the temporary cells and zero, respectively. It also records the time of the switch.

Subsequently, after the switch’s duration is over, the revert() method returns the sprite’s cells and the index to what they were before the switch and invokes the cell switch behavior’s callback function.

Example 13.3. Switching and reverting animation cells


CellSwitchBehavior.prototype = {
   ...

   switch: function (sprite, now) {
      sprite.originalCells = sprite.artist.cells;
      sprite.originalIndex = sprite.artist.cellIndex;

      sprite.switchStartTime = now;

      sprite.artist.cells = this.cells;
      sprite.artist.cellIndex = 0;
   },

   revert: function (sprite, now) {
      sprite.artist.cells = sprite.originalCells;
      sprite.artist.cellIndex = sprite.originalIndex;

      if (this.callback) {
         this.callback(sprite, this);
      }
   },
   ...
};


To animate through the cells that the sprite temporarily displays, the sprite must have some other behavior—such as the runner’s run behavior discussed in [Missing XREF!] or cycle behaviors used by many of Snail Bait’s other sprites—to cycle through those cells. For example, Example 13.4 shows the creation of the runner sprite with the runner’s explode behavior.

Example 13.4. Creating the runner sprite, revised


SnailBait.prototype = {
   ...

   createRunnerSprite: function () {
      ...

      this.runner = new Sprite(
                       'runner',

                       new SpriteSheetArtist(this.spritesheet,
                                             this.runnerCellsRight),

                       [
                          this.runBehavior,
                          this.jumpBehavior,
                          this.collideBehavior,
                          this.runnerExplodeBehavior
                       ]
                   );
      ...
   },
   ...
};


Recall that the runner’s run behavior continuously cycles through the runner’s animation cells. The runner’s explode behavior, listed in Example 13.5, temporarily changes those animation cells.

Example 13.5. Runner explode behavior


var SnailBait = function () {
   ...

   this.RUNNER_EXPLOSION_DURATION = 500; // milliseconds

   this.explosionCells = [
      {
         left: 3   top: 48,
         width: 50, height: this.EXPLOSION_CELLS_HEIGHT
      },

      {
         left: 63,  top: 48,
         width: 70, height: this.EXPLOSION_CELLS_HEIGHT
      },

      {
         left: 146, top: 48,
         width: 70, height: this.EXPLOSION_CELLS_HEIGHT
      },

      {
         left: 233, top: 48,
         width: 70, height: this.EXPLOSION_CELLS_HEIGHT
      },

      {
         left: 308, top: 48,
         width: 70, height: this.EXPLOSION_CELLS_HEIGHT
      },

      {
         left: 392, top: 48,
         width: 70, height: this.EXPLOSION_CELLS_HEIGHT
      },

      { left: 473, top: 48,
        width: 70, height: this.EXPLOSION_CELLS_HEIGHT
      }
   ];

   this.runnerExplodeBehavior = new CellSwitchBehavior(
      this.explosionCells,
      this.RUNNER_EXPLOSION_DURATION,

      function (sprite, now, fps, lastAnimationFrameTime) { // Trigger
         return sprite.exploding;
      },

      function (sprite, animator)  { // Callback
         sprite.exploding =  false;
      }
   );
   ...
};


The runner’s explode behavior is an instance of CellSwitchBehavior. The cells are the explosionCells, the duration of the switch is 500 ms, and the behavior’s trigger is the runner’s exploding property. The callback that the behavior invokes at the end of the animation sets the runner’s exploding property to false, ensuring that the explosion does not repeat.

The trigger for the runner’s explode behavior—meaning the exploding property of the runner—set to true by Snail Bait’s explode() method, listed in Example 13.6.

Snail Bait explodes several types of sprites: the runner, bees, and bats. If the sprite is the runner and her run animation rate (stored in the runAnimationRate property) is zero, that means she is not cycling through her images because she’s jumping. To ensure that the runner cycles through the explosion cells when she’s jumping, the explode() method sets her animation rate to its normal value.

Example 13.6. Snail Bait’s explode() method


SnailBait.prototype = {
   ...

   explode: function (sprite) {
      if ( ! sprite.exploding) {
         if (sprite.runAnimationRate === 0) {
            sprite.runAnimationRate = this.RUN_ANIMATION_RATE;
         }

         sprite.exploding = true;
         ...
      }
   },
   ...
};


Subsequently, the explode() method sets the sprite’s exploding property to true, which triggers its explode behavior. SnailBait’s explode() method is invoked from the collide behavior, as shown in Example 13.7.

Example 13.7. Exploding the runner


var SnailBait = function () {
   ...

   this.collideBehavior = {
      ...

      processBadGuyCollision: function (sprite) {
         snailBait.explode(sprite);  // sprite is the runner
         ...
      },
      ...
   };
   ...
};


Cell switch behaviors let you implement many types of effects that are specific to one or more individual sprites. Games often have special effects that apply to the game as a whole however, as we discuss next.

13.2. Create Special Effects

Snail Bait has two special effects that apply to the entire game: shaking the game and transitioning between lives. When the runner collides with a bat, a bee, or the snail bomb, the runner explodes as you saw in the preceding section. When the explosion finishes, Snail Bait shakes the entire game back-and-forth before transitioning to either the next life or to the end-game sequence.

In the previous section, you saw how cell switch behaviors neatly encapsulate the code to temporarily animate a sprite through a sequence of cells. To implement shaking the game and life transitions, we take a more ad hoc approach, using JavaScript’s setTimeout() function for the relatively simple business of shaking the game and using CSS transitions for transitioning between lives.

13.2.1. Shake the Game

Snail Bait shakes the game with a shake() method. It invokes that method in the collide behavior’s processBadGuyCollision() method, as shown in Example 13.8. See Chapter 11 for a discussion of the collide behavior.

Example 13.8. Shaking the game when colliding with bad guys


var SnailBait = function () {
   ...

   this.collideBehavior = {
      ...

      processBadGuyCollision: function (sprite) {
         snailBait.explode(sprite);  // sprite is the runner
         snailBait.shake();
         ...
      },
      ...
   };
};


The processBadGuyCollision() method explodes the runner and shakes the game.

Although they are not suitable for time-critical animations, as discussed in Chapter 3, the setTimeout() and setInterval() JavaScript functions are nonetheless useful for implementing special effects. When Snail Bait’s runner explodes, for example, Snail Bait uses setTimeout() to shake the entire game back and forth with its shake() method, listed in Example 13.9.

Example 13.9. Shaking Snail Bait


SnailBait.prototype = {
   ...

   shake: function () {
      var SHAKE_INTERVAL = 80, // milliseconds
          v = snailBait.BACKGROUND_VELOCITY*1.5,
          originalVelocity = snailBait.bgVelocity;

      this.bgVelocity = -v;

      setTimeout( function (e) {
       snailBait.bgVelocity = v;
       setTimeout( function (e) {
          snailBait.bgVelocity = -v;
          setTimeout( function (e) {
             snailBait.bgVelocity = v;
             setTimeout( function (e) {
                snailBait.bgVelocity = -v;
                setTimeout( function (e) {
                   snailBait.bgVelocity = v;
                   setTimeout( function (e) {
                      snailBait.bgVelocity = -v;
                      setTimeout( function (e) {
                         snailBait.bgVelocity = v;
                         setTimeout( function (e) {
                            snailBait.bgVelocity = -v;
                            setTimeout( function (e) {
                               snailBait.bgVelocity = v;
                               setTimeout( function (e) {
                                  snailBait.bgVelocity = -v;
                                  setTimeout( function (e) {
                                     snailBait.bgVelocity = v;
                                     setTimeout( function (e) {
                                        snailBait.bgVelocity = originalVelocity;
                                     }, SHAKE_INTERVAL);
                                   }, SHAKE_INTERVAL);
                                }, SHAKE_INTERVAL);
                              }, SHAKE_INTERVAL);
                           }, SHAKE_INTERVAL);
                        }, SHAKE_INTERVAL);
                     }, SHAKE_INTERVAL);
                  }, SHAKE_INTERVAL);
              }, SHAKE_INTERVAL);
           }, SHAKE_INTERVAL);
        }, SHAKE_INTERVAL);
      }, SHAKE_INTERVAL);
    },

   ...
};


The shake() method shakes the game back and forth at a velocity equal to 1.5 times the game’s normal background velocity. Every 80 milliseconds, Snail Bait reverses the sign of that velocity to move everything in the game in the opposite direction. When the shaking is finished, the shake() method restores the game’s background velocity to what it was when the method was originally invoked.

Alternatively, you could implement the shake() method with a recursive function, as shown in Example 13.10.

Example 13.10. A recursive variant of the shake() method (functionality identical to Example 13.9)


SnailBait.prototype = {
   ...

   shake: function () {
      var NUM_SHAKES = 12,
          SHAKE_INTERVAL = 80, // milliseconds
          velocity = snailBait.BACKGROUND_VELOCITY*1.5,
          originalVelocity = snailBait.bgVelocity,
          i = 0;

      reverseDirection = function () {
         snailBait.bgVelocity = i % 2 ? velocity : -velocity;

         if (i < NUM_SHAKES) {
            setTimeout(reverseDirection, SHAKE_INTERVAL);
            ++i;
         }
         else {
            snailBait.bgVelocity = originalVelocity;
         }
      };

      reverseDirection();
   },
   ...
};


Now that you’ve seen how to shake the game, let’s see how Snail Bait implements a more sophisticated special effect: transitioning between lives.

13.2.2. Transition Between Lives

After making the runner explode and subsequently shaking the game when the runner collides with a bat, a bee, or the snail bomb, Snail Bait transitions to the next life. That transition has three distinct phases:

1. The runner disappears, time slows to a crawl, and the game’s canvas fades almost entirely from view.

2. At the moment the canvas stops fading out, Snail Bait resets the level, placing the background and all sprites except the runner at their original locations. The game puts the runner at her usual horizontal position, but puts her on the third track with no platform underneath her.

3. As soon as the runner appears, the canvas begins transitioning from barely visible to fully opaque, time returns to normal, and the runner falls, while running in place, from the third track to the platform on the first track. When she lands on the platform, the runner stops running in place.

Figure 13.3 shows the transition in the final phase after the runner has landed on the platform and the canvas is transitioning to fully opaque.

Figure 13.3. Transition between lives

Image

The transition between lives starts in Snail Bait’s loseLife() method, which the game invokes from the collide behavior’s processBadGuyCollision() method, whose final implementation is shown in Example 13.11.

Example 13.11. Processing bad guy collisions


var SnailBait = function () {
   ...

   this.collideBehavior = {
      ...

      processBadGuyCollision: function (sprite) {
         snailBait.explode(sprite);  // sprite is the runner
         snailBait.shake();
         snailBait.loseLife();
      },
      ...
   };
   ...
};


The processBadGuyCollision() method explodes the sprite, shakes the game, and invokes Snail Bait’s loseLife() method, listed in Example 13.12.

Example 13.12. Losing a life


var SnailBait = function () {
   ...

   this.RUNNER_EXPLOSION_DURATION = 500;
   ...
};

SnailBait.prototype = {
   ...

   loseLife: function () {
      var transitionDuration = 3000; // Same as canvas's transition time

      this.lives--;
      ...

      if (this.runner.exploding) {
         this.startLifeTransition(snailBait.RUNNER_EXPLOSION_DURATION);
         transitionDuration += snailBait.RUNNER_EXPLOSION_DURATION;
      }
      else {
         this.startLifeTransition();
      }
      ...

      setTimeout( function () { // After the canvas's transition
         snailBait.endLifeTransition();
      }, transitionDuration);
   },
   ...
};


The loseLife() method decrements the game’s lives property and starts the life transition by invoking startLifeTransition(). The value that loseLife() passes to startLifeTransition() when the runner is exploding represents a delay, in milliseconds, before startLifeTransition() actually starts the transition, so the transition does not start until the explosion is complete. If the runner is not exploding, meaning she fell through the bottom of the game, loseLife() starts the life transition immediately by invoking startLifeTransition() without passing a parameter.

Three seconds after the life transition begins, the loseLife() method ends the transition. That three seconds corresponds to the duration for the canvas’s transition on its opacity property, as you can see in Example 13.13.

Example 13.13. The CSS transition for Snail Bait’s canvas


#snailbait-game-canvas {
   ...

   -webkit-transition: opacity 3s;
   -moz-transition: opacity 3s;
   -o-transition: opacity 3s;
   transition: opacity 3s;
}


The startLifeTransition() method is listed in Example 13.14.

Example 13.14. Starting the life transition


SnaiBait.prototype = {
   ...

   startLifeTransition: function (slowMotionDelay) {
      var CANVAS_TRANSITION_OPACITY =  0.05,
          SLOW_MOTION_RATE = 0.1;

      if (slowMotionDelay === undefined) {
         slowMotionDelay = 0;
      }

      this.canvas.style.opacity = CANVAS_TRANSITION_OPACITY;
      this.playing = false;

      setTimeout( function () {
         snailBait.setTimeRate(SLOW_MOTION_RATE);
         snailBait.runner.visible = false;
      }, slowMotionDelay);
   },
   ...
};


The startLifeTransition() method starts the life transition by setting the opacity of the game’s canvas to 0.05. The browser responds to that setting by smoothly animating the canvas from fully opaque to barely visible. The animation lasts three seconds.

The startLifeTransition() method also sets the game’s playing property to false, which causes the game to disregard keyboard input until the playing property is subsequently reset to true, as you can see from Example 13.15.

Example 13.15. Disregarding keyboard input during transitions


window.addEventListener(
   'keydown',

   function (e) {
      var key = e.keyCode;

      if (! snailBait.playing || snailBait.runner.exploding) {
         return;
      }
      ...
   }
);


Using setTimeout(), the startLifeTransition() method in Example 13.14 waits for the specified delay—either 500 ms if the runner is exploding, or 0 ms if the runner has fallen through the bottom of the game—to make the runner disappear and slow the game’s time rate to 1/10th of its normal rate.

Snail Bait ends life transitions, with the endLifeTransition() method listed in Example 13.16, three seconds after they begin, which coincides with the moment the game’s canvas finishes fading out.

Example 13.16. Ending the life transition


SnaiBait.prototype = {
   ...

   endLifeTransition: function () {
      var TIME_RESET_DELAY = 1000,
          RUN_DELAY = 500;

      this.canvas.style.opacity = this.OPAQUE;

      if (this.lives === 0) this.gameOver();
      else                  this.restartLevel();

      setTimeout( function () { // Reset the time
         snailBait.setTimeRate(1.0);

         setTimeout( function () { // Stop running
            snailBait.runner.runAnimationRate = 0;
         }, RUN_DELAY);
      }, TIME_RESET_DELAY);
   },
   ...
};


The endLifeTransition() method sets the opacity of the game’s canvas to fully opaque and the browser responds by starting an animation from the current opacity of 0.05 to 1.0. That animation, as before, takes three seconds. The endLifeTransition() method subsequently invokes either gameOver() or restartLevel(), depending on the number of remaining lives.

Recall that startLifeTransition() slows Snail Bait’s time rate to 1/10th of its normal rate at the beginning of the transition. The endLifeTransition() method waits one second before setting the game’s time rate back to normal. Subsequently, after 0.5 seconds, endLifeTransition() stops the runner’s run animation by setting her runAnimationRate property to 0, and sets the game’s playing property to true so that the game resumes handling keyboard input.

The restartLevel() method, listed in Example 13.17, restarts the level by resetting the game’s offsets along with runner properties and making all sprites visible once again.

Example 13.17. Restarting the level


SnailBait.prototype = {
   ...

   restartLevel: function () {
      this.resetOffsets();
      this.resetRunner();
      this.makeAllSpritesVisible();

      this.playing =  true;
   },

   resetOffsets: function () {
      this.bgVelocity       = 0;
      this.backgroundOffset = 0;
      this.platformOffset   = 0;
      this.spriteOffset     = 0;
   },

   resetRunner: function () {
      this.runner.left      = snailBait.RUNNER_LEFT;
      this.runner.track     = 3;
      this.runner.hOffset   = 0;
      this.runner.visible   = true;
      this.runner.exploding = false;
      this.runner.jumping   = false;
      this.runner.falling   = false;
      this.runner.top       = this.calculatePlatformTop(3) -
                              this.runner.height;

      this.runner.artist.cells     = this.runnerCellsRight;
      this.runner.artist.cellIndex = 0;
   },

   makeAllSpritesVisible: function () {
      for (var i=0; i < snailBait.sprites; ++i) {
         snailBait.sprites[i].visible = true;
      }
   },
   ...
};


When restartLevel() makes all sprites visible, the runner reappears. The last thing the runner did was explode, and as we discussed in Section 13.1, “Implement Sprite Animations,” on p. 323, when the runner explodes, Snail Bait makes sure that her run animation rate is nonzero to ensure that she cycles through her explosion animation cells. Therefore, when restartLevel() makes the runner visible again, she will be running in place.

Now that you’ve seen how to implement sprite animations and create special effects, let’s see how to choreograph sprite behaviors to trigger sprite animations.

13.3. Choreograph Effects

A little more than halfway through the game’s only level, the runner encounters two bees that block her path, as shown in Figure 13.4. It’s possible for the runner to avoid the leftmost bee by running underneath it and jumping just before she falls off the platform; however, the space between the rightmost bee and the platform below it is too small for the runner to pass through.

Figure 13.4. Detonating the blue button opens a pathway for the runner

Image

To get past the bees shown in Figure 13.4, the runner must detonate the blue button by landing on it, as shown in Figure 13.4. That detonation triggers the sequence of events shown in Figure 13.5.

The top screenshot in Figure 13.5 shows the runner just before she lands on the blue button. In the screenshot below that, the runner has just landed on the button, causing the button to flatten and the leftmost bee to explode.

Figure 13.5. Landing on the blue button makes the bees explode (top to bottom)

Image

Shortly after the leftmost bee explodes, the rightmost bee also explodes, as shown in the third screenshot from the top in Figure 13.5.

The bottom screenshot in Figure 13.5 shows the runner passing by the exploding rightmost bee. When she lands on the platform, the runner must immediately jump over the bat in front of her, which never explodes.

That’s how button detonation works, so let’s see how it’s implemented next.

13.3.1. Explode Bees

To make bees explode, we add a cell switch behavior to each bee, as shown in Example 13.18.

Example 13.18. Making bees explode


var SnailBait = function () {
   ...
   this.BAD_GUYS_EXPLOSION_DURATION = 1500;
   ...
};

SnailBait.prototype = {
   ...

   createBeeSprites: function () {
      var bee,
          beeArtist,
          BEE_FLAP_DURATION = 100,
          BEE_FLAP_GAP = 30;

      for (var i = 0; i < this.beeData.length;  ++i) {
         bee = new  Sprite(
             'bee',

             new SpriteSheetArtist(this.spritesheet,
                                   this.beeCells),
             [
                new CycleBehavior(BEE_FLAP_DURATION),

                new CellSwitchBehavior(
                       this.explosionCells,
                       this.BAD_GUYS_EXPLOSION_DURATION,

                       function (sprite, now, fps) { // Trigger
                          return sprite.exploding;
                       },

                       function (sprite, animator) { // Callback
                          sprite.exploding = false;
                       }
                    )
             ]
         );
         ...
      }
   },
   ...
};


Cell switch behaviors for bees are nearly identical to the cell switch behavior for the runner listed in Example 13.5, except bees explode for a longer duration than the runner to give the runner a little extra time as she runs by an exploding bee before it reverts to being a bee.

Now that we have exploding bees, let’s make them explode when the runner lands on the blue button.

13.3.2. Detonate Buttons

To detonate buttons when the runner lands on them, we modify the processCollision() method of the runner’s collide behavior to process collisions between the runner and Snail Bait’s buttons, as shown in Example 13.19.

Example 13.19. Tripping the detonate behavior’s trigger


var SnailBait = function () {
   ...

   this.collideBehavior = {
      ...

      processCollision: function (sprite, otherSprite) {
         if (sprite.jumping && 'platform' === otherSprite.type) {
            ...
         }
         ...

         else if ('button' === otherSprite.type) {
            if (sprite.jumping && sprite.descendTimer.isRunning() ||
                sprite.falling) {
               otherSprite.detonating = true;
            }
         }
      },
      ...
   };
   ...
};


When the runner collides with a button and she’s either descending from a jump or falling, the revised processCollision() method sets the button’s detonating property to true. That property is the trigger for the blue button’s detonate behavior, listed in Example 13.20.

Example 13.20. Blue button detonate behavior


var SnailBait = function () {
   ...

   // Detonate buttons..................................................

   this.blueButtonDetonateBehavior = {
      execute: function(sprite, now, fps, lastAnimationFrameTime) {
         var BUTTON_REBOUND_DELAY = 1000,
             SECOND_BEE_EXPLOSION_DELAY = 400; // milliseconds

         if ( ! sprite.detonating) { // trigger
            return;
         }

         sprite.artist.cellIndex = 1; // flatten the button

         snailBait.explode(snailBait.bees[5]);

         setTimeout( function () {
            snailBait.explode(snailBait.bees[6]);
         }, SECOND_BEE_EXPLOSION_DELAY);

         sprite.detonating = false; // reset trigger

         setTimeout( function () {
            sprite.artist.cellIndex = 0; // rebound
         }, BUTTON_REBOUND_DELAY);
      }
   };
   ...
};


The blue button’s detonate behavior’s execute() method, which is invoked by Snail Bait for every animation frame, takes action only when the detonating property of the sprite to which it is attached is true. If that’s the case, the behavior flattens the button, makes the leftmost sprite in Figure 13.4 explode, and sets the button’s detonating property to false so that the behavior does not repeat.

Subsequently, after a 400 ms delay, the blue button detonate behavior’s execute() method explodes the rightmost bee in Figure 13.4.

Finally, one second after the button detonates, the blue button detonate behavior makes the button pop back up by resetting the animation cell drawn by the button’s artist.

Snail Bait creates the blue button’s detonate behavior and attaches it to the blue button in the game’s createButtonSprites() method, as shown in Example 13.21.

Example 13.21. Creating button sprites, revised


SnailBait.prototype = {
   ...

   createButtonSprites: function () {
      var button;

      for (var i = 0; i < this.buttonData.length; ++i) {
         if (i !== this.buttonData.length - 1) {
            button = new Sprite('button',
                       new SpriteSheetArtist(this.spritesheet,
                                            this.blueButtonCells),
                       [ this.paceBehavior,
                         this.blueButtonDetonateBehavior ]);
         }
         else {
            ...
         }
         ...
      }
   },
   ...
};


13.4. Conclusion

In this chapter you saw the implementation of a simple cell switch behavior that lets you temporarily animate sprites. You can use that behavior to make sprites do all sorts of things besides explode.

Although it’s not reliable for time-critical animations, JavaScript’s setTimeout() function is useful for special effects, as you saw in the final two-thirds of this chapter. You also saw how to implement special effects that apply to games as a whole, as opposed to sprite animations that involve only a single sprite.

In this chapter, you saw how to implement visual special effects. In the next chapter, we explore audio special effects by incorporating sound and music into Snail Bait.

13.5. Exercises

1. Experiment with the timing for life transitions by making them take a longer or shorter period of time. You will have to modify the game’s CSS in addition to its JavaScript.

2. Instead of blowing up when she runs into a bat, turn the runner into a flapping bat for two seconds and continue play.

3. Instead of blowing up two bees in succession, blow up all sprites except the runner when the runner lands on the blue button.

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

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