Click here to Skip to main content
14,122,675 members
Click here to Skip to main content
Add your own
alternative version

Stats

36.1K views
1.3K downloads
55 bookmarked
Posted 27 Apr 2014
Licenced CPOL

HTML Game using Canvas and Javascript

Rate this:
Please Sign up or sign in to vote.
This article is an effort to introduce beginners to HTML Canvas through a simple Game development.

Introduction

This article is seventh in the series of 12 articles that will take us through learning HTML5 and CSS3, specifically learning about Canvas and its uses through development of simple games called "Balloon Bob" and "Bobby Carter"

This article would introduce the below concepts and use them in our game alongside.

 

What is Canvas ?

The Canvas element was introduced as part of HTML 5 to enable us to dynamically render 2D images. The drawing is created via scripting using languages like Javascript. Some of the usual implementations include creating games, drawing graphs, making photo compositions, creating animations or even do real-time video processing or rendering. Today we will learn about Canvas by creating a simple Game on a web page.

Creating a Canvas

Canvas element is a resolution dependent bitmap canvas, it basically is a rectangle where we can use scripting to draw anything we like or want. If you are thinking how this element would look, then the answer is “nothing”. Canvas element in itself has no content or border. An example of creating a Canvas is as below.

<canvas id="mycanvas" width="100" height="100">Your Browser Doesn’t support Canvas Element</canvas>

With the ID attribute each canvas in the page can be identified via scripts like any other HTML elements and also each of the canvas on the page maintains its own state. The text between the Canvas tags is only displayed when the browser doesn’t recognize and support the Canvas element.

Browser Support:

Most of the modern browsers support the canvas element and below is a quick list of versions that this element was supported from.

  • Internet Explorer (9.0+)
  • Safari (3.0+)
  • Firefox (3.0+),
  • Chrome (3.0+)
  • Opera (10.0+)
  • iOS (1.0+)
  • Android (1.0+)

If you need to implement Canvas in IE with version less than 9 then there is a very nice solution called Explore Canvas.

And that’s it we created a Canvas on our page

Before we go any further let me tell you about the game we are going to build today because we are going to learn as much as possible about Canvas through building this game here.

The game is a simple breakout game. We would have some Balloons on the top section of our Canvas and a Trampoline below. We will need to bounce our Bob to break as many Balloons as possible. What better use in a game with Bob’s pointed head isn’t it . That’s it. Let’s call this game “Balloon Bob” :)

Let’s look at some concepts that need to be understood to build some graphics.

2D rendering Context:

The 2D Rendering Context or just the 2D context is part where we draw our shapes. When we say drawing on a Canvas we infact would be drawing on a context or in this case 2D rendering context which of course is accessible through the Canvas element. The canvas element in itself doesn’t provide any properties or methods to draw.

The getContext() method returns an object that provides methods and properties for drawing on the canvas.

This is all there is to know about this 2D Context, but it’s important to know as without the context we cannot complete our game :)

Canvas Coordinates and Paths

Canvas Co-ordinates:

In our previous article we learnt a lot about x’s, y’s and z’s. Well, that’s good because we can reuse some of that knowledge now. When drawing on a paper we essentially are drawing on a 2D plain or a 2D context. The 2D rendering context we are going to use is not much different, it uses the standard Cartesian coordinate system, below is a quick diagram of a 2D context with some points represented. Our Canvas basically is the 4th Quadrant where both x and y values are positive. Each unit of X and Y should be one pixel on our rendering screen in most cases.

Let’s take the first Step in our game creation and create a Canvas.

<div style="width:100%;height:100%;text-align:center">
    <canvas id="BalloonBob" ></canvas>
</div>

Remember one thing, Canvas always have to be defined with dimensions because by default it only gets a dimension of 300px * 150px (width * height). Using CSS to control the dimension property will only result in Canvas being scaled to those measurements. So let’s give 800px of width and 600px of Height. This means while drawing, our x can have a max value of 800 and y a value of 600.

<div style="width:100%;height:100%;text-align:center">
        <canvas id="scene" width="800" height="600" ></canvas>
</div>

Paths:

In a simple language, Paths on Canvas are a series of points with drawing instructions between those points.

Paths are used to create shapes including lines on a Canvas. With this you might already have understood that Path is VERY important. At any given time there can only be one Path on a Canvas.

Below are the key path functions.

  • beginPath()
  • closePath()
  • moveTo(x, y)
  • lineTo(x, y)
  • rect(x, y, width, height)
  • arc(x, y, radius, starting Angle, ending Angle, anticlockwiseFlag)
  • quadraticCurveTo(ctrlPtx, ctrlPty, x, y)
  • bezierCurveTo(ctrlPt1x, ctrlPt2y, ctrlPt2x, ctrlPt2y, x, y)
  • arcTo(xa, ya, xb, yb, radius)

We might not use all of these in our game but let’s quickly go over what these are.

If any of you have used LOGO (Logic Oriented Graphic Oriented) before and still remember TURTLE and its movements then it will come handy because drawing on canvas is no different than drawing in LOGO language.

Before we start on the path functions we need to learn about a function by name stroke().

The stroke() function :

This function actually draws the path we have defined with all those moveTo() , lineTo , rect and closePath() methods. The default color is black and can be changed with a context property by name strokeStyle which takes color/gradient/pattern as its value. For now let’s use colors and will cover the gradient and patterns later.

Syntax: context.strokeStyle=color|gradient|pattern;

A Path on Canvas is started and ended with the beginPath() and closePath() functions.

The beginPath() function:

This function starts a new path always by clearing out any existing path. We will see the meaning and uses of this function shortly.

The closePath() function:

closes the path by drawing a line from the current position back to the initial position. For Example to draw a square you could draw three sides and close the path which will in turn draw the fourth line.

The moveTo(x, y) function:

This moves our position to the given coordinates without drawing a line.

The lineTo(x, y) function:

This draws a line from whatever the current are to those specified in the arguments.

The rect(x, y, width, height) function:

This draws a rectangle at the X/Y coordinates that you give with the given width and height. Unloke fillRect() and strokeRect() functions this function only adds to the current path and does not paint on to the canvas immediately .Nothing is drawn until you call stroke() or fill().

The above ones are the simplest ones so I will try to cover them all in one example below.

Example:

The below code is first creating a Canvas by name myCanvas with width 250 and height 250, we are also adding a border so we can see where the canvas is.

Then we are adding a script section in which we are first getting the canvas Element through which we will obtain our context called ctx.

  • Step1: Beginning the path
  • Step2: We are drawing a rectangle with x:20, y:20, height:100 and width:150.
  • Step3: Moving to x:40 and y:20
  • Step4: Drawing a line from the current position to x:40 and y:120
  • Step5: Setting our stroke style to Green
  • Step6: closing the path and calling the stroke method which will create the above paths (Rectangle and a line)
  • Step7: We are repeating steps similar to above to draw a rectangle next but we are using the closePath() method to draw the last line.
<html>
<body>
<canvas id="HTMLCanvas" width="600" height="600" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas element. Please reopen the page in a supported browser</canvas>
<script>
var c=document.getElementById("HTMLCanvas");
var bobContext=c.getContext("2d");
bobContext.beginPath();
bobContext.rect(20,20,150,100);
bobContext.moveTo(40,20);
bobContext.lineTo(40,120);
bobContext.strokeStyle = "green";
bobContext.closePath();
bobContext.stroke();
bobContext.beginPath();
bobContext.moveTo(120,120);
bobContext.lineTo(120,200);
bobContext.strokeStyle = "RED";
bobContext.lineTo(170,200);
bobContext.closePath();
bobContext.stroke();
</script>
</body>

The arc(x, y, radius, startingAngle, endingAngle, anticlockwise) function:

Arc function is used to draw a circle or a half circle (ARC) of a give radius(3rd argument), the middle point is the X,Y (1st and 2nd arguments). The startingAngle and endingAngle are the angles at which the arc begins and ends.

The angles are measured in Radians and not degrees. A simple formula to calculate your radians in to divide your degrees by (180/Pi). Pi is approximately 3.14 and in Javascript we can use the constant Math.PI.

The Angle 0 is on the right side, which is the 3o clock position on a circle and the last argument anticlockwise dictates how the line will be drawn, this wouldn’t matter if we are drawing a full circle but when we are not then we will have to use anti clockwise to get your desired arc at the desired position.

Simple example below.

We are first drawing a full circle and some half circle with anticlockwise value true and false.

<script>
var c=document.getElementById("HTMLCanvas");
var bobContext=c.getContext("2d");
bobContext.beginPath();
bobContext.strokeStyle = "red";
bobContext.arc(60,75,50,0,2*Math.PI);
bobContext.stroke();
bobContext.closePath();
bobContext.beginPath();
bobContext.strokeStyle = "green";
bobContext.arc(60,125,40,1.2*Math.PI,1.8*Math.PI);
bobContext.stroke();
bobContext.closePath();
bobContext.beginPath();
bobContext.strokeStyle = "green";
bobContext.arc(180,75,50,1.2*Math.PI,1.8*Math.PI,false);
bobContext.stroke();
bobContext.closePath();
bobContext.beginPath();
bobContext.strokeStyle = "green";
bobContext.arc(300,75,50,1.2*Math.PI,1.8*Math.PI,true);
bobContext.stroke();
</script>

The quadraticCurveTo(cpx, cpy, x, y) function:

This function helps draw a quadratic curve. The origin position will be whatever position we currently are in and the end postion would be the second two coordinates given, the first two coordinates will be used as the control point.

A control point is what defines the curvature of aquadratic curve. It does so by creating two imaginary tangential lines which are connected to the origin point and the ending point. The origin point nothing but the current position we would be in, we can move to the right position using the moveto method.

Sharper curves are created by defining farther control points and Broader ones by defining them much closer.

Syntax: context.quadraticCurveTo(ctrlptx,ctrlpty,x,y);

Example1:

This will not create a curve as our control point is on the original path

var c=document.getElementById("HTMLCanvas");
var bobCtx=c.getContext("2d");
bobCtx.beginPath(); 
bobCtx.moveTo(5,1);                   // Create a origin point
bobCtx. quadraticCurveTo(5,5,5,9);    // Create a Quadratic Curve Path
bobCtx.stroke();                      // Stroke it on the Canvas

Example2:

var c=document.getElementById("HTMLCanvas");
var bobCtx=c.getContext("2d");
bobCtx.beginPath(); 
bobCtx.moveTo(5,1);                  // Create a origin point
bobCtx. quadraticCurveTo(9,6,5,9);   // Create a Quadratic Curve Path
bobCtx.stroke();                        // Stroke it on the Canvas

Example3:

var c=document.getElementById("HTMLCanvas");
var bobCtx=c.getContext("2d");
bobCtx.beginPath(); 
bobCtx.moveTo(5,1);                          // Create a origin point
bobCtx. quadraticCurveTo(9,2,5,9);    // Create a Quadratic Curve Path
bobCtx.stroke();                                // Stroke it on the Canvas

The bezierCurveTo(ctrlpt1x, ctrlpt1y, ctrlpt2x, ctrlpt2y, x, y) function:

This function helps draw a bezier curve. The origin point would be whatever position we are currently in and the end poistion would be the fifth and sixth coordinates speified. The first 4 coordinates are used as the control points.

Bezier curves are defined with the origin point, two control points, and an ending point.

As you can see the differece between quadratic curves and Bezier curves is the number of Control points used. The former uses one control point where as the latter uses with two control points. This helps us create more advanced curves.

  • The first curve is tangential to the line that can be drawn between the originpoint and the first control point.
  • The second curve is tangential to the line that can be drawn between the second control point and the ending point.
Syntax: context.bezierCurveTo(ctrlpt1x,ctrlpt1y,ctrlpt2x,ctrlpt2y,x,y);

Examples:

Example1: No change as the Control points are on the path

var c=document.getElementById("HTMLCanvas");
var bobContext=c.getContext("2d");
bobContext.beginPath(); 
bobContext.moveTo(5,1);           // Create a origin point
bobContext bezierCurveTo(5,5,7.5,5,5,1); // Create a Path
bobContext.stroke();                // Draw it
bobContext bezierCurveTo(5,5,7.5,5,5,1); //No change as the Control points are on the path

Example2:

var c=document.getElementById(HTMLCanvas");
var bobContext=c.getContext("2d");
bobContext.beginPath();
bobContext.moveTo(5,1);           // Create a origin point
bobContext.bezierCurveTo(9,6,9,8,5,1);  // Create a Path
bobContext.stroke();                // Draw it

Example3:

var c=document.getElementById("HTMLCanvas");
var bobContext=c.getContext("2d");
bobContext.beginPath(); 
bobContext.moveTo(5,1);           // Create a origin point
bobContext bezierCurveTo(9,2,1,8,5,1);  // Create a Path
bobContext.stroke();                // Draw it

Below is GIF image that shows the results of all three examples above.

The arcTo(x1, y1, x2, y2, radius) function:

This method mostly is a combination of ARC and Quadratic Curve functionality. Like the arc() method this one draws similar arcs using the current position as the start point with the given radius to [xb, yb] using [xa, ya] as a control point.

Synatax: context.arcTo(xa,ya,xb,yb,r);

Example:

var c=document.getElementById("HTMLCanvas");
var bobContext=c.getContext("2d");
bobContext.beginPath(); 
bobContext.moveTo(20,20);           // Create a starting point
bobContext.lineTo(100,20);          // Create a horizontal line
bobContext.arcTo(150,20,150,70,50); // Create an arc
bobContext.lineTo(150,120);         // Continue with vertical line
bobContext.stroke();                // Draw it

Alright..!! Now that we have understood the paths in Canvas, let’s start creating the shapes we need for our Game.

We already created a rectangle (800/600) Canvas above, time to start drawing on our canvas using the Script.

Creating a Trampoline for Bob to Jump on :

Our Trampoline will just be a rectangle. Before creating the Trampoline some quick Javascript setup for easy code reading. We will have the below Global Variables and you will see how we would use them as we go along. We shall call our Canvas drawArea and our Context as BobContext.

var drawArea, BobContext;
var gameHandle = 0;
var bRightBut = false;
var bLeftBut = false;
var objBob, objTrampoline, objTargetBalloons;
var playPoints = 0;
var tickTock;
var playTime = iMin = iSec = 0;
var prevTickTock, prevScore;
var curvature = (4 * (Math.sqrt(2) - 1)) / 3;
var w_factor = 0.0333;
var h_factor = 0.4;
var tie_w_factor = 0.12;
var tie_h_factor = 0.10;
var tie_curve_factor = 0.13;
var grad_factor = 0.3;
var grad_rad = 3;
var bob = new Image();
var refreshRate = 10;
var clickCounter = 1;
var refreshCounter = 0;
var hitX = -1;
var hitY = -1; var hitRad = -1;

We will also create a class for Trampoline so it would be easier to access its properties.

function Trampoline(x, w, h) {
    this.x = x; //X position
    this.w = w; //Width
    this.h = h; //Height
}


Clear method:

function clear() {
    BobContext.clearRect(0, 0, BobContext.canvas.width, BobContext.canvas.height);
    BobContext.fillStyle = '#fff';
    BobContext.fillRect(0, 0, BobContext.canvas.width, BobContext.canvas.height);
}


Since we need to animate this game we will refresh our screen every 10 milliseconds and during this refresh we will redraw our shapes in their new positions to create the illusion of movement.
To refresh screen every so unit of time Java script provides two easy methods.

They are explained below.

Javascript timer methods

The setInterval and clearInterval Methods

The setInterval method

This method executes specified method every specified milliseconds. This method returns a var which will be the handle of this timer which will be used to pause or stop the timer later.

Syntax: var1 = setInterval("javascript_method",milliseconds);

The clearInterval method

This method needs a handle that was received when we kicked off the timer and using that it would stop the timer and there will not be any new executions after that.

Syntax: clearInterval(var1)

We will also need to capture our keyboard keypress and mouse. We will use the below functions to capture the movements of the mouse within the Canvas area and also Key press events only if the keys pressed are either a Left Arrow or a Right Arrow.

  $(window).keydown(function (event) { // keyboard-down alerts
        switch (event.keyCode) {
            case 37: // 'Left' key
                bLeftBut = true;
                break;
            case 39: // 'Right' key
                bRightBut = true;
                break;
        }
    });
    $(window).keyup(function (event) { // keyboard-up alerts
        switch (event.keyCode) {
            case 37: // 'Left' key
                bLeftBut = false;
                break;
            case 39: // 'Right' key
                bRightBut = false;
                break;
        }
    });
var iCanvX1 = $(drawArea).offset().left;
    var iCanvX2 = iCanvX1 + width;
    $('#scene').mousemove(function (e) { // binding mousemove event
        if (e.pageX > iCanvX1 && e.pageX < iCanvX2) {
            objTrampoline.x = Math.max(e.pageX - iCanvX1 - (objTrampoline.w / 2), 0);
            objTrampoline.x = Math.min(BobContext.canvas.width - objTrampoline.w, objTrampoline.x);
        }
    });


If you see the above code you will notice that the Mouse Move event is directly changing the Trampoline X value, so when we refresh the screen every 10 milliseconds we would have our Trampoline drawn at the new x location. This way you will get a feeling that the trampoline is responding to Mouse movement in real time. And as you might have already guess the Y position of trampoline will be constant always.

Also for the keyboard events we are setting two vars called butLeft and butRight based on what key was pressed. We are going to used these conditions in our refresh method to recalculate our Trampoline X value. For instance if the left key was pressed then we will Move our Trampoline to the left by subtracting 50 px from its current X value.
Now let’s come to the Crux of our game. The refreshScreen method, this is where we would put all our drawing, calculation and logic. For now let’s create this method only to capture Trampoline’s position and draw the same accourding to the mouse movements and key press.

function refreshScreen() {
    clear();
    if (bRightBut)
        objTrampoline.x += 5; //Move Trampoline by 5 pixels
    else if (bLeftBut)
        objTrampoline.x -= 5; //Move Trampoline by 5 pixels
    BobContext.fillStyle = '#000';
    BobContext.beginPath();
    BobContext.rect(objTrampoline.x, BobContext.canvas.height - objTrampoline.h, objTrampoline.w, objTrampoline.h);
    BobContext.closePath();
}


Another two methods below to set the timer on load and call the above refreshScreen method every 10 milliseconds. The above Mouse and keyboard events will go into the init() method

//Document ready/On-Load function
$(function () {
    init();
});
//Initialisation
function init() {
    drawArea = document.getElementById('scene');
    BobContext = drawArea.getContext('2d');
    BobContext.clearRect(0, 0, drawArea.width, drawArea.height);
    var width = drawArea.width;
    var height = drawArea.height;
    objTrampoline = new Trampoline(width / 2, 120, 8); // new Trampoline object
    gameHandle = setInterval(refreshScreen, refreshRate); // loop re Draw Canvas
    $(window).keydown(function (event) { // keyboard-down alerts
        switch (event.keyCode) {
            case 37: // 'Left' key
                bLeftBut = true;
                break;
            case 39: // 'Right' key
                bRightBut = true;
                break;
        }
    });
    $(window).keyup(function (event) { // keyboard-up alerts
        switch (event.keyCode) {
            case 37: // 'Left' key
                bLeftBut = false;
                break;
            case 39: // 'Right' key
                bRightBut = false;
                break;
        }
    });
    var iCanvX1 = $(drawArea).offset().left;
    var iCanvX2 = iCanvX1 + width;
    $('#scene').mousemove(function (e) { // binding mousemove event
        if (e.pageX > iCanvX1 && e.pageX < iCanvX2) {
            objTrampoline.x = Math.max(e.pageX - iCanvX1 - (objTrampoline.w / 2), 0);
            objTrampoline.x = Math.min(BobContext.canvas.width - objTrampoline.w, objTrampoline.x);
        }
    });
}

That’s it, below is a GIF image showing how this will look now.

Let’s make some Balloons, for this we shall you’re the Bizier Curve method.

Below a Baloon class.

function Balloon(centerX, centerY, radius, color) {
    this.centerX = centerX;
    this.centerY = centerY;
    this.radius = radius * .85;
    this.baseColor = color;  
}

And a method to draw the Balloon. I have added comments inside the code to make it easier to understand.

Balloon.prototype.draw = function () {
    var centerX = this.centerX;
    var centerY = this.centerY;
    var radius = this.radius;
    var handleLength = curvature * radius; //Curvature = [(4 * (Math.sqrt(2) - 1)) / 3]
    var widthDiff = (radius * w_factor); //width factor is a constant of 0.333 defined as global
    var heightDiff = (radius * h_factor); //height factor is a constant of 0.4 defined as global -- Height will be larger than width to make it a Baloon else would become a circular balloon
    var balloonBottomY = centerY + radius + heightDiff; //Calculating the Bottom point of our Baloon by adding the centre Y, the Radies and the Height difference which was obtained from height factor
   
    BobContext.beginPath();
    // Section to draw the Top Left Curve
    var topLeftCurveStartX = centerX - radius;
    var topLeftCurveStartY = centerY;
    var topLeftCurveEndX = centerX;
    var topLeftCurveEndY = centerY - radius;
    BobContext.moveTo(topLeftCurveStartX, topLeftCurveStartY);
    BobContext.bezierCurveTo(topLeftCurveStartX, topLeftCurveStartY - handleLength - widthDiff,
        topLeftCurveEndX - handleLength, topLeftCurveEndY,
        topLeftCurveEndX, topLeftCurveEndY); // The 2 Control points are placed in a way to get a bigger arc on the top
    // Section to draw the Top Right Curve
    var topRightCurveStartX = centerX;
    var topRightCurveStartY = centerY - radius;
    var topRightCurveEndX = centerX + radius;
    var topRightCurveEndY = centerY;
    BobContext.bezierCurveTo(topRightCurveStartX + handleLength + widthDiff, topRightCurveStartY,
        topRightCurveEndX, topRightCurveEndY - handleLength,
        topRightCurveEndX, topRightCurveEndY);  // The 2 Control points are placed in a way to get a bigger arc on the top 
    // Section to draw the Bottom Right Curve
    var bottomRightCurveStartX = centerX + radius;
    var bottomRightCurveStartY = centerY;
    var bottomRightCurveEndX = centerX;
    var bottomRightCurveEndY = balloonBottomY;
    BobContext.bezierCurveTo(bottomRightCurveStartX, bottomRightCurveStartY + handleLength,
        bottomRightCurveEndX + handleLength, bottomRightCurveEndY,
        bottomRightCurveEndX, bottomRightCurveEndY);  // The 2 Control points are placed in a way to get a a smaller curve at the bottom
    // Section to draw the Bottom Left Curve
    var bottomLeftCurveStartX = centerX;
    var bottomLeftCurveStartY = balloonBottomY;
    var bottomLeftCurveEndX = centerX - radius;
    var bottomLeftCurveEndY = centerY;
    BobContext.bezierCurveTo(bottomLeftCurveStartX - handleLength, bottomLeftCurveStartY,
        bottomLeftCurveEndX, bottomLeftCurveEndY + handleLength,
        bottomLeftCurveEndX, bottomLeftCurveEndY);  // The 2 Control points are placed in a way to get a a smaller curve at the bottom
    BobContext.fillStyle = this.baseColor;
    BobContext.fill();
    // End balloon path
    // Create balloon tie
    var halfTieWidth = (radius * tie_w_factor) / 2;
    var tieHeight = (radius * tie_h_factor);
    var tieCurveHeight = (radius * tie_curve_factor);
    BobContext.beginPath();
    BobContext.moveTo(centerX - 1, balloonBottomY);
    BobContext.lineTo(centerX - halfTieWidth, balloonBottomY + tieHeight);
    BobContext.quadraticCurveTo(centerX, balloonBottomY + tieCurveHeight,
        centerX + halfTieWidth, balloonBottomY + tieHeight);
    BobContext.lineTo(centerX + 1, balloonBottomY); // Quadratic Curve to make a slightly curved triangle at the bottom
    BobContext.fill();
}

Now that we have the Balloon class and its draw method lets go ahead and create a Balloon with some 100 radius at 100,100 (x,y). Let’s choose the color Orange

var balloon1 = new Balloon(100, 100, 100, 'FF9900'); //create a object of Balloon class
balloon1.draw(0); //Call the method of Balloon class for the above object

Below is how it would look.

Yeah..!! It sure looks like a balloon but doesn’t really have the real Balloon look, does it?

To achieve that, HTML provides us with methods to create Gradients.

Canvas Gradients, Images

Gradient is similar to what we learnt in our previous article about CSS gradients. We have two Gradient methods for Canvas namely LinearGradient and RadialGradient.

createLinearGradient():

This method creates a gradient object. However, defining a gradient doesn’t draw anything on the canvas. It’s just an object stored in memory. To apply a gradient, you set your fillStyle to the gradient object created above and draw a shape, like a rectangle or a line.

Syntax: context.createLinearGradient(x1,y1,x2,y2);
Create Linear Gradient Arguments
Argument Description
x1 This is the x-coordinate value of the start of the gradient
y1 This is the y-coordinate value of the start of the gradient
x2 This is the x-coordinate value of the end of the gradient
y2 This is the y-coordinate value of the end of the gradient

However, we will also need to add color stops to really get our gradient effect. We will have to use the below method to do so.

addColorStop():

This method specifies the colors and position in a gradient object.

Syntax: gradient.addColorStop(stop,color);

Here the gradient is the object we created earlier using the createLinearGradient method.

Stop is a value between 0.0 and 1.0. Using these values we can define as many colors as we want on our gradient. Let’s look at an example which creates a Rainbow like gradient and applies it to a rectangle.

var c=document.getElementById("HTMLCanvas");
var bobContext=c.getContext("2d");
var grd=bobContext.createLinearGradient(0,0,200,0);
grd.addColorStop(0,"black");
grd.addColorStop("0.3","magenta");
grd.addColorStop("0.5","blue");
grd.addColorStop("0.6","green");
grd.addColorStop("0.8","yellow");
grd.addColorStop(1,"red");
bobContext.fillStyle=grd;
bobContext.fillRect(10,10,200,100);

Result will be as below

createRadialGradient():

This method creates a Radial or a circular gradient instead of a linear pattern

Syntax: context.createRadialGradient(x1,y1,r1,x2,y2,r2);
Create Radial Gradient Arguments
Argument Description
x1 This is the x-coordinate value of the start of the gradient
y1 This is the y-coordinate value of the start of the gradient
r1 The radius of the outermost circle
x2 This is the x-coordinate value of the end of the gradient
y2 This is the y-coordinate value of the end of the gradient
r2 The radius of the innermost circle

Like the linear Gradient even the Radial gradient will need a defined set of color stops.

So let’s use the above example and just change the linear gradient to Radial gradient by adding the radius. Radius 100 will define the outermost radius for the color defined at 1.0 stop and 10 will be the radius of the inner most color at 0.0 color stop.

var grd=bobContext.createRadialGradient(100,50,100,100,50,10);

And below will be the result. Since the radius of Black is 100 it has gone out of our rectangle which is not big enough to hold the whole gradient.



So now for our Balloon let’s use the Radial Gradient. All we need to do is apply darker base color on the outer edge and lighten is gradually as we move inward.

To get the lighter and Darker Color let’s use the below method which returns the lighter or Darker color code based on the value between 0 and 1 we pass.

function ColorLuminance(hex, lum) {
    // validate hex string
    hex = String(hex).replace(/[^0-9a-f]/gi, '');
    if (hex.length < 6) {
        hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
    }
    lum = lum || 0;
    // convert to decimal and change luminosity
    var rgb = "#",
        c, i;
    for (i = 0; i < 3; i++) {
        c = parseInt(hex.substr(i * 2, 2), 16);
        c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16);
        rgb += ("00" + c).substr(c.length);
    }
    return rgb;
}

And in our Balloon class lets add some data members to hold these lighter and darker color codes

function Balloon(centerX, centerY, radius, color) {
    this.centerX = centerX;
    this.centerY = centerY;
    this.radius = radius * .85;
    this.baseColor = ColorLuminance(color, 0);
    this.darkColor = ColorLuminance(color, -0.3);
    this.lightColor = ColorLuminance(color, 0.3);
}

Our Draw method should now create a gradient and apply the same to the balloon

var gradientOffset = (radius / 3);
var balloonGradient =
    BobContext.createRadialGradient(centerX + gradientOffset, centerY - gradientOffset,
        grad_rad,
        centerX, centerY, radius + heightDiff);
balloonGradient.addColorStop(0, this.lightColor);
balloonGradient.addColorStop(0.7, this.darkColor);
BobContext.fillStyle = balloonGradient;
BobContext.fill();

That’s it. Let’s see how our Balloon looks now.


Viola!! Now, we are talking. That looks like a 3D image than a 2D image, isn’t it ?

Since we need to have more balloons to target lets create an Array to hold all these Balloons and draw them next to each other in 5 rows. So our Array would be 5 rows and how many ever columns we can fit. Below is the TargetBalloons class that will hold an arraj of Balloon objects.

function TargetBalloons(w, h, r, c, p) {
    this.w = w;
    this.h = h;
    this.r = r; // Number of Rows
    this.c = c; // Number of Columns
    this.p = p; // padding
    this.objs; //Array of Balloon Objects
    this.colorCode; // Random colors for the balloons
    this.colors = ['9d9d9d', 'f80207', 'feff01', '0072ff', 'fc01fc', '03fe03', 'FF9900', '99CC00', '99FFFF', '330033', 'fff']; //Available colors for the balloons
}

Lets fill this array in our Init() method.

objTargetBalloons = new TargetBalloons((width / 8) - 1, 20, 6, 8, 2); // new TargetBalloons object
    objTargetBalloons.objs = new Array(objTargetBalloons.r); // fill-in TargetBalloons
    objTargetBalloons.colorCode = new Array(objTargetBalloons.r);
    for (i = 0; i < objTargetBalloons.r; i++) {
        objTargetBalloons.objs[i] = new Array(objTargetBalloons.c);
        objTargetBalloons.colorCode[i] = new Array(objTargetBalloons.c);
        for (j = 0; j < objTargetBalloons.c; j++) {
            objTargetBalloons.objs[i][j] = 1; //1= Active and 0=Burst/Inactive
            objTargetBalloons.colorCode[i][j] = Math.ceil(Math.random() * 11); //Select Random color code out of the 11 available colors
        }
    }

As the next step we will have to loop through this array and create our balloons inside our refreshScreen method.

// Create Balloons (from array of its objects)
for (i = 0, k = 25; i < objTargetBalloons.r; i++) {
    if (k == 0)
        k = 25; //Spacing for alternate rows
    else
        k = 0;
    for (j = 0; j < objTargetBalloons.c; j++)
        {
            var balloon1 = new Balloon(k + (j * (objTargetBalloons.w + objTargetBalloons.p)) + objTargetBalloons.p + objTargetBalloons.w / 4, (i * (objTargetBalloons.h + objTargetBalloons.p)) + objTargetBalloons.p,                  objTargetBalloons.w / 2, objTargetBalloons.colors[objTargetBalloons.colorCode[i][j]]);
             balloon1.draw();
            }
        }
    }
}

Since we now know about gradients lets also apply a linear Gradient to our Trampoline

// Create Trampoline
    var trampolineGradient = BobContext.createLinearGradient(objTrampoline.x, BobContext.canvas.height - objTrampoline.h,
                                                             objTrampoline.x + objTrampoline.w, (BobContext.canvas.height - objTrampoline.h));
    trampolineGradient.addColorStop(0, "gray");
    trampolineGradient.addColorStop(0.5, "white");
    trampolineGradient.addColorStop(1.0, "gray");
    BobContext.fillStyle = trampolineGradient;

We have a beautiful screen filled with Balloons.

Now that we have the stage ready let’s give our Bob a welcome to enter the arena to assist us bursting these balloons.

To draw images on canvas HTML provides a drawImage() method.

DrawImage():

As we saw earlier this method is used to draw images on Canvas. So without much ado let’s just straight into syntax and examples.

This method can take 3, 5 or 9 arguments.

drawImage(image, x, y) : This is the simplest of all the methods which takes a image and draws it at given x and y coordinates. The x and y will be the upper-left corner of the image. Values of x,y = (0, 0) would draw the image at the upper-left corner of the canvas. This will also draw the image at its original resolution

drawImage(image, x, y, w, h): This method does the same thing as above but it would scale the image down or up to the given width[w] and height[h].

drawImage(image, clipX, clipY, clipW, clipH, x, y, scaleW, scaleH) : This method takes an image, clips it to the rectangle (clipX, clipY, clipW, clipH), scales it to dimensions (scaleW, scaleH), and draws it on the canvas at coordinates (x, y).

Example:

var canvas = document.getElementById("e");
var context = canvas.getContext("2d");
var bob = new Image();
cat.src = "images/bob.png";
context.drawImage(bob, sx=2, sy=2, sw=3, sh=3, dx,=4 dy=4, dw=4, dh=4) 

Below is a representation of how the above example would be rendered

But what if you need to repeat your images all over canvas ?

For instance you need to create a background with images for your canvas you can do a loop to draw the same image over and over again until you fill the canvas. Well, HTML has a more easier and elegant approach.

It provides a method called createPattern() which repeats a specified element in a specified direction. In our case this element will be an image.

Syntax: context.createPattern(image,"repeat-value");

Parameter

Description

image

Defines the element which can be aimage, canvas, or avideo element that will be used for the pattern

Repeat-value

This parameter defines the repeating direction of the pattern on canvas. Below are the values that this parameter takes

repeat

This is the default value and it repeats the element both horizontally and vertically

repeat-x

The pattern repeats the element only horizontally

repeat-y

The pattern repeats the element only vertically

no-repeat

The pattern will display the element only once

Let’s quickly look at an example of creating a background with a small image which repeats both Horizantally and vertically.

Let’s add a variable called imgBG(image Background) and assign a image to the same in our init() method.

var imgBG = new Image(); 
imgBG.src = 'images/pattern.jpg';

And in our clear() method lets draw this image using createPattern() and drawimage() methods every time we finish clearing the screen.

function clear() {
    BobContext.clearRect(0, 0, BobContext.canvas.width, BobContext.canvas.height);
    var BGpattern = BobContext.createPattern(imgBG, 'repeat');
    BobContext.rect(0, 0, drawArea.width, drawArea.height);
    BobContext.fillStyle = BGpattern;
    BobContext.fill();
}

The result will look something like below

Time to get Bob into our canvas. Below is the class to hold Bob’s positions

function Bobby(x, y, dx, dy, r) {
    this.x = x; //x value
    this.y = y; //y value
    this.dx = dx; //change in x when moving/bouncing
    this.dy = dy; // change in y when moving/bouncing
    this.r = r; //radius of Bob’s presence
}

In our Init method lets instantiate a obj of the Bobby class and call the object objBob, for now our dx and dy will be 0 and radius will be 10. Bob’s initial position will bottom-centre of the Canvas.

objBob = new Bobby(width / 2, 550, 0, 0, 10);

In the refresh screen let’s actually draw Bob.

BobContext.drawImage(bob, objBob.x, objBob.y, 50, 50);

And now our screen will look like below

So we got most of the stuff done. Only thing now is to bounce Bob around and detect collisions with the wall and trampoline.

Below is the code which will detect collisions and change the dx and dy values so that bobby will be created at a new position on every refresh

First thing lets kick off Bob’s movement by giving a small dx and dy values while creating Bob’s object in the init() method.

objBob = new Bobby(width / 2, 550, 0.5, -5, 10); // new Bobby object

Now in our refreshscreen() method lets do some collision detection.

// Evaluate if a Wall or Trampoline was hit
iRowH = objTargetBalloons.h + objTargetBalloons.p;
iRow = Math.floor(objBob.y / iRowH);
iCol = Math.floor(objBob.x / (objTargetBalloons.w + objTargetBalloons.p));

// Hit a wall!! Reverse Bob's direction
if (objBob.x + objBob.dx + objBob.r > BobContext.canvas.width || objBob.x + objBob.dx - objBob.r < 0) {
    objBob.dx = -objBob.dx;
}

// Hit a Trampoline!! Reverse Bob's direction
if (objBob.y + objBob.dy - objBob.r < 0) {

    objBob.dy = -objBob.dy;
} else if (objBob.y + objBob.dy + objBob.r > BobContext.canvas.height - (objTrampoline.h + 25)) {
    if (objBob.x > objTrampoline.x && objBob.x < objTrampoline.x + objTrampoline.w) {
        objBob.dx = 10 * ((objBob.x - (objTrampoline.x + objTrampoline.w / 2)) / objTrampoline.w);
        objBob.dy = -objBob.dy;
    } else if (objBob.y + objBob.dy + objBob.r > BobContext.canvas.height) {
        clearInterval(gameHandle); //END GAME
    }
}

//If nothing was hit continue on Bob's current path
objBob.x += objBob.dx;
objBob.y += objBob.dy;


Notice that we have code in such a way that if Bob hits left half of the trampoline them we are creating an angled movement towards left of the canvas and similary to the right when he hits the right side. Below is the condition that does that.

objBob.dx = 10 * ((objBob.x - (objTrampoline.x + objTrampoline.w / 2))/ objTrampoline.w);

Our Bob’s jumping around in joy.. You can see how happy he is below.

The last part is to burst the balloons when hit. And it’s pretty simple because all we have to do is set the value of the balloon array element to 0 when hit. Since we have the balloons created in 8/5 grids its easy to detect if Bob has enetered that area and if yes just set the active value of that [i][j] to 0.

Below is the code. Let’s also create a var called playPoints and keep incrementing the same whenever a Balloon is hit.

// Burst Balloon if hit and reverse Bob's direction
if (objBob.y < objTargetBalloons.r * iRowH && iRow >= 0 && iCol >= 0 && objTargetBalloons.objs[iRow][iCol] == 1) {
objBob.dy = -objBob.dy; //reverse y direction of Bob
playPoints++; //Increase the score by one
objTargetBalloons.objs[i][j] = 0; //Set the active value of the Balloon value to 0 so that the Balloon will not be created when refreshing the next time in another 10 milliseconds
}

Sounds good, but let’s also experiment with some more methods of canvas namely Translate, Rotate and Scale.

Translate, Rotate and Scale methods

Translate

Just like in CSS even in Canvas Translate is used to move objects by x and y values. However, translate on a canvas means moving the whole canvas by the x and y value and anything drawn after the translate will be draw on the new canvas with a shift by x and y values.

Syntax: context.translate(x,y);

Example: Below is a quick representation of how a translate works.

Rotate

A simple definition would sound like “Rotate method rotates the canvas by a given angle” :)

Just like the translate rotate rotates the whole canvas and not the elements. Also the rangle of values for rotate would between -90 and +90 degrees as anything beyond that would rotate the canvas into other quadrants and our drawings will not be visible.

Syntax: context.rotate(angle);

The value of angle should be in radians. As mentioned earlier to convert our degrees to radians use the below formulae:

Radians = degrees*Math.PI/180.

For instance, to rotate 45 degrees, specify the following: Context.rotate(45*Math.PI/180);

Below is a quick representation of how a rotate works. Notice that anything outside the canvas(yellow box) is not displayed , the blue lines shows the 45 degree angle at which the canvas is rotated.

The last method in this section is Scale(x,y).

Scale

This method just like the other two methods impacts the whole canvas and when used would scale the canvas according to the x and y values. When x=2 and y=2 the canvas would be scaled to double the original size. Below is a representation of before and after the scale is applied to a Canvas.

Syntax: Context.scale(x,y);

To avoid all other drawing being shifted after using translate/scale/rotate method HTML provides two methods to restore the Canvas back to its original state.

Save and restore

Call save() before translate/rotate/scale and restore immediately after translate/rotate/scale so that ll our drawings after that will be drawn on the normal coordinates and not the translated/rotated/scaled ones.

Lastly, I would like to discuss about a property and a method related to drawing text on Canvas, so you can get back to playing with the code to make this game a even more cooler one

Font property and FillText method

Context.Font

The property is know as font and is applied to a Context. It sets or gets the font properties for text content on the canvas context.

The syntax is as below and is exactly same as the font property in CSS.

Syntax: context.font=font-style font-variant font-weight font-size font-face;

Example: context.font="italic small-caps bold 12px arial";

FillText() Method

This method is used to create or draw text on Canvas. The default color is black and if there was a Font property set before calling this method then the same would be taken into consideration.

Syntax: context.fillText(text,x,y,maxWidth);

Example:

var canvas1=document.getElementById("HTMLCanvas");
var bobContext=canvas1.getContext("2d");
bobContext.font="20px Georgia";
bobContext.fillText("Bob is Jumping!",20,70);
bobContext.font="30px Verdana";
// Let us reate gradient
var grd=bobContext.createLinearGradient(0,0,canvas1.width,0);
grd.addColorStop("0","Green");
grd.addColorStop("0.5","Red");
grd.addColorStop("1.0","Blue");
// set fill style to gradient
bobContext.fillStyle=grd;
bobContext.fillText("Bob is Jumping!",40,100);

I have used the above methods to move, shrink , rotate a balloon when hit by Bob and to display score and x,y co-ordinates of Bob when he is bouncing.

I am attaching the final code instead of adding it here to keep the length of this article under control :) . But below is quick GIF image to see how it looks on screen.

I have also added some functionality to pause on a single click on Canvas and a button to restart the game anytime. You may want to try adding multi lives for a single game and even multiplayer functionality.

Assignment: Game - Bobby Carter

As an assignment lets create a simple game called Bobby Carter

Our Bob has a new job now to collect all the carts on canvas.

However, as he collects the carts he has to pull them behind him and the cart train gets longer. The objective is to collect as many carts as possible without hitting his own cart train.

Try this and if you get stuck, you can download the solution attached at the top of this article.

Below is how my solution resulted in, due to time constrain it looks very simple but you are welcome to play with the code and make it better and cooler.

Using the code

Three simple steps to download, load and start playing the Game. The Java script files had most of the code and are located in the Script Sub folders of each Game.

  1. Download
  2. Unzip
  3. Open the HTML in Chrome (I haven't tested this on other browers. Hence, would recommend Chrome)

Points of Interest

I sure learnt one thing. Creating a GAME is much more fun than Playing one. I am sure you will feel the same too.

I would also like to thank the author, Franken Logan for helping me create perfect Balloons.

History

Initial creation of the article - Guru prasad K Basavaraju - 4/27/2014

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Guruprasad.K.Basavaraju
Software Developer (Senior)
United States United States
No Biography provided

You may also be interested in...

Pro

Comments and Discussions

 
-- There are no messages in this forum --
Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web05 | 2.8.190518.1 | Last Updated 17 Jul 2015
Article Copyright 2014 by Guruprasad.K.Basavaraju
Everything else Copyright © CodeProject, 1999-2019
Layout: fixed | fluid