In this recipe, we will implement a parallax effect for the game's background images. This effect causes the background to move at a slower rate than the foreground, which in return helps to give the illusion of depth within our game. More specifically this technique is achieved by placing multiple layers in front of each other and moving them at different speeds along the x axis.
To do this, we will need to implement a new object called
ScrollingBackground
with the following code:function ScrollingBackground() { this.width = 0; this.height = 0; this.deltaScroll = 1; this.InitScrollingBackground = function(texture, x, y, z, width, height, deltaScroll) { this.InitDrawableObject(texture, x, y, z); this.width = width; this.height = height; this.deltaScroll = deltaScroll; return this; } this.DisposeScrollingBackground = function() { this.DisposeDrawableObject(); }; this.UpdateBackground = function(canvas, position, fillArea, deltaPosition) { var left; var top; var width; var height; var xOffset = Math.abs(deltaPosition[0]) % this.texture.width; var yOffset = Math.abs(deltaPosition[1]) % this.texture.height; if(deltaPosition[0] < 0) left = this.texture.width - xOffset; else left = xOffset; if(deltaPosition[1] < 0) top = this.texture.height - yOffset; else top = yOffset; if(fillArea[0] < this.texture.width - left) width = fillArea[0]; else width = this.texture.width - left; if(fillArea[1] < this.texture.height - top) height = fillArea[1]; else height = this.texture.height - top; canvas.drawImage(this.texture, left, top, width, height, position[0], position[1], width, height); return [width, height]; } var background, position, fillArea, deltaPosition; this.Draw = function(deltaTime, canvas, deltaX, deltaY) { background = [0, 0]; for (var y = 0; y < this.height; y += background[1]) { for (var x = 0; x < this.width; x += background[0]) { position = [this.x + x, this.y + y]; fillArea = [this.width - x, this.height - y]; deltaPosition = [0, 0]; if (x === 0) { [0] = deltaX * this.deltaScroll; } if (y === 0) { deltaPosition[1] = deltaY * this.deltaScroll; } background = this.UpdateBackground(canvas, position, fillArea, deltaPosition); } } } } ScrollingBackground.prototype = new DrawableObject();
Before we can take advantage of this new object, we need to load the textures that will represent the background into our application.
var sky = new Image(); sky.src = "textures/sky.png"; var cloud = new Image(); cloud.src = "textures/cloud.png"; var mountain = new Image(); mountain.src = "textures/mountain.png"; var forest = new Image(); forest.src = "textures/forest.png";
With the
ScrollingBackground
object implemented and the required textures loaded, we then need to create a number of new instances of this object. Each of which will utilize the previously loaded textures that will be used to represent a layer within the background.this.sky = new ScrollingBackground().InitScrollingBackground(sky, 0, 0, 1, 800, 600, 0.5); this.cloud = new ScrollingBackground().InitScrollingBackground(cloud, 0, 0, 2, 800, 600, 0.5); this.mountain = new ScrollingBackground().InitScrollingBackground(mountain, 0, 0, 3, 800, 600, 0.75); this.forest = new ScrollingBackground().InitScrollingBackground(forest, 0, 0, 4, 800, 600, 0.9);
With each of the background layers implemented and positioned correctly our level should look similar to the following screenshot:
We begin by creating a new object called ScrollingBackground
, which is responsible for moving a 2D texture horizontally along the canvas. Using a series of scrolling layers we are able to produce a parallax effect that gives off the illusion of depth within the game. This is achieved by moving the background layer(s) at a slower speed than those layers that are closer to the foreground. As each layer moves off the canvas it then wraps back around to the other side of the canvas thus continually looping. This is beneficial to game development as it helps to reduce the amount of assets required for level design. It can also be said that it is more efficient than loading large textures into our application, which would result in a decrease in performance.
The object in question requires that we pass a texture, 2D position, depth position, width, height, and speed to it. The texture parameter refers to the texture that is loaded into the application and that will be used to represent a background layer. The 2D position indicates where the texture should start to be drawn on the canvas, for example, the top-left corner of the canvas, whereas the depth position indicates the position of the texture within the list of layers that make up the game. The width and height are used to find the point at which the texture should be drawn to, that is its end point. The width and height parameters can be used to draw a portion of a texture rather than the whole texture thus increasing the overall performance of the application.
Finally the speed parameter is used to indicate how fast the texture should move along the x axis. This movement is also determined by the deltaX
and deltaY
variables within the ScrollingBackground
object. Both of which are passed to the Draw
and Update
functions of the application and used to determine how far the texture has moved along the x axis. By knowing how far the texture has moved, we then know when to wrap the texture around the canvas using the UpdateBackground
function. This function wraps the texture so that it is drawn on the right-hand side of the canvas, giving the illusion of a continually looping background.
With the ScrollingBackground
object implemented, we then loaded each of the background layers into the application. This is done in much the same way as we have done in previous recipes. We declare a new image object and pass it the path of a texture for it to load into the application. A reference of this texture is then passed to a new instance of ScrollingBackground
.
As well as passing a texture, we also pass a 2D position, depth position, width, height, and a speed with which the texture should move. This creates a new background layer, which is drawn to the canvas and placed behind the level. This process is repeated a number of times and as a result creates a series of background layers, each of which differ in speed and help to produce a parallax effect.