In this 5th and for the moment last part of my series on creating an html5-canvas-game I’m going to write about adding animations and other elements to polish up the game a little bit.
The result:
Background Parallax
So we’re going to start off with the background – at the moment it’s looking kinda pale and you cannot really see any movement. A background can contain pretty much anything that doesn’t distract the player from the game but is also not too boring. In this example I will just create a simple grid. Add the following to the jumpy.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
self.createBgGrid = function(numX, numY) { var grid = new Container(); grid.snapToPixel = true; // calculating the distance between // the grid-lines var gw = w/numX; var gh = h/numY; // drawing the vertical line var verticalLine = new Graphics(); verticalLine.beginFill(Graphics.getRGB(101, 60, 176)); verticalLine.drawRect(0,0,gw * 0.02,gh*(numY+2)); var vs; // placing the vertical lines: // we're placing 1 more than requested // to have seamless scrolling later for ( var c = -1; c < numX+1; c++) { vs = new Shape(verticalLine); vs.snapToPixel = true; vs.x = c * gw; vs.y = -gh; grid.addChild(vs); } // drawing a horizontal line var horizontalLine = new Graphics(); horizontalLine.beginFill(Graphics.getRGB(101, 60, 176)); horizontalLine.drawRect(0,0,gw*(numX+1),gh * 0.02); var hs; // placing the horizontal lines: // we're placing 1 more than requested // to have seamless scrolling later for ( c = -1; c < numY+1; c++ ) { hs = new Shape(horizontalLine); hs.snapToPixel = true; hs.x = 0; hs.y = c * gh; grid.addChild(hs); } // return the grid-object return grid; } |
And when the game is initialized, don’t forget to add the grid to the stage:
1 2 3 4 5 6 7 8 |
// setting up 'constants' for the number of vertical // and horizontag background lines - play around with those values var GRID_HORIZONTAL = 8, GRID_VERTICAL = 4; // and the following will be added to 'self.initializeGame' background = self.createBgGrid(GRID_HORIZONTAL,GRID_VERTICAL); stage.addChild(background); |
To emphasize movement we will make the background move with about 50% of the world-movement creating a parallax-effect. We accomplish that by adding the following to the ‘self.tick’-method:
1 2 3 4 |
// the background 'moves' about 45% of the speed of the world-object // and it's position snaps back to zero once it's reached a limit background.x = (world.x * .45) % (w/GRID_HORIZONTAL); // horizontal background.y = (world.y * .45) % (h/GRID_VERTICAL); // vertical |
Note: This is pretty much the most simple way to make a background move seamlessly as the world moves, however if you have a more complex background you will probably have to use either a greater modulo(%) or handle you background-movement individually.
The changes above should already result in something similar to this:
Pretty simple – right?
More Background Parallax
To add more motion to the scene we will create just a couple white lines in the background, that are supposed to symbolize distant waterfalls. To create such a waterfall-line, we add the following method to the jumpy.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
self.createPixelLine = function(width) { // adjusting the width to the games scale // but not smaller than 1px and round it // to only have full pixels to render or // anti-aliasing will slow everything down width = Math.max(Math.round(width * scale),1); // drawing the line vl = new Graphics(); vl.beginFill(Graphics.getRGB(255,255,255)); vl.drawRect(0,0,width,h); lineShape = new Shape(vl); lineShape.snapToPixel = true; // the thinner the line, the less alpha // to make it look like it's further away lineShape.alpha = width * .25; // if it's further away, make it move slower lineShape.speedFactor = 0.3 + lineShape.alpha * 0.3 + Math.random() * 0.2; // the range defines when the line will be // moved back to the end of the screen lineShape.range = w + Math.random() * w * .3; // every line needs an offset, so they // don't start off at the same position lineShape.offsetX = Math.random() * w; return lineShape; } |
And when we initialize the game, we will just create 4(or how many we want) of those lines:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// create and add another background- // /parallax-layer background_2 = new Container(); stage.addChild(background_2); for ( var c = 0; c < 4; c++) { // create a line/ distant waterfall var line = self.createPixelLine((Math.random()*3+1)|0); // add it to the scene background_2.addChild(line); // and push it to the parallaxObjects // don't forget to initialize that array parallaxObjects.push(line); } |
And now we will make those lines move, just we like we made the grid move (it’s a little more complex this time, but the basic idea behind it, is the same):
1 2 3 4 5 6 7 8 |
l = parallaxObjects.length; for ( c = 0; c < l; c++ ) { p = parallaxObjects[c]; // just change the x-coordinate // a change in the y-coordinate would not have any // result, since it's just a white line p.x = ((world.x * p.speedFactor - p.offsetX) % p.range) + p.range; } |
And voila – now we got a pretty decent looking parallax-background already, of course with this way, it’s pretty simple to use just any graphic instead of white line, just add some vertcal-movement as well, and it should be good to go. The current result after adding all parallax elements looks like this:
Animations
Now we will bring some more life into the scene by adding some animations, starting with some waterfalls. The key to animations in EaselJS is pretty much the same like most other engines and platforms: SpriteSheets. A SpriteSheet is a graphic that contains all frames of the animation as stills. This is how the SpriteSheets of the waterfall, we are going to use, looks, it contains 3 frames:
The basic way for creating a SpriteSheet-Animation in EaselJS looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
// defining the spriteSheetData // this whole object can also be // stored in a JSON-file, however // by resizing my assets with my // custom algorithm this would make // things more complicated var waterFallSpriteSheetData = { images: [assets[WATERFALL_IMAGE]], frames: { // dimensions of the animation height: 256 * scale, width: 8 * scale, // number of frames count: 3 }, animations: { // we call this animation 'default' default: { // it contains all 3 frames frames:[0, 1, 2], // and it runs with a decent speed frequency: 5 } } } // ... // and now we can initialize an animation at any time var spriteSheet = new SpriteSheet(waterFallSpriteSheetData); var waterfall = new BitmapAnimation(spriteSheet); waterfall.snapToPixel = true; waterfall.gotoAndPlay('default'); |
CAUTION: EaselJS version 0.4.2 does NOT support Canvas-Elements in SpriteSheets, however our scaling-algorithm creates a Canvas-Element, meaning that we have to UPDATE our version of EaselJS – you can download the “NEXT-Version” on github.com/CreateJS/EaselJS – However, be aware, that this version uses namespacing, the simplest fix to get our game running with the “NEXT-Version” is to add the following into our index.html BEFORE loading EaselJS:
1 2 3 4 5 6 7 8 |
<script> var createjs = window; </script> <!-- now your can load the rest --> <script src="js/easeljs-NEXT.min.js"></script> <script src="js/utils.js"></script> <script src="js/hero.js"></script> <script src="js/jumpy.js"></script> |
Note: The SpriteSheet-Data CAN also be stored and loaded from a JSON – but in this case the SpriteSheet-Data contans dynamic values, like the size and the image, that’s why I prefer to just initialize the whole thing within a JS method. For a more detailed view on all the possible attributes of a SpriteSheet, please refer to: http://createjs.com/Docs/EaselJS/SpriteSheet.html.
As my next step I created a new layer in the front to add the waterfalls to and added a method that would add a waterfall to every platform with a probability of 35%. Since this is just placing and moving objects, I’m not going into detail about what lines of code I added where – for details please download and view the sourcecode.
As a final step I then added a fly to fly around our hero, it’s based on a simple 2-frame animation, and its movement is the following:
1 2 3 4 5 6 7 |
// the movement of the fly // calculate an offset to create a "swirling-like" behaviour fly.offsetX = ( Math.cos(ticks/10) * 10) * scale; fly.offsetY = ( Math.sin(ticks/ 7) * 5) * scale; // smoothly follow the hero by 10% of the distance every frame fly.x = fly.x + (hero.x - fly.x) * .1 + fly.offsetX; fly.y = fly.y + (hero.y - fly.y) * .1 + fly.offsetY; |
And as allways: Download the source.
Agenda
- Part 1: User-Input (Keystrokes, MouseClick, Touch) & Movement
- Part 2: Collisions between objects
- Part 3: Movement&More Collision
- Part 4: Adjustments for mobile devices
- Part 5: Polishing up the game with animations & eye candy
Pingback: Retro Style Platform Runner Game for mobile with EaselJS (Part 1) | indiegamr