HTML5 2D game development: Implement gravity and add sound

Animate realistic falls and add a soundtrack and sound effects

In this series, HTML5 maven David Geary shows you how to implement an HTML5 2D video game one step at a time. In this installment, you'll complete Snail Bait's mechanics by learning how to incorporate gravity when the runner falls. Then, you'll see how to implement sound — both a musical soundtrack and sound effects.

David Geary, Author and speaker, Clarity Training, Inc.

David GearyThe author of Core HTML5 Canvas, David Geary is also the co-founder of the HTML5 Denver User's Group and the author of eight Java books, including the best-selling books on Swing and JavaServer Faces. David is a frequent speaker at conferences, including JavaOne, Devoxx, Strange Loop, NDC, and OSCON, and is a three-time JavaOne Rock Star. He is the author of the HTML5 2D game development, JSF 2 fu, and GWT fu article series for developerWorks.



21 June 2013

Also available in Chinese Russian Japanese

Now that Snail Bait can detect collisions — as implemented in the previous article in this series — the game must deal with one significant type of noncollision: the runner missing a platform. In that case, she begins to fall. In this article, I'll show you how to incorporate gravity to implement realistic falling. Gravity and falling round out all the mechanics of gameplay that Snail Bait needs. After that, I'll switch gears and show you how to incorporate sound (including music) into the game. The full sample code for this installment is available for download.

Falling

Snail Bait's runner falls when she either runs off the edge of a platform or collides with a platform from underneath, as shown in Figure 1:

Figure 1. Falling off the edge of a platform
Screen capture of Snail Bait with the runner falling off the edge of a platform

She also falls when she misses a platform at the end of a jump's descent, as illustrated in Figure 2:

Figure 2. Falling at the end of a jump
Screen capture of Snail Bait with the runner falling at the end of a jump

The runner falls by virtue of her fall behavior. Listing 1 shows the instantiation of the runner sprite, specifying her array of behaviors:

Listing 1. The runner's behaviors
Sprite = function () {
   ...
   this.runner = new Sprite('runner',           // type
                            this.runnerArtist,  // artist
                            [ this.runBehavior, // behaviors
                              this.jumpBehavior,
                              this.collideBehavior,
                              this.fallBehavior,
                            ]); 
};

The runner's fall behavior — like all sprite behaviors — is invoked by Snail Bait for every animation frame when the sprite is visible. Most of the time the fall behavior does nothing. While the runner's falling attribute is true, the fall behavior incrementally moves the runner vertically every animation frame to make it appear as though she is falling. That attribute is set by the runner's fall() method, as shown in Listing 2:

Listing 2. The runner's fall() method
SnailBait.prototype = {
   ...
   equipRunner: function () {
      ...
      this.equipRunnerForJumping();
      this.equipRunnerForFalling();
   },

   equipRunnerForFalling: function () {
      ...

      this.runner.fallAnimationTimer = new AnimationTimer();

      this.runner.fall = function (initialVelocity) {
         // set attributes for the runner's fall behavior and
         // start the fall animation timer

         this.velocityY = initialVelocity || 0;
         this.initialVelocityY = initialVelocity || 0;
         this.falling = true;

         this.fallAnimationTimer.start();
      }
   },
   ...
};

What about the runner's horizontal velocity?

The only concern of the runner's fall behavior is placing the runner vertically. The fall behavior doesn't need to modify the runner's horizontal velocity because — although it looks like the runner is moving from right to left (and left to right) — she never actually moves horizontally at all. Instead, the background moves beneath her to make it appear that she has horizontal motion.

When the game starts, Snail Bait invokes its equipRunner() method, which, among other things, equips the runner for both jumping and falling. The equipRunnerForFalling() method contains the implementation of the runner's fall() method. The runner's fall() method sets the runner's initial velocity, sets its falling attribute to true, and starts an animation timer to track how much time elapsed during the runner's fall.

When the runner's fall() method sets the runner's falling attribute to true, it trips a trigger in the runner's fall behavior. The runner then falls until the game resets the runner's falling attribute to false. The rest of this discussion of gravity focuses on the implementation of that behavior.


Incorporating gravity

Gravity is a special case

Gravity produces nonlinear motion, which I discuss in the seventh article in this series. In that article I implement nonlinear jumping by using time transducers that approximate gravity with ease-out and ease-in tweening functions. If you vary those transducers, you can produce an infinite spectrum of nonlinear motion, meaning that gravity is a special case of linear motion in general.

Near the Earth's surface, gravity accelerates falling objects at 9.81 meters per second per second, meaning that for every second something falls, its velocity increases by nearly 10 m/s (or 32 ft/s). The consequence of gravity for game developers is that in addition to calculating the position of sprites based on their velocity, you must also calculate their velocity when they are falling.

The math for calculating velocity under the influence of gravity is straightforward: Multiply the force of gravity by the sprite's elapsed fall time, and add that value to the sprite's initial velocity when it began to fall. As is often the case for equations, the confusing part is not the math but the units because the result of the preceding equation leaves you with meters per second. To make that number meaningful, Snail Bait converts it to pixels per second, using the following algorithm for calculating sprite positions:

  • At the beginning of the game:
    1. Define the width of the game in pixels.
    2. Arbitrarily define the width of the game in meters.
    3. Divide the width in pixels by the width in meters to get a ratio of pixels/meter.
  • Subsequently, for every animation frame:
    1. Use acceleration due to gravity (9.81 m/s/s) to calculate speed in meters/second.
    2. Multiply the speed in m/s by the pixels/meter ratio that is calculated in step 3 to get pixels/second.
    3. Calculate position based on the speed in pixels/second.

Now you can convert the preceding algorithm into code. The first step is to define the force of gravity and the game's pixels/meter ratio, as shown in Listing 3:

Listing 3. Constants pertaining to gravity and falling
var SnailBait = { // SnailBait constructor function
   ...
  
   this.GRAVITY_FORCE = 9.81,
   this.PIXELS_PER_METER = this.canvas.width / 10; // 10 meters, randomly selected width
   ...
}

When the runner runs off the end of a platform or collides with one from underneath, she begins to fall with no vertical velocity. When the runner does not land on a platform at the end of a jump, however, she begins to fall with the vertical velocity she had at the end of the jump's descent. Listing 4 shows how the runner's jump behavior uses the GRAVITY_FORCE and PIXELS_PER_METER constants that are defined in Listing 3 to calculate that initial velocity:

Listing 4. Falling at the end of a jump
this.jumpBehavior = {
   ...

   finishDescent: function (sprite) {
      sprite.stopJumping();

      if (snailBait.isOverPlatform(sprite) !== -1) {
         sprite.top = sprite.verticalLaunchPosition;
      }
      else {
         // The argument passed to the sprite's fall() method
         // is the sprite's initial velocity when it begins to fall
   
         sprite.fall(snailBait.GRAVITY_FORCE *
                     (sprite.descendAnimationTimer.getElapsedTime()/1000) *
                     snailBait.PIXELS_PER_METER);
      }
   },
};

The runner's vertical velocity at the end of her descent is the force of gravity times the elapsed descent time, times the game's pixel/meter ratio:

(9.81 m/s/s) * (elapsed descent time in seconds)* (800 pixels / 10 m)

That calculation results in pixels/second and represents the runner's vertical velocity at the end of her jump's descent. (See the sixth article in the series to see the rest of the jump behavior's implementation.)

The other place where Snail Bait calculates the runner's speed with GRAVITY_FORCE and PIXELS_PER_METER is in the runner's fall behavior, as shown in Listing 5:

Listing 5. Setting sprite velocity and calculating the runner's vertical drop for the current frame
this.fallBehavior = {
   ...
   setSpriteVelocity: function (sprite) {
      sprite.velocityY = sprite.initialVelocityY + 

                         snailBait.GRAVITY_FORCE *
                         (sprite.fallAnimationTimer.getElapsedTime()/1000) *
                         snailBait.PIXELS_PER_METER;
   },

   calculateVerticalDrop: function (sprite, fps) {
      return sprite.velocityY / fps;
   },
};

The fall behavior's setSpriteVelocity() method sets the runner's velocity depending on how long she has been falling. The method is careful to incorporate the runner's initial velocity that might have been set by the jump behavior in Listing 2.

The calculateVerticalDrop() method uses time-based motion — which I discuss in the Time-based motion section of the second article in this series — to calculate the runner's vertical drop based on the velocity that is calculated by setSpriteVelocity() and the current frame rate.

As I discussed at length in the fifth article in this series, Snail Bait iterates over all its sprites every animation frame. For each visible sprite, Snail Bait iterates over the sprite's behaviors, invoking each behavior's execute() method in turn. Listing 6 shows the execute() method for the runner's fall behavior:

Listing 6. The fall behavior's execute() method
this.fallBehavior = {
      execute: function (sprite, time, fps) { // sprite is the runner
         var deltaY;

         if (sprite.jumping) {
            return;
         }

         if (this.isOutOfPlay(sprite) || sprite.exploding) {
            if (sprite.falling) {
               sprite.stopFalling();
            }
            return;
         }
         
         if (!sprite.falling) {
            if (!sprite.exploding && !this.isPlatformUnderneath(sprite)) {
               sprite.fall();
            }
            return;

         }

         this.setSpriteVelocity(sprite);
         deltaY = this.calculateVerticalDrop(sprite, fps);
               
         if (!this.willFallBelowCurrentTrack(sprite, deltaY)) {
            sprite.top += deltaY;
         }
         else { // will fall below current track
            if (this.isPlatformUnderneath(sprite)) {
               this.fallOnPlatform(sprite);
               sprite.stopFalling();
            }
            else {
               sprite.top += deltaY;
               sprite.track--;
            }
         }
      }
   },

When the runner jumps, falls out of play, or explodes, the fall behavior's execute() method either initiates or stops falling and returns. If she falls out of play or explodes while falling, the method invokes her stopFalling() method. If the runner isn't falling, isn't currently exploding, and doesn't have a platform underneath her, the method invokes her fall() method.

With those prerequisites out of the way, the fall behavior's execute() method calculates the runner's current velocity and position. If that new position does not place the runner below her current platform, the method moves her there. Otherwise, the runner fell below her platform so the method checks to see if another platform is underneath her. If so, the method places her on that platform and stops the fall. If the runner falls below her platform and no platform is underneath her, the method moves her to her new position and decrements her current track.

The fall behavior's execute() method uses four convenience methods. The two methods in Listing 7 determine if the runner fell out of play or will fall below her current track:

Listing 7. Determining if the runner fell out of play or will fall below her current track
this.fallBehavior = {
   isOutOfPlay: function (sprite) {
      return sprite.top > snailBait.TRACK_1_BASELINE;
   },

   willFallBelowCurrentTrack: function (sprite, deltaY) {
      return sprite.top + sprite.height + deltaY >
             snailBait.calculatePlatformTop(sprite.track);
   },
   ...
};

The convenience methods in Listing 8 determine if a platform is underneath the runner, and lands the runner on a platform:

Listing 8. Determining if a platform is underneath the runner, and landing the runner on a platform
this.fallBehavior = {
   isPlatformUnderneath: function (sprite) {
      return snailBait.isOverPlatform(sprite) !== -1;
   },

   fallOnPlatform: function (sprite) {
      sprite.top = snailBait.calculatePlatformTop(sprite.track) - sprite.height;
      sprite.stopFalling();
   },
   ...
};

It's important to realize that the fall behavior's execute() method moves the runner vertically only when her falling attribute is true. The runner's stopFalling() method sets that attribute to false, as shown in Listing 9, in addition to setting the runner's vertical velocity to 0 and stopping the runner's fall animation timer:

Listing 9. The fall behavior's stopFalling() method
SnailBait.prototype = {
   ...

   equipRunnerForFalling: function () {
      ...

      this.runner.stopFalling = function () {
         this.falling = false;
         this.velocityY = 0;
         this.fallAnimationTimer.stop();
      }
   },
   ...
};

Pausing when the runner is falling

As I discuss in the Pausing behaviors section of the seventh article in this series, behaviors must implement pause() and unpause() methods so they can pause and resume in concert with the game as a whole. The runner's fall behavior meets that requirement, as shown in Listing 10:

Listing 10. Pausing and unpausing the fall behavior
 var SnailBait = function () {
   ...

   this.fallBehavior = {
      pause: function (sprite) {
         sprite.fallAnimationTimer.pause();
      },

      unpause: function (sprite) {
         sprite.fallAnimationTimer.unpause();
      },
   }
   ...
}

The fall behavior tracks elapsed time during falls with the runner's fall animation timer. Therefore, the behavior's pause() and unpause() methods simply pause and unpause that timer.

Now that you've seen how Snail Bait makes the runner fall, it's time to look at something totally unrelated: the game's sound.


Controlling sound effects and music

Snail Bait can play a music soundtrack and sound effects simultaneously. In the lower left of Figure 3 are the sound and music check boxes that let the user control whether the game plays sound effects, music, or both:

Figure 3. Sound and music controls
Screen capture of Snail Bait that shows the check boxes used to control sound and music

Listing 11 shows the HTML for the check boxes:

Listing 11. Snail Bait's sound and music check boxes
<div id='sound-and-music'>
   <div class='checkbox-div'>
      Sound <input id='sound-checkbox' type='checkbox' checked/>
   </div>
          
   <div class='checkbox-div'>
      Music <input id='music-checkbox' type='checkbox'/>
   </div>
</div>

You can use a check box's checked attribute (without a value) to control whether the check box is initially checked or not. If the attribute is present, the check box is initially checked; otherwise, it is not, as evidenced by Figure 3 and Listing 11.

In Listing 12, Snail Bait accesses the check box elements programmatically and maintains two variables — soundOn and musicOn — that the event handlers keep in sync with the check boxes. The music check box event handler also plays or pauses the game's music soundtrack, which (unlike the sound effects) constantly plays in the background.

Listing 12. Sound and music check box event handlers
var SnailBait = function () {
   ...
   this.soundCheckbox = document.getElementById('sound-checkbox');
   this.musicCheckbox = document.getElementById('music-checkbox');

   this.soundOn = this.soundCheckbox.checked;
   this.musicOn = this.musicCheckbox.checked;
   ... 
};
...

snailBait.soundCheckbox.onchange = function (e) {
   snailBait.soundOn = snailBait.soundCheckbox.checked;
};

snailBait.musicCheckbox.onchange = function (e) {
   snailBait.musicOn = snailBait.musicCheckbox.checked;

   if (snailBait.musicOn) {
      snailBait.soundtrack.play();
   }
   else {
      snailBait.soundtrack.pause();
   }
};

Snail Bait's startGame() method plays the soundtrack if the music is on, as shown in Listing 13:

Listing 13. Starting the game
SnailBait.prototype = {
SnailBait.prototype = {
   ...

   startGame: function () {
      if (this.musicOn) {
         this.soundtrack.play();
      }
      requestNextAnimationFrame(this.animate);
   },
   ...

I also modify the game's togglePaused() method to pause or play the soundtrack depending on whether the game is paused, as shown in Listing 14:

Listing 14. Pausing the music
SnailBait.prototype = {
   ...

   togglePaused: function () {
      ...

      if (this.paused && this.musicOn) {
         this.soundtrack.pause();
      }
      else if ( ! this.paused && this.musicOn) {
         this.soundtrack.play();
      }
   },
};

Implementing sound effects

Snail Bait plays sound effects at various points in the game — for example, when the runner collides with a bee or a bat — and sometimes must play multiple sounds simultaneously. The game uses HTML5 audio elements to implement sound effects.

HTML audio elements

Snail Bait creates an audio element for each of its sounds, as shown in Listing 15:

Listing 15. Snail Bait's audio elements
<!DOCTYPE html>
<html>
   <head>
      <title>Snail Bait</title>
      <link rel='stylesheet' href='game.css'/>
   </head>
 
   <body>
      <audio id='soundtrack'>
        <source src='sounds/soundtrack.mp3' type='audio/mp3'>
        <source src='sounds/soundtrack.ogg' type='audio/ogg'>
      </audio>

      <audio id='plop-sound' >
        <source src='sounds/plop.mp3' type='audio/mp3'>
        <source src='sounds/plop.ogg' type='audio/ogg'>
      </audio>

      <audio id='chimes-sound' >
        <source src='sounds/chimes.mp3' type='audio/mp3'>
        <source src='sounds/chimes.ogg' type='audio/ogg'>
      </audio>

      <audio id='whistle-down-sound' >
        <source src='sounds/whistledown.mp3' type='audio/mp3'>
        <source src='sounds/whistledown.ogg' type='audio/ogg'>
      </audio>

      <audio id='thud-sound' >
        <source src='sounds/thud.mp3' type='audio/mp3'>
        <source src='sounds/thud.ogg' type='audio/ogg'>
      </audio>

      <audio id='jump-sound' >
        <source src='sounds/jump.mp3' type='audio/mp3'>
        <source src='sounds/jump.ogg' type='audio/ogg'>
      </audio>

      <audio id='coin-sound' >
        <source src='sounds/coin.mp3' type='audio/mp3'>
        <source src='sounds/coin.ogg' type='audio/ogg'>
      </audio>

      <audio id='explosion-sound' >
        <source src='sounds/explosion.mp3' type='audio/mp3'>
        <source src='sounds/explosion.ogg' type='audio/ogg'>
      </audio>
      ...

  </body>
</html>

Inside each audio element, I specify two sound files in different formats; the browser selects the format that it can play. The MP3 and OGG formats cover all the bases for modern browsers; see Resources for a more information about HTML5 audio formats.

Additionally, Snail Bait accesses each audio element in JavaScript by using the document's getElementById() method, as shown in Listing 16:

Listing 16. Accessing Snail Bait's audio elements in JavaScript
SnailBait = function () {
   ...

   this.coinSound           = document.getElementById('coin-sound'),
   this.chimesSound         = document.getElementById('chimes-sound'),
   this.explosionSound      = document.getElementById('explosion-sound'),
   this.fallingWhistleSound = document.getElementById('whistle-down-sound'),
   this.plopSound           = document.getElementById('plop-sound'),
   this.jumpWhistleSound    = document.getElementById('jump-sound'),
   this.soundtrack          = document.getElementById('soundtrack'),
   this.thudSound           = document.getElementById('thud-sound'),
   ...

};

Volume

For every sound that it plays, Snail Bait defines a volume level, from 0.0 (silent) to 1.0 (full volume). Listing 17 shows the constants that Snail Bait defines — and that I determined empirically — for volumes:

Listing 17. Defining volume levels for Snail Bait's sounds
SnailBait = function () {
   // Sound-related constants

   this.COIN_VOLUME            = 1.0,
   this.CHIMES_VOLUME          = 1.0,
   this.EXPLOSION_VOLUME       = 0.25,
   this.FALLING_WHISTLE_VOLUME = 0.10,
   this.JUMP_WHISTLE_VOLUME    = 0.05,
   this.PLOP_VOLUME            = 0.20,
   this.SOUNDTRACK_VOLUME      = 0.12,
   this.THUD_VOLUME            = 0.20,
   ...

};

With references to audio elements and constants that represent volume levels in hand, Snail Bait initializes the audio elements when the game starts, as shown in Listing 18:

Listing 18. Setting volume levels for Snail Bait's sounds
SnailBait.prototype = {
   ...

   initializeSounds: function () {
      this.coinSound.volume           = this.COIN_VOLUME;
      this.chimesSound.volume         = this.CHIMES_VOLUME;
      this.explosionSound.volume      = this.EXPLOSION_VOLUME;
      this.fallingWhistleSound.volume = this.FALLING_WHISTLE_VOLUME;
      this.plopSound.volume           = this.PLOP_VOLUME;
      this.jumpWhistleSound.volume    = this.JUMP_WHISTLE_VOLUME;
      this.soundtrack.volume          = this.SOUNDTRACK_VOLUME;
      this.thudSound.volume           = this.LANDING_VOLUME;
   },

   start: function () {
      this.createSprites();
      this.initializeImages();
      this.initializeSounds();
      this.equipRunner();
      this.splashToast('Good Luck!');
      ...
   },
   ...   
   
};

Playing multiple sounds simultaneously

HTML5 audio elements have a straightforward API, including the following methods that Snail Bait uses to play sounds:

  • play()
  • pause()
  • load()

Snail Bait also uses the following audio element attributes:

  • currentTime
  • ended

You can see the use of all the preceding methods and attributes (except for pause(), which is used in Listing 12 and Listing 14) in Listing 19:

Listing 19. Playing sounds with Snail Bait's audio tracks
SnailBait = function () {
   ...
   this.soundOn = true,

   this.audioTracks = [ // 8 tracks is more than enough
      new Audio(), new Audio(), new Audio(), new Audio(), 
      new Audio(), new Audio(), new Audio(), new Audio()
   ],
   ...

   // Playing sounds.......................................................

   soundIsPlaying: function (sound) {
      return !sound.ended && sound.currentTime > 0;
   },

   playSound: function (sound) {
      var track, index;

      if (this.soundOn) {
         if (!this.soundIsPlaying(sound)) {
            sound.play();
         }
         else {
            for (i=0; index < this.audioTracks.length; ++index) {
               track = this.audioTracks[index];
            
               if (!this.soundIsPlaying(track)) {
                  track.src = sound.currentSrc;
                  track.load();
                  track.volume = sound.volume;
                  track.play();

                  break;
               }
            }
         }              
      }
   },
   ...
};

To play multiple sounds simultaneously, Snail Bait creates an array of eight audio elements. Snail Bait's playSound() method iterates over that array and uses the first audio element that's not currently playing to play the sound.

Realize that Snail Bait never plays sound effects through the original audio elements that are specified in HTML in Listing 15. Instead, the game plays sound effects through the eight audio elements that it creates programmatically in Listing 19. The game loads the sound from the original audio element into the programmatically created element and then plays the programmatically created element.


Playing Snail Bait's sound effects

Listings 20 through 23 show excerpts of the code that plays Snail Bait's sound effects at various points. I discussed all the code in those listings previously in this series, so I won't repeat those discussions. I left enough of the logic that surrounds calls to playSound() to impart the original context.

When the runner jumps, Snail Bat plays a whistling sound, as shown in Listing 20:

Listing 20. The sound the runner makes when she jumps
var SnailBait = function () {
   ...

   this.equipRunnerForJumping: function() {
       ...
      this.runner.jump = function () {
         if (this.jumping) // 'this' is the runner
            return;

         this.runAnimationRate = 0;
         this.jumping = true;
         this.verticalLaunchPosition = this.top;
         this.ascendAnimationTimer.start();

         snailBait.playSound(snailBait.jumpWhistleSound);
      };
   },

When sprites explode, Snail Bait plays the explosionSound, as shown in Listing 21:

Listing 21. Explosions
SnailBait.prototype = {
   ...

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

      this.playSound(this.explosionSound);
      this.explosionAnimator.start(sprite, true);  // true means sprite reappears
   },
   ...
};

When sprites land on a platform, they make a thud; when they fall below a track, they make a whistling sound (different from the jumping whistle):

Listing 22. Sounds associated with falling
SnailBait = function () {
   ...

   this.fallBehavior = {
      ...

      fallOnPlatform: function (sprite) {
         sprite.top = snailBait.calculatePlatformTop(sprite.track) - sprite.height;
         sprite.stopFalling();
         snailBait.playSound(snailBait.thudSound);
      },

      execute: function (sprite, time, fps) {
         var deltaY;

         if (!this.willFallBelowCurrentTrack(sprite, deltaY)) {
            sprite.top += deltaY;
         }
         else { // will fall below current track
            if (this.isPlatformUnderneath(sprite)) {
               this.fallOnPlatform(sprite);
               sprite.stopFalling();
            }
            else {
               sprite.track--;

               sprite.top += deltaY;

               if (sprite.track === 0) {
                  snailBait.playSound(snailBait.fallingWhistleSound);
               }
            }
         }
         ...
      }
   };
   ...
};

Collisions between sprites play various sounds, depending on which sprites are involved in the collision, as shown in Listing 23:

Listing 23. Collision sounds
var SnailBait =  function () {
   ...

   this.collideBehavior = {
      ...

      processCollision: function (sprite, otherSprite) {
         if (otherSprite.value) { // Modify Snail Bait sprites so they have values
            // Keep score...
         }

         if ('coin'  === otherSprite.type    ||
             'sapphire' === otherSprite.type ||
             'ruby' === otherSprite.type     || 
             'button' === otherSprite.type   ||
             'snail bomb' === otherSprite.type) {
            otherSprite.visible = false;

            if ('coin' === otherSprite.type) {
               snailBait.playSound(snailBait.coinSound);
            }
            if ('sapphire' === otherSprite.type || 'ruby' === otherSprite.type) {
               snailBait.playSound(snailBait.chimesSound);
            }
         }

         if ('bat' === otherSprite.type   ||
             'bee' === otherSprite.type   ||
             'snail' === otherSprite.type || 
             'snail bomb' === otherSprite.type) {
            snailBait.explode(sprite);
         }

         if (sprite.jumping && 'platform' === otherSprite.type) {
            this.processPlatformCollisionDuringJump(sprite, otherSprite);
         }
      },
   },

   processPlatformCollisionDuringJump: function (sprite, platform) {
      var isDescending = sprite.descendAnimationTimer.isRunning();

      sprite.stopJumping();

      if (isDescending) {
         sprite.track = platform.track; 
         sprite.top = snailBait.calculatePlatformTop(sprite.track) - sprite.height;
      }
      else { // Collided with platform while ascending
         snailBait.playSound(snailBait.plopSound);
         sprite.fall(); 
      }
   },
   ...
};

Next time

In the next installment, I'll wrap up the HTML5 2D game development series by discussing final game play and polish, such as transitions between lives and a game-ending animation.


Download

DescriptionNameSize
Sample codecode.zip11.9MB

Resources

Learn

Get products and technologies

  • Replica Island: You can download the source for this popular open source platform video game for Android. Most of Snail Bait's sprites are from Replica Island (used with permission).
  • Download IBM product evaluation versions and get your hands on application development tools and middleware products from DB2®, Lotus®, Rational®, Tivoli®, and WebSphere®.

Discuss

  • Get involved in the developerWorks community. Connect with other developerWorks users as you explore the developer-driven blogs, forums, groups, and wikis.

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Web development on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Web development, Java technology
ArticleID=933734
ArticleTitle=HTML5 2D game development: Implement gravity and add sound
publish-date=06212013