Learn how to make games, using nothing but HTML and JavaScript.
Push the buttons to move the red square:
Example
function startGame() { myGamePiece = new component(30, 30, "red", 10, 120); myGamePiece.gravity = 0.05; myScore = new component("30px", "Consolas", "black", 280, 40, "text"); myGameArea.start(); } var myGameArea = { canvas : document.createElement("canvas"), start : function() { this.canvas.width = 480; this.canvas.height = 270; this.context = this.canvas.getContext("2d"); document.body.insertBefore(this.canvas, document.body.childNodes[0]); this.frameNo = 0; }, clear : function() { this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); } } <div id="canvascontainer"></div> The HTML<canvas>
element is displayed as a rectangular object on a web page: <script> var myGameArea; function startGame() { myGameArea = new gamearea(); } var x = 0, y = 0; function gamearea() { this.canvas = document.createElement("canvas"); this.canvas.width = 320; this.canvas.height = 180; document.getElementById("canvascontainer").appendChild(this.canvas); this.context = this.canvas.getContext("2d"); this.context.scale(1.4, 1.4); //var v = document.getElementById("video1"); var i; //v.addEventListener("play", function() { i = window.setInterval(function() { myGameArea.context.clearRect(0, 0, myGameArea.canvas.width, myGameArea.canvas.height); x++; var ctx = document.getElementsByTagName("canvas")[0].getContext("2d"); //ctx.drawImage(v,5,5,260,125); ctx.strokeStyle="#000000"; ctx.font="20px Georgia"; if (x > 0) { ctx.strokeText("You can do anything",10,30); } if (x > 50) { y += 0.5; ctx.strokeText("on the canvas",10,50+y); } if (x > 250) { clearInterval(i); var ctx = document.getElementsByTagName("canvas")[0].getContext("2d"); ctx.font="20px Georgia"; ctx.strokeText("Try it!",140,100); } },20)//;}, false); //v.addEventListener("pause", function() {window.clearInterval(i);}, false); //v.addEventListener("ended", function() { //}, false); } startGame(); </script> <h2>HTML Canvas</h2> The<canvas>
element is perfect for making games in HTML. The<canvas>
element offers all the functionality you need for making games. Use JavaScript to draw, write, insert images, and more, onto the<canvas>
..getContext("2d")
The<canvas>
element has a built-in object, called thegetContext("2d")
object, with methods and properties for drawing.Get Started
To make a game, start by creating a gaming area, and make it ready for drawing:Example
function startGame() { myGameArea.start(); } var myGameArea = { canvas : document.createElement("canvas"), start : function() { this.canvas.width = 480; this.canvas.height = 270; this.context = this.canvas.getContext("2d"); document.body.insertBefore(this.canvas, document.body.childNodes[0]); } } Try it Yourself » The objectmyGameArea
will have more properties and methods later in this tutorial. The functionstartGame()
invokes the methodstart()
of themyGameArea
object. Thestart()
method creates a<canvas>
element and inserts it as the first childnode of the<body>
element. <script> var myGameArea; var myGamePiece; function startGame() { myGameArea = new gamearea(); myGamePiece = new component(30, 30, "rgb(255, 0, 0)", 10, 75); } function gamearea() { this.canvas = document.createElement("canvas"); this.canvas.width = 320; this.canvas.height = 180; document.getElementById("canvascontainer").appendChild(this.canvas); this.context = this.canvas.getContext("2d"); } function component(width, height, color, x, y) { this.width = width; this.height = height; this.x = x; this.y = y; ctx = myGameArea.context; ctx.fillStyle = color; ctx.fillRect(this.x, this.y, this.width, this.height); } startGame(); </script>Add a Component
Make a component constructor, which lets you add components onto the gamearea. The object constructor is calledcomponent
, and we make our first component, calledmyGamePiece
:Example
var myGamePiece; function startGame() { myGameArea.start(); myGamePiece = new component(30, 30, "red", 10, 120);} function component(width, height, color, x, y) { this.width = width; this.height = height; this.x = x; this.y = y; ctx = myGameArea.context; ctx.fillStyle = color; ctx.fillRect(this.x, this.y, this.width, this.height); } Try it Yourself » The components have properties and methods to control their appearances and movements.Frames
To make the game ready for action, we will update the display 50 times per second, which is much like frames in a movie. First, create a new function calledupdateGameArea()
. In themyGameArea
object, add an interval which will run theupdateGameArea()
function every 20th millisecond (50 times per second). Also add a function calledclear()
, that clears the entire canvas. In thecomponent
constructor, add a function calledupdate()
, to handle the drawing of the component. TheupdateGameArea()
function calls theclear()
and theupdate()
method. The result is that the component is drawn and cleared 50 times per second:Example
var myGameArea = { canvas : document.createElement("canvas"), start : function() { this.canvas.width = 480; this.canvas.height = 270; this.context = this.canvas.getContext("2d"); document.body.insertBefore(this.canvas, document.body.childNodes[0]); this.interval = setInterval(updateGameArea, 20); }, clear : function() { this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); }} function component(width, height, color, x, y) { this.width = width; this.height = height; this.x = x; this.y = y; this.update = function(){ ctx = myGameArea.context; ctx.fillStyle = color; ctx.fillRect(this.x, this.y, this.width, this.height); }} function updateGameArea() { myGameArea.clear(); myGamePiece.update(); } Try it Yourself »Make it Move
To prove that the red square is being drawn 50 times per second, we will change the x position (horizontal) by one pixel every time we update the game area:Example
function updateGameArea() { myGameArea.clear(); myGamePiece.x += 1; myGamePiece.update(); } Try it Yourself »Why Clear The Game Area?
It might seem unnecessary to clear the game area at every update. However, if we leave out theclear()
method, all movements of the component will leave a trail of where it was positioned in the last frame:Example
function updateGameArea() { // myGameArea.clear(); myGamePiece.x += 1; myGamePiece.update(); } Try it Yourself »Change the Size
You can control the width and height of the component:Example
Create a 10x140 pixels rectangle: function startGame() { myGameArea.start(); myGamePiece = new component(140, 10, "red", 10, 120); } Try it Yourself »Change the Color
You can control the color of the component:Example
function startGame() { myGameArea.start(); myGamePiece = new component(30, 30, "blue", 10, 120); } Try it Yourself » You can also use other colorvalues like hex, rgb, or rgba:Example
function startGame() { myGameArea.start(); myGamePiece = new component(30, 30, "rgba(0, 0, 255, 0.5)", 10, 120); } Try it Yourself »Change the Position
We use x- and y-coordinates to position components onto the game area. The upper-left corner of the canvas has the coordinates (0,0) Mouse over the game area below to see its x and y coordinates: You can position the components wherever you like on the game area:Example
function startGame() { myGameArea.start(); myGamePiece = new component(30, 30, "red", 2, 2); } Try it Yourself »Many Components
You can put as many components as you like on the game area:Example
var redGamePiece, blueGamePiece, yellowGamePiece; function startGame() { redGamePiece = new component(75, 75, "red", 10, 10); yellowGamePiece = new component(75, 75, "yellow", 50, 60); blueGamePiece = new component(75, 75, "blue", 10, 110); myGameArea.start(); } function updateGameArea() { myGameArea.clear(); redGamePiece.update(); yellowGamePiece.update(); blueGamePiece.update(); } Try it Yourself »Moving Components
Make all three components move in different directions:Example
function updateGameArea() { myGameArea.clear(); redGamePiece.x += 1; yellowGamePiece.x += 1; yellowGamePiece.y += 1; blueGamePiece.x += 1; blueGamePiece.y -= 1; redGamePiece.update(); yellowGamePiece.update(); blueGamePiece.update(); } Try it Yourself » var myGameArea; var myGamePiece; function startGame() { myGameArea = new gamearea(); myGamePiece = new component(30, 30, "red", 10, 75); myGameArea.start(); } function gamearea() { this.canvas = document.createElement("canvas"); this.canvas.width = 320; this.canvas.height = 180; document.getElementById("canvascontainer").appendChild(this.canvas); this.context = this.canvas.getContext("2d"); this.start = function() { this.interval = setInterval(updateGameArea, 20); } this.clear = function(){ this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); } } function component(width, height, color, x, y) { this.width = width; this.height = height; this.x = x; this.y = y; this.speedX = 0; this.speedY = 0; this.update = function() { ctx = myGameArea.context; ctx.fillStyle = color; ctx.fillRect(this.x, this.y, this.width, this.height); } } function updateGameArea() { myGameArea.clear(); myGamePiece.x += myGamePiece.speedX; myGamePiece.y += myGamePiece.speedY; myGamePiece.update(); } function moveup(e) { myGamePiece.speedY = -1; } function movedown() { myGamePiece.speedY = 1; } function moveleft() { myGamePiece.speedX = -1; } function moveright() { myGamePiece.speedX = 1; } function clearmove(e) { myGamePiece.speedX = 0; myGamePiece.speedY = 0; } startGame();Get in Control
Now we want to control the red square. Add four buttons, up, down, left, and right. Write a function for each button to move the component in the selected direction. Make two new properties in thecomponent
constructor, and call themspeedX
andspeedY
. These properties are being used as speed indicators. Add a function in thecomponent
constructor, callednewPos()
, which uses thespeedX
andspeedY
properties to change the component's position. The newpos function is called from the updateGameArea function before drawing the component:Example
<script> function component(width, height, color, x, y) { this.width = width; this.height = height; this.speedX = 0; this.speedY = 0; this.x = x; this.y = y; this.update = function() { ctx = myGameArea.context; ctx.fillStyle = color; ctx.fillRect(this.x, this.y, this.width, this.height); } this.newPos = function() { this.x += this.speedX; this.y += this.speedY; } } function updateGameArea() { myGameArea.clear(); myGamePiece.newPos(); myGamePiece.update(); } function moveup() { myGamePiece.speedY -= 1; } function movedown() { myGamePiece.speedY += 1; } function moveleft() { myGamePiece.speedX -= 1; } function moveright() { myGamePiece.speedX += 1; } </script> <button onclick="moveup()">UP</button> <button onclick="movedown()">DOWN</button> <button onclick="moveleft()">LEFT</button> <button onclick="moveright()">RIGHT</button> Try it Yourself »Stop Moving
If you want, you can make the red square stop when you release a button. Add a function that will set the speed indicators to 0. To deal with both normal screens and touch screens, we will add code for both devices:Example
function stopMove() { myGamePiece.speedX = 0; myGamePiece.speedY = 0; } </script> <button onmousedown="moveup()" onmouseup="stopMove()" ontouchstart="moveup()">UP</button> <button onmousedown="movedown()" onmouseup="stopMove()" ontouchstart="movedown()">DOWN</button> <button onmousedown="moveleft()" onmouseup="stopMove()" ontouchstart="moveleft()">LEFT</button> <button onmousedown="moveright()" onmouseup="stopMove()" ontouchstart="moveright()">RIGHT</button> Try it Yourself »Keyboard as Controller
We can also control the red square by using the arrow keys on the keyboard. Create a method that checks if a key is pressed, and set thekey
property of themyGameArea
object to the key code. When the key is released, set thekey
property tofalse
:Example
var myGameArea = { canvas : document.createElement("canvas"), start : function() { this.canvas.width = 480; this.canvas.height = 270; this.context = this.canvas.getContext("2d"); document.body.insertBefore(this.canvas, document.body.childNodes[0]); this.interval = setInterval(updateGameArea, 20); window.addEventListener('keydown', function (e) { myGameArea.key = e.keyCode; }) window.addEventListener('keyup', function (e) { myGameArea.key = false; }) }, clear : function(){ this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); } } Then we can move the red square if one of the arrow keys are pressed:Example
function updateGameArea() { myGameArea.clear(); myGamePiece.speedX = 0; myGamePiece.speedY = 0; if (myGameArea.key && myGameArea.key == 37) {myGamePiece.speedX = -1; } if (myGameArea.key && myGameArea.key == 39) {myGamePiece.speedX = 1; } if (myGameArea.key && myGameArea.key == 38) {myGamePiece.speedY = -1; } if (myGameArea.key && myGameArea.key == 40) {myGamePiece.speedY = 1; } myGamePiece.newPos(); myGamePiece.update(); } Try it Yourself »Multiple Keys Pressed
What if more than one key is pressed at the same time? In the example above, the component can only move horizontally or vertically. Now we want the component to also move diagonally. Create akeys
array for themyGameArea
object, and insert one element for each key that is pressed, and give it the valuetrue
, the value remains true untill the key is no longer pressed, the value becomesfalse
in the keyup event listener function:Example
var myGameArea = { canvas : document.createElement("canvas"), start : function() { this.canvas.width = 480; this.canvas.height = 270; this.context = this.canvas.getContext("2d"); document.body.insertBefore(this.canvas, document.body.childNodes[0]); this.interval = setInterval(updateGameArea, 20); window.addEventListener('keydown', function (e) { myGameArea.keys = (myGameArea.keys || []); myGameArea.keys[e.keyCode] = true; }) window.addEventListener('keyup', function (e) { myGameArea.keys[e.keyCode] = false; }) }, clear : function(){ this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); } } function updateGameArea() { myGameArea.clear(); myGamePiece.speedX = 0; myGamePiece.speedY = 0; if (myGameArea.keys && myGameArea.keys[37]) {myGamePiece.speedX = -1; } if (myGameArea.keys && myGameArea.keys[39]) {myGamePiece.speedX = 1; } if (myGameArea.keys && myGameArea.keys[38]) {myGamePiece.speedY = -1; } if (myGameArea.keys && myGameArea.keys[40]) {myGamePiece.speedY = 1; } myGamePiece.newPos(); myGamePiece.update(); } Try it Yourself »Using The Mouse Cursor as a Controller
If you want to control the red square by using the mouse cursor, add a method inmyGameArea
object that updates the x and y coordinates of the mouse cursor:.Example
var myGameArea = { canvas : document.createElement("canvas"), start : function() { this.canvas.width = 480; this.canvas.height = 270; this.canvas.style.cursor = "none"; //hide the original cursor this.context = this.canvas.getContext("2d"); document.body.insertBefore(this.canvas, document.body.childNodes[0]); this.interval = setInterval(updateGameArea, 20); window.addEventListener('mousemove', function (e) { myGameArea.x = e.pageX; myGameArea.y = e.pageY; }) }, clear : function(){ this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); } } Then we can move the red square using the mouse cursor:Example
function updateGameArea() { myGameArea.clear(); if (myGameArea.x && myGameArea.y) { myGamePiece.x = myGameArea.x; myGamePiece.y = myGameArea.y; } myGamePiece.update(); } Try it Yourself »Touch The Screen to Control The Game
We can also control the red square on a touch screen. Add a method in themyGameArea
object that uses the x and y coordinates of where the screen is touched:Example
var myGameArea = { canvas : document.createElement("canvas"), start : function() { this.canvas.width = 480; this.canvas.height = 270; this.context = this.canvas.getContext("2d"); document.body.insertBefore(this.canvas, document.body.childNodes[0]); this.interval = setInterval(updateGameArea, 20); window.addEventListener('touchmove', function (e) { myGameArea.x = e.touches[0].screenX; myGameArea.y = e.touches[0].screenY; }) }, clear : function(){ this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); } } Then we can move the red square if the user touches the screen, by using the same code as we did for the mouse cursor:Example
function updateGameArea() { myGameArea.clear(); if (myGameArea.x && myGameArea.y) { myGamePiece.x = myGameArea.x; myGamePiece.y = myGameArea.y; } myGamePiece.update(); } Try it Yourself »Controllers on The Canvas
We can also draw our own buttons on the canvas, and use them as controllers:Example
function startGame() { myGamePiece = new component(30, 30, "red", 10, 120); myUpBtn = new component(30, 30, "blue", 50, 10); myDownBtn = new component(30, 30, "blue", 50, 70); myLeftBtn = new component(30, 30, "blue", 20, 40); myRightBtn = new component(30, 30, "blue", 80, 40); myGameArea.start(); } Add a new function that figures out if a component, in this case a button, is clicked. Start by adding event listeners to check if a mouse button is clicked (mousedown
andmouseup
). To deal with touch screens, also add event listeners to check if the screen is clicked on (touchstart
andtouchend
):Example
var myGameArea = { canvas : document.createElement("canvas"), start : function() { this.canvas.width = 480; this.canvas.height = 270; this.context = this.canvas.getContext("2d"); document.body.insertBefore(this.canvas, document.body.childNodes[0]); this.interval = setInterval(updateGameArea, 20); window.addEventListener('mousedown', function (e) { myGameArea.x = e.pageX; myGameArea.y = e.pageY; }) window.addEventListener('mouseup', function (e) { myGameArea.x = false; myGameArea.y = false; }) window.addEventListener('touchstart', function (e) { myGameArea.x = e.pageX; myGameArea.y = e.pageY; }) window.addEventListener('touchend', function (e) { myGameArea.x = false; myGameArea.y = false; }) }, clear : function(){ this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); } } Now themyGameArea
object has properties that tells us the x- and y-coordinates of a click. We use these properties to check if the click was performed on one of our blue buttons. The new method is calledclicked
, it is a method of thecomponent
constructor, and it checks if the component is being clicked. In theupdateGameArea
function, we take the neccessarry actions if one of the blue buttons is clicked:Example
function component(width, height, color, x, y) { this.width = width; this.height = height; this.speedX = 0; this.speedY = 0; this.x = x; this.y = y; this.update = function() { ctx = myGameArea.context; ctx.fillStyle = color; ctx.fillRect(this.x, this.y, this.width, this.height); } this.clicked = function() { var myleft = this.x; var myright = this.x + (this.width); var mytop = this.y; var mybottom = this.y + (this.height); var clicked = true; if ((mybottom < myGameArea.y) || (mytop > myGameArea.y) || (myright < myGameArea.x) || (myleft > myGameArea.x)) { clicked = false; } return clicked; } } function updateGameArea() { myGameArea.clear(); if (myGameArea.x && myGameArea.y) { if (myUpBtn.clicked()) { myGamePiece.y -= 1; } if (myDownBtn.clicked()) { myGamePiece.y += 1; } if (myLeftBtn.clicked()) { myGamePiece.x += -1; } if (myRightBtn.clicked()) { myGamePiece.x += 1; } } myUpBtn.update(); myDownBtn.update(); myLeftBtn.update(); myRightBtn.update(); myGamePiece.update(); } Try it Yourself » <script> var myGameArea; var myGamePiece; var myObstacles = []; function startGame() { myGameArea = new gamearea(); myGamePiece = new component(30, 30, "red", 10, 75); myGameArea.start(); } function gamearea() { this.canvas = document.createElement("canvas"); this.canvas.width = 320; this.canvas.height = 180; document.getElementById("canvascontainer").appendChild(this.canvas); this.context = this.canvas.getContext("2d"); this.pause = false; this.frameNo = 0; this.start = function() { this.interval = setInterval(updateGameArea, 20); } this.stop = function() { clearInterval(this.interval); this.pause = true; } this.clear = function(){ this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); } } function component(width, height, color, x, y) { this.width = width; this.height = height; this.speedX = 0; this.speedY = 0; this.x = x; this.y = y; this.update = function() { ctx = myGameArea.context; ctx.fillStyle = color; ctx.fillRect(this.x, this.y, this.width, this.height); } this.crashWith = function(otherobj) { var myleft = this.x; var myright = this.x + (this.width); var mytop = this.y; var mybottom = this.y + (this.height); var otherleft = otherobj.x; var otherright = otherobj.x + (otherobj.width); var othertop = otherobj.y; var otherbottom = otherobj.y + (otherobj.height); var crash = true; if ((mybottom < othertop) || (mytop > otherbottom) || (myright < otherleft) || (myleft > otherright)) { crash = false; } return crash; } } function updateGameArea() { var x, y, min, max, height, gap; for (i = 0; i < myObstacles.length; i += 1) { if (myGamePiece.crashWith(myObstacles[i])) { myGameArea.stop(); } } if (myGameArea.pause == false) { myGameArea.clear(); myGameArea.frameNo += 1; if (myGameArea.frameNo == 1 || everyinterval(100)) { x = myGameArea.canvas.width; y = myGameArea.canvas.height - 100; min = 20; max = 100; height = Math.floor(Math.random()*(max-min+1)+min); min = 50; max = 100; gap = Math.floor(Math.random()*(max-min+1)+min); myObstacles.push(new component(10, height, "green", x, 0)); myObstacles.push(new component(10, x - height - gap, "green", x, height + gap)); } for (i = 0; i < myObstacles.length; i += 1) { myObstacles[i].x += -1; myObstacles[i].update(); } myGamePiece.x += myGamePiece.speedX; myGamePiece.y += myGamePiece.speedY; myGamePiece.update(); } } function everyinterval(n) { if ((myGameArea.frameNo / n) % 1 == 0) {return true;} return false; } function moveup(e) { myGamePiece.speedY = -1; } function movedown() { myGamePiece.speedY = 1; } function moveleft() { myGamePiece.speedX = -1; } function moveright() { myGamePiece.speedX = 1; } function clearmove(e) { myGamePiece.speedX = 0; myGamePiece.speedY = 0; } startGame(); </script>Add Some Obstacles
Now we want to add some obstacles to our game. Add a new component to the gaming area. Make it green, 10px wide, 200px high, and place it 300px to the right and 120px down. Also update the obstacle component in every frame:Example
var myGamePiece; var myObstacle; function startGame() { myGamePiece = new component(30, 30, "red", 10, 120); myObstacle = new component(10, 200, "green", 300, 120); myGameArea.start(); } function updateGameArea() { myGameArea.clear(); myObstacle.update(); myGamePiece.newPos(); myGamePiece.update(); } Try it Yourself »Hit The Obstacle = Game Over
In the example above, nothing happens when you hit the obstacle. In a game, that is not very satisfying. How do we know if our red square hits the obstacle? Create a new method in the component constructor, that checks if the component crashes with another component. This method should be called every time the frames updates, 50 times per second. Also add astop()
method to themyGameArea
object, which clears the 20 milliseconds interval.Example
var myGameArea = { canvas : document.createElement("canvas"), start : function() { this.canvas.width = 480; this.canvas.height = 270; this.context = this.canvas.getContext("2d"); document.body.insertBefore(this.canvas, document.body.childNodes[0]); this.interval = setInterval(updateGameArea, 20); }, clear : function() { this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); }, stop : function() { clearInterval(this.interval); } } function component(width, height, color, x, y) { this.width = width; this.height = height; this.speedX = 0; this.speedY = 0; this.x = x; this.y = y; this.update = function() { ctx = myGameArea.context; ctx.fillStyle = color; ctx.fillRect(this.x, this.y, this.width, this.height); } this.newPos = function() { this.x += this.speedX; this.y += this.speedY; } this.crashWith = function(otherobj) { var myleft = this.x; var myright = this.x + (this.width); var mytop = this.y; var mybottom = this.y + (this.height); var otherleft = otherobj.x; var otherright = otherobj.x + (otherobj.width); var othertop = otherobj.y; var otherbottom = otherobj.y + (otherobj.height); var crash = true; if ((mybottom < othertop) || (mytop > otherbottom) || (myright < otherleft) || (myleft > otherright)) { crash = false; } return crash; } } function updateGameArea() { if (myGamePiece.crashWith(myObstacle)) { myGameArea.stop(); } else { myGameArea.clear(); myObstacle.update(); myGamePiece.newPos(); myGamePiece.update(); } } Try it Yourself »Moving Obstacle
The obstacle is of no danger when it is static, so we want it to move. Change the property value ofmyObstacle.x
at every update:Example
function updateGameArea() { if (myGamePiece.crashWith(myObstacle)) { myGameArea.stop(); } else { myGameArea.clear(); myObstacle.x += -1; myObstacle.update(); myGamePiece.newPos(); myGamePiece.update(); } } Try it Yourself »Multiple Obstacles
How about adding multiple obstacles? For that we need a property for counting frames, and a method for execute something at a given frame rate.Example
var myGameArea = { canvas : document.createElement("canvas"), start : function() { this.canvas.width = 480; this.canvas.height = 270; this.context = this.canvas.getContext("2d"); document.body.insertBefore(this.canvas, document.body.childNodes[0]); this.frameNo = 0; this.interval = setInterval(updateGameArea, 20); }, clear : function() { this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); }, stop : function() { clearInterval(this.interval); } } function everyinterval(n) { if ((myGameArea.frameNo / n) % 1 == 0) {return true;} return false; } The everyinterval function returns true if the current framenumber corresponds with the given interval. To define multiple obstacles, first declare the obstacle variable as an array. Second, we need to make some changes in the updateGameArea function.Example
var myGamePiece; var myObstacles = []; function updateGameArea() { var x, y; for (i = 0; i < myObstacles.length; i += 1) { if (myGamePiece.crashWith(myObstacles[i])) { myGameArea.stop(); return; } } myGameArea.clear(); myGameArea.frameNo += 1; if (myGameArea.frameNo == 1 || everyinterval(150)) { x = myGameArea.canvas.width; y = myGameArea.canvas.height - 200 myObstacles.push(new component(10, 200, "green", x, y)); } for (i = 0; i < myObstacles.length; i += 1) { myObstacles[i].x += -1; myObstacles[i].update(); } myGamePiece.newPos(); myGamePiece.update(); } Try it Yourself » In theupdateGameArea
function we must loop through every obstacle to see if there is a crash. If there is a crash, theupdateGameArea
function will stop, and no more drawing is done. TheupdateGameArea
function counts frames and adds an obstacle for every 150th frame.Obstacles of Random Size
To make the game a bit more difficult, and fun, we will send in obstacles of random sizes, so that the red square must move up and down to not crash.Example
function updateGameArea() { var x, height, gap, minHeight, maxHeight, minGap, maxGap; for (i = 0; i < myObstacles.length; i += 1) { if (myGamePiece.crashWith(myObstacles[i])) { myGameArea.stop(); return; } } myGameArea.clear(); myGameArea.frameNo += 1; if (myGameArea.frameNo == 1 || everyinterval(150)) { x = myGameArea.canvas.width; minHeight = 20; maxHeight = 200; height = Math.floor(Math.random()*(maxHeight-minHeight+1)+minHeight); minGap = 50; maxGap = 200; gap = Math.floor(Math.random()*(maxGap-minGap+1)+minGap); myObstacles.push(new component(10, height, "green", x, 0)); myObstacles.push(new component(10, x - height - gap, "green", x, height + gap)); } for (i = 0; i < myObstacles.length; i += 1) { myObstacles[i].x += -1; myObstacles[i].update(); } myGamePiece.newPos(); myGamePiece.update(); } Try it Yourself » <script> var myGameArea; var myGamePiece; var myObstacles = []; var myscore; function restartGame() { document.getElementById("myfilter").style.display = "none"; document.getElementById("myrestartbutton").style.display = "none"; myGameArea.stop(); myGameArea.clear(); myGameArea = {}; myGamePiece = {}; myObstacles = []; myscore = {}; document.getElementById("canvascontainer").innerHTML = ""; startGame() } function startGame() { myGameArea = new gamearea(); myGamePiece = new component(30, 30, "red", 10, 75); myscore = new component("15px", "Consolas", "black", 220, 25, "text"); myGameArea.start(); } function gamearea() { this.canvas = document.createElement("canvas"); this.canvas.width = 320; this.canvas.height = 180; document.getElementById("canvascontainer").appendChild(this.canvas); this.context = this.canvas.getContext("2d"); this.pause = false; this.frameNo = 0; this.start = function() { this.interval = setInterval(updateGameArea, 20); } this.stop = function() { clearInterval(this.interval); this.pause = true; } this.clear = function(){ this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); } } function component(width, height, color, x, y, type) { this.type = type; if (type == "text") { this.text = color; } this.score = 0; this.width = width; this.height = height; this.speedX = 0; this.speedY = 0; this.x = x; this.y = y; this.update = function() { ctx = myGameArea.context; if (this.type == "text") { ctx.font = this.width + " " + this.height; ctx.fillStyle = color; ctx.fillText(this.text, this.x, this.y); } else { ctx.fillStyle = color; ctx.fillRect(this.x, this.y, this.width, this.height); } } this.crashWith = function(otherobj) { var myleft = this.x; var myright = this.x + (this.width); var mytop = this.y; var mybottom = this.y + (this.height); var otherleft = otherobj.x; var otherright = otherobj.x + (otherobj.width); var othertop = otherobj.y; var otherbottom = otherobj.y + (otherobj.height); var crash = true; if ((mybottom < othertop) || (mytop > otherbottom) || (myright < otherleft) || (myleft > otherright)) { crash = false; } return crash; } } function updateGameArea() { var x, y, min, max, height, gap; for (i = 0; i < myObstacles.length; i += 1) { if (myGamePiece.crashWith(myObstacles[i])) { myGameArea.stop(); document.getElementById("myfilter").style.display = "block"; document.getElementById("myrestartbutton").style.display = "block"; return; } } if (myGameArea.pause == false) { myGameArea.clear(); myGameArea.frameNo += 1; myscore.score +=1; if (myGameArea.frameNo == 1 || everyinterval(150)) { x = myGameArea.canvas.width; y = myGameArea.canvas.height - 100; min = 20; max = 100; height = Math.floor(Math.random()*(max-min+1)+min); min = 50; max = 100; gap = Math.floor(Math.random()*(max-min+1)+min); myObstacles.push(new component(10, height, "green", x, 0)); myObstacles.push(new component(10, x - height - gap, "green", x, height + gap)); } for (i = 0; i < myObstacles.length; i += 1) { myObstacles[i].x += -1; myObstacles[i].update(); } myscore.text="SCORE: " + myscore.score; myscore.update(); myGamePiece.x += myGamePiece.speedX; myGamePiece.y += myGamePiece.speedY; myGamePiece.update(); } } function everyinterval(n) { if ((myGameArea.frameNo / n) % 1 == 0) {return true;} return false; } function moveup(e) { myGamePiece.speedY = -1; } function movedown() { myGamePiece.speedY = 1; } function moveleft() { myGamePiece.speedX = -1; } function moveright() { myGamePiece.speedX = 1; } function clearmove(e) { myGamePiece.speedX = 0; myGamePiece.speedY = 0; } startGame(); </script>Count The Score
There are many ways to keep the score in a game, we will show you how to write a score onto the canvas. First make a score component:Example
var myGamePiece; var myObstacles = []; var myScore; function startGame() { myGamePiece = new component(30, 30, "red", 10, 160); myScore = new component("30px", "Consolas", "black", 280, 40, "text"); myGameArea.start(); } The syntax for writing text on a canvas element is different from drawing a rectangle. Therefore we must call the component constructor using an additional argument, telling the constructor that this component is of type "text". In the component constructor we test if the component is of type "text", and use thefillText
method instead of thefillRect
method:Example
function component(width, height, color, x, y, type) { this.type = type; this.width = width; this.height = height; this.speedX = 0; this.speedY = 0; this.x = x; this.y = y; this.update = function() { ctx = myGameArea.context; if (this.type == "text") { ctx.font = this.width + " " + this.height; ctx.fillStyle = color; ctx.fillText(this.text, this.x, this.y); } else { ctx.fillStyle = color; ctx.fillRect(this.x, this.y, this.width, this.height); } } ... } At last we add some code in the updateGameArea function that writes the score onto the canvas. We use theframeNo
property to count the score:Example
function updateGameArea() { var x, height, gap, minHeight, maxHeight, minGap, maxGap; for (i = 0; i < myObstacles.length; i += 1) { if (myGamePiece.crashWith(myObstacles[i])) { myGameArea.stop(); return; } } myGameArea.clear(); myGameArea.frameNo += 1; if (myGameArea.frameNo == 1 || everyinterval(150)) { x = myGameArea.canvas.width; minHeight = 20; maxHeight = 200; height = Math.floor(Math.random()*(maxHeight-minHeight+1)+minHeight); minGap = 50; maxGap = 200; gap = Math.floor(Math.random()*(maxGap-minGap+1)+minGap); myObstacles.push(new component(10, height, "green", x, 0)); myObstacles.push(new component(10, x - height - gap, "green", x, height + gap)); } for (i = 0; i < myObstacles.length; i += 1) { myObstacles[i].speedX = -1; myObstacles[i].newPos(); myObstacles[i].update(); } myScore.text = "SCORE: " + myGameArea.frameNo; myScore.update(); myGamePiece.newPos(); myGamePiece.update(); } Try it Yourself » <script> var myGameArea; var myGamePiece; function startGame() { myGameArea = new gamearea(); myGamePiece = new component(30, 30, "smiley.gif", 10, 75, "image"); myGameArea.start(); } function gamearea() { this.canvas = document.createElement("canvas"); this.canvas.width = 320; this.canvas.height = 180; document.getElementById("canvascontainer").appendChild(this.canvas); this.context = this.canvas.getContext("2d"); this.start = function() { this.interval = setInterval(updateGameArea, 20); } this.clear = function(){ this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); } } function component(width, height, color, x, y, type) { this.type = type; if (type == "image") { this.image = new Image(); this.image.src = color; } this.width = width; this.height = height; this.speedX = 0; this.speedY = 0; this.x = x; this.y = y; this.update = function() { ctx = myGameArea.context; if (type == "image") { ctx.drawImage(this.image, this.x, this.y, this.width, this.height); } else { ctx.fillStyle = color; ctx.fillRect(this.x, this.y, this.width, this.height); } } } function updateGameArea() { myGameArea.clear(); myGamePiece.x += myGamePiece.speedX; myGamePiece.y += myGamePiece.speedY; myGamePiece.update(); } function move(dir) { myGamePiece.image.src = "angry.gif"; if (dir == "up") {myGamePiece.speedY = -1; } if (dir == "down") {myGamePiece.speedY = 1; } if (dir == "left") {myGamePiece.speedX = -1; } if (dir == "right") {myGamePiece.speedX = 1; } } function clearmove() { myGamePiece.image.src = "smiley.gif"; myGamePiece.speedX = 0; myGamePiece.speedY = 0; } startGame(); </script>How to Use Images?
To add images on a canvas, the getContext("2d") object has built-in image properties and methods. In our game, to create the gamepiece as an image, use the component constructor, but instead of referring to a color, you must refer to the url of the image. And you must tell the constructor that this component is of type "image":Example
function startGame() { myGamePiece = new component(30, 30, "smiley.gif", 10, 120, "image"); myGameArea.start(); } In the component constructor we test if the component is of type "image", and create an image object by using the built-in "new Image()" object constructor. When we are ready to draw the image, we use the drawImage method instead of the fillRect method:Example
function component(width, height, color, x, y, type) { this.type = type; if (type == "image") { this.image = new Image(); this.image.src = color; } this.width = width; this.height = height; this.speedX = 0; this.speedY = 0; this.x = x; this.y = y; this.update = function() { ctx = myGameArea.context; if (type == "image") { ctx.drawImage(this.image, this.x, this.y, this.width, this.height); } else { ctx.fillStyle = color; ctx.fillRect(this.x, this.y, this.width, this.height); } } } Try it Yourself »Change Images
You can change the image whenever you like by changing thesrc
property of theimage
object of your component.![]()
If you want to change the smiley everytime it moves, change the image source when the user clicks a button, and back to normal when the button is not clicked:
Example
function move(dir) { myGamePiece.image.src = "angry.gif"; if (dir == "up") {myGamePiece.speedY = -1; } if (dir == "down") {myGamePiece.speedY = 1; } if (dir == "left") {myGamePiece.speedX = -1; } if (dir == "right") {myGamePiece.speedX = 1; } } function clearmove() { myGamePiece.image.src = "smiley.gif"; myGamePiece.speedX = 0; myGamePiece.speedY = 0; } Try it Yourself »Background Images
Add a background image to your game area by adding it as a component, and also update the background in every frame:Example
var myGamePiece; var myBackground; function startGame() { myGamePiece = new component(30, 30, "smiley.gif", 10, 120, "image"); myBackground = new component(656, 270, "citymarket.jpg", 0, 0, "image"); myGameArea.start(); } function updateGameArea() { myGameArea.clear(); myBackground.newPos(); myBackground.update(); myGamePiece.newPos(); myGamePiece.update(); } Try it Yourself »Moving Background
Change the background component'sspeedX
property to make the background move:Example
function updateGameArea() { myGameArea.clear(); myBackground.speedX = -1; myBackground.newPos(); myBackground.update(); myGamePiece.newPos(); myGamePiece.update(); } Try it Yourself »Background Loop
To make the same background loop forever, we must use a specific technique. Start by telling the component constructor that this is a background. The component constructor will then add the image twice, placing the second image immediately after the first image. In thenewPos()
method, check if thex
position of the component has reach the end of the image, if it has, set thex
position of the component to 0:Example
function component(width, height, color, x, y, type) { this.type = type; if (type == "image" || type == "background") { this.image = new Image(); this.image.src = color; } this.width = width; this.height = height; this.speedX = 0; this.speedY = 0; this.x = x; this.y = y; this.update = function() { ctx = myGameArea.context; if (type == "image" || type == "background") { ctx.drawImage(this.image, this.x, this.y, this.width, this.height); if (type == "background") { ctx.drawImage(this.image, this.x + this.width, this.y, this.width, this.height); } } else { ctx.fillStyle = color; ctx.fillRect(this.x, this.y, this.width, this.height); } } this.newPos = function() { this.x += this.speedX; this.y += this.speedY; if (this.type == "background") { if (this.x == -(this.width)) { this.x = 0; } } } } Try it Yourself » <script> var myGameArea; var myGamePiece; var myObstacles = []; var mysound; function startGame() { myGameArea = new gamearea(); myGamePiece = new component(30, 30, "red", 10, 75); mysound = new sound("bounce.mp3"); myGameArea.start(); } function gamearea() { this.canvas = document.createElement("canvas"); this.canvas.width = 320; this.canvas.height = 180; document.getElementById("canvascontainer").appendChild(this.canvas); this.context = this.canvas.getContext("2d"); this.pause = false; this.frameNo = 0; this.start = function() { this.interval = setInterval(updateGameArea, 20); } this.stop = function() { this.pause = true; clearInterval(this.interval); } this.clear = function(){ this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); } } function component(width, height, color, x, y) { this.width = width; this.height = height; this.speedX = 0; this.speedY = 0; this.x = x; this.y = y; this.update = function() { ctx = myGameArea.context; ctx.fillStyle = color; ctx.fillRect(this.x, this.y, this.width, this.height); } this.crashWith = function(otherobj) { var myleft = this.x; var myright = this.x + (this.width); var mytop = this.y; var mybottom = this.y + (this.height); var otherleft = otherobj.x; var otherright = otherobj.x + (otherobj.width); var othertop = otherobj.y; var otherbottom = otherobj.y + (otherobj.height); var crash = true; if ((mybottom < othertop) || (mytop > otherbottom) || (myright < otherleft) || (myleft > otherright)) { crash = false; } return crash; } } function updateGameArea() { var x, y, min, max, height, gap; for (i = 0; i < myObstacles.length; i += 1) { if (myGamePiece.crashWith(myObstacles[i])) { mysound.play(); myGameArea.stop(); } } if (myGameArea.pause == false) { myGameArea.clear(); myGameArea.frameNo += 1; if (myGameArea.frameNo == 1 || everyinterval(150)) { x = myGameArea.canvas.width; y = myGameArea.canvas.height - 100; min = 20; max = 100; height = Math.floor(Math.random()*(max-min+1)+min); min = 50; max = 100; gap = Math.floor(Math.random()*(max-min+1)+min); myObstacles.push(new component(10, height, "green", x, 0)); myObstacles.push(new component(10, x - height - gap, "green", x, height + gap)); } for (i = 0; i < myObstacles.length; i += 1) { myObstacles[i].x += -1; myObstacles[i].update(); } myGamePiece.x += myGamePiece.speedX; myGamePiece.y += myGamePiece.speedY; myGamePiece.update(); } } function sound(src) { this.sound = document.createElement("audio"); this.sound.src = src; this.sound.setAttribute("preload", "auto"); this.sound.setAttribute("controls", "none"); this.sound.style.display = "none"; document.body.appendChild(this.sound); this.play = function(){ this.sound.play(); } } function everyinterval(n) { if ((myGameArea.frameNo / n) % 1 == 0) {return true;} return false; } function moveup(e) { myGamePiece.speedY = -1; } function movedown() { myGamePiece.speedY = 1; } function moveleft() { myGamePiece.speedX = -1; } function moveright() { myGamePiece.speedX = 1; } function clearmove(e) { myGamePiece.speedX = 0; myGamePiece.speedY = 0; } startGame(); </script>How to Add Sounds?
Use the HTML5 <audio> element to add sound and music to your games. In our examples, we create a new object constructor to handle sound objects:Example
function sound(src) { this.sound = document.createElement("audio"); this.sound.src = src; this.sound.setAttribute("preload", "auto"); this.sound.setAttribute("controls", "none"); this.sound.style.display = "none"; document.body.appendChild(this.sound); this.play = function(){ this.sound.play(); } this.stop = function(){ this.sound.pause(); } } To create a new sound object use thesound
constructor, and when the red square hits an obstacle, play the sound:Example
var myGamePiece; var myObstacles = []; var mySound; function startGame() { myGamePiece = new component(30, 30, "red", 10, 120); mySound = new sound("bounce.mp3"); myGameArea.start(); } function updateGameArea() { var x, height, gap, minHeight, maxHeight, minGap, maxGap; for (i = 0; i < myObstacles.length; i += 1) { if (myGamePiece.crashWith(myObstacles[i])) { mySound.play(); myGameArea.stop(); return; } } ... } Try it Yourself »Background Music
To add background music to your game, add a new sound object, and start playing when you start the game:Example
var myGamePiece; var myObstacles = []; var mySound; var myMusic; function startGame() { myGamePiece = new component(30, 30, "red", 10, 120); mySound = new sound("bounce.mp3"); myMusic = new sound("gametheme.mp3"); myMusic.play(); myGameArea.start(); } Try it Yourself » <script> var myGameArea; var myGamePiece; var myoverlay; var myresult; var accel = false; function startGame() { myGameArea = new gamearea("canvascontainer"); //myresult = new component(myGameArea, "30px", "Consolas", "white", 30, 50, "text"); //myresult.text = "Land me Safely" myGamePiece = new component(30, 30, "red", 50, 50); //myoverlay = new component(myGameArea, 640, 360, "rgba(0,255,0,0.1)", 0, 0); myGameArea.start(); } function gamearea() { this.canvas = document.createElement("canvas"); this.canvas.width = 320; this.canvas.height = 180; document.getElementById("canvascontainer").appendChild(this.canvas); this.context = this.canvas.getContext("2d"); this.start = function() { this.interval = setInterval(updateGameArea, 20); } this.stop = function() { clearInterval(this.interval); this.pause = true; } this.clear = function(){ this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); } } function component(width, height, color, x, y, type) { this.type = type; if (type == "image") { this.image = new Image(); this.image.src = color; } this.width = width; this.height = height; this.angle = 0; this.moveangle = 0; this.color = color; this.rotation = 0; this.speed = 0; this.fallspeed = 0; this.x = x; this.y = y; this.speedX = 0; this.speedY = 0; this.update = function() { ctx = myGameArea.context; ctx.save(); ctx.translate(this.x, this.y); ctx.rotate(this.angle); if (this.type == "text") { ctx.font = this.width + " " + this.height; ctx.fillStyle = color; ctx.fillText(this.text, this.x, this.y); } else { ctx.fillStyle = this.color; ctx.fillRect(-(this.width / 2), -(this.height / 2), this.width, this.height); } ctx.restore(); } this.newGravityPos = function(degrees, thrust) { degrees -= 90; angle = degrees * Math.PI / 180; this.speedX += thrust * Math.cos(angle); this.speedY += thrust * Math.sin(angle); this.calcSpeedAngle(); } this.calcSpeedAngle = function(){ this.speed = Math.sqrt((this.speedX * this.speedX) + (this.speedY * this.speedY)); } } function updateGameArea() { myGameArea.clear(); myGamePiece.x += myGamePiece.speedX; myGamePiece.y += myGamePiece.speedY; if (accel == true || (document.key && document.key == 38)) { myGamePiece.newGravityPos(0, 0.2); } myGamePiece.newGravityPos(180, 0.05); if (myGamePiece.speed > 2 || myGamePiece.y < -15) { // myoverlay.color = "rgba(255,0,0,0.1)" } else { // myoverlay.color = "rgba(0,255,0,0.1)" } myGamePiece.update(); //myoverlay.update(); if (myGamePiece.y > 165) { myGameArea.stop(); if (myGamePiece.speed > 2) { //myresult.width = "26px"; //myresult.x = 10; //myresult.text = "Landing was to hard!"; //myoverlay.color = "rgba(255,0,0,0.1)" } else { //myoverlay.color = "rgba(0,255,0,0.1)" //myresult.text = "Nice landing!"; } } if (myGamePiece.y < -15) { myGameArea.stop(); //myresult.width = "18px"; //myresult.x = 4; //myresult.text = "You lost the red square!"; } //myresult.update(); } function accelerate(x) { accel = x; } function restartgame(x) { myGameArea.stop(); document.getElementById("canvascontainer").innerHTML = ""; startGame(); } startGame(); </script>Gravity
To add this functionality to our component constructor, first add agravity
property, which sets the current gravity. Then add agravitySpeed
property, which increases everytime we update the frame:Example
function component(width, height, color, x, y, type) { this.type = type; this.width = width; this.height = height; this.x = x; this.y = y; this.speedX = 0; this.speedY = 0; this.gravity = 0.05; this.gravitySpeed = 0; this.update = function() { ctx = myGameArea.context; ctx.fillStyle = color; ctx.fillRect(this.x, this.y, this.width, this.height); } this.newPos = function() { this.gravitySpeed += this.gravity; this.x += this.speedX; this.y += this.speedY + this.gravitySpeed; } } Try it Yourself »Hit the Bottom
To prevent the red square from falling forever, stop the falling when it hits the bottom of the game area:Example
this.newPos = function() { this.gravitySpeed += this.gravity; this.x += this.speedX; this.y += this.speedY + this.gravitySpeed; this.hitBottom(); } this.hitBottom = function() { var rockbottom = myGameArea.canvas.height - this.height; if (this.y > rockbottom) { this.y = rockbottom; } } Try it Yourself »Accelerate Up
In a game, when you have a force that pulls you down, you should have a method to force the component to accelerate up. Trigger a function when someone clicks a button, and make the red square fly up in the air:Example
<script> function accelerate(n) { myGamePiece.gravity = n; } </script> <button onmousedown="accelerate(-0.2)" onmouseup="accelerate(0.1)">ACCELERATE</button> Try it Yourself »A Game
Make a game based on what we have learned so far:Example
Try it Yourself » <script> var myGamePiece; var myoverlay; var myresult; var accel = false; function startGame() { myGamePiece = new component(30, 30, "red", 50, 50); myGameArea.start(); } var myGameArea = { canvas : document.createElement("canvas"), start : function() { this.canvas.width = 320; this.canvas.height = 180; this.context = this.canvas.getContext("2d"); document.getElementById("canvascontainer").appendChild(this.canvas); this.interval = setInterval(updateGameArea, 20); }, stop : function() { clearInterval(this.interval); }, clear : function() { this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); } } function component(width, height, color, x, y, type) { this.type = type; this.width = width; this.height = height; this.speed = 0; this.gravity = 0.1; this.gravitySpeed = 0; this.bounce = 0.6; this.angle = 0; this.x = x; this.y = y; this.update = function() { ctx = myGameArea.context; ctx.save(); ctx.translate(this.x, this.y); ctx.rotate(this.angle); ctx.fillStyle = color; ctx.fillRect(this.width / -2, this.height / -2, this.width, this.height); ctx.restore(); } this.newPos = function() { var rockbottom = myGameArea.canvas.height - (this.height / 2); this.gravitySpeed += this.gravity; this.x += this.speed * Math.cos(this.angle); this.y += (this.speed * Math.sin(this.angle)) + this.gravitySpeed; if (this.y > rockbottom) { this.gravitySpeed=-(this.gravitySpeed * this.bounce); this.gravitySpeed = Number(this.gravitySpeed.toFixed(2)); this.y = rockbottom; } } } function updateGameArea() { myGameArea.clear(); myGamePiece.newPos(); myGamePiece.update(); } function restartgame(x) { myGameArea.stop(); document.getElementById("canvascontainer").innerHTML = ""; startGame(); } startGame(); </script>Bouncing
Another functionallity we want to add is thebounce
property. Thebounce
property indicates if the component will bounce back when gravity makes it fall down to the ground. The bounce property value must be a number. 0 is no bounce at all, and 1 will make the component bounce all the way backto where it start falling.Example
function component(width, height, color, x, y, type) { this.type = type; this.width = width; this.height = height; this.x = x; this.y = y; this.speedX = 0; this.speedY = 0; this.gravity = 0.1; this.gravitySpeed = 0; this.bounce = 0.6; this.update = function() { ctx = myGameArea.context; ctx.fillStyle = color; ctx.fillRect(this.x, this.y, this.width, this.height); } this.newPos = function() { this.gravitySpeed += this.gravity; this.x += this.speedX; this.y += this.speedY + this.gravitySpeed; this.hitBottom(); } this.hitBottom = function() { var rockbottom = this.gamearea.canvas.height - this.height; if (this.y > rockbottom) { this.y = rockbottom; this.gravitySpeed = -(this.gravitySpeed * this.bounce); } } } Try it Yourself » <button id="playagain" onclick="clickPlayagain()">Rotate</button> <script> var myGameArea; var myGamePiece; function startGame() { myGameArea = new gamearea(""); myGamePiece = new component(30, 30, "red", 25, 90); myGameArea.start(); } function gamearea(x) { this.canvas = document.createElement("canvas"); this.canvas.width = 320; this.canvas.height = 180; document.getElementById("canvascontainer" + x).innerHTML = ""; document.getElementById("canvascontainer" + x).appendChild(this.canvas); this.context = this.canvas.getContext("2d"); this.frameNo = 0; this.start = function() { this.interval = setInterval(updateGameArea, 20); } this.stop = function() { clearInterval(this.interval); } this.clear = function(){ this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); } } function component(width, height, color, x, y, type) { this.type = type; this.width = width; this.height = height; this.speed = 0; this.angle = 0; this.moveangle = 0; this.x = x; this.y = y; this.update = function() { ctx = myGameArea.context; ctx.save(); ctx.translate(this.x, this.y); ctx.rotate(this.angle); if (this.type == "text") { ctx.font = this.width + " " + this.height; ctx.fillStyle = color; width = ctx.measureText(this.text).width; height = ctx.measureText("m").width ctx.fillText(this.text, width/ -2, height/2); } else { ctx.fillStyle = color; ctx.fillRect(this.width / -2, this.height / -2, this.width, this.height); } ctx.restore(); } this.newPos = function() { this.x += this.speed * Math.cos(this.angle); this.y += this.speed * Math.sin(this.angle); this.angle += this.moveangle } } var rotation; function updateGameArea(y) { myGameArea.clear(); myGameArea.frameNo++; myGamePiece.speed = 0; myGamePiece.moveangle = 1 * Math.PI / 180; if (myGameArea.frameNo > 90) { myGamePiece.moveangle = 0; } myGamePiece.newPos(); myGamePiece.update(); } function clickPlayagain() { myGameArea.stop(); startGame(); } startGame(); </script>Rotating Components
Earlier in this tutorial, the red square was able to move around on the gamearea, but it could not turn or rotate. To rotate components, we have to change the way we draw components. The only rotation method available for the canvas element will rotate the entire canvas:Everything else you draw on the canvas will also be rotated, not only the specific component. That is why we have to make some changes in the
update()
method: First, we save the current canvas context object:ctx.save();
Then we move the entire canvas to the center of the specific component, using the translate method:ctx.translate(x, y);
Then we perform the wanted rotation using the rotate() method:
ctx.rotate(angle);
Now we are ready to draw the component onto the canvas, but now we will draw it with its center position at position 0,0 on the translated (and rotated) canvas:
ctx.fillRect(width / -2, height / -2, width, height);
When we are finished, we must restore the context object back to its saved position, using the restore method:
ctx.restore();
The component is the only thing that is rotated:![]()
The Component Constructor
Thecomponent
constructor has a new property calledangle
, which is radian number that represents the angle of the component. Theupdate
method of thecomponent
constructor is were we draw the component, and here you can see the changes that will allow the component to rotate:Example
function component(width, height, color, x, y) { this.width = width; this.height = height; this.angle = 0; this.x = x; this.y = y; this.update = function() { ctx = myGameArea.context; ctx.save(); ctx.translate(this.x, this.y); ctx.rotate(this.angle); ctx.fillStyle = color; ctx.fillRect(this.width / -2, this.height / -2, this.width, this.height); ctx.restore(); } } function updateGameArea() { myGameArea.clear(); myGamePiece.angle += 1 * Math.PI / 180; myGamePiece.update(); } Try it Yourself » <button id="playagain" onclick="clickPlayagain()">Play again</button>How to Move Objects?
Add aspeed
property to thecomponent
constructor, which represents the current speed of the component. Also make some changes in thenewPos()
method, to calculate the position of the component, based onspeed
andangle
. By default, the components are facing up, and by setting the speed property to 1, the component will start moving forward.Example
function component(width, height, color, x, y) { this.gamearea = gamearea; this.width = width; this.height = height; this.angle = 0; this.speed = 1; this.x = x; this.y = y; this.update = function() { ctx = myGameArea.context; ctx.save(); ctx.translate(this.x, this.y); ctx.rotate(this.angle); ctx.fillStyle = color; ctx.fillRect(this.width / -2, this.height / -2, this.width, this.height); ctx.restore(); } this.newPos = function() { this.x += this.speed * Math.sin(this.angle); this.y -= this.speed * Math.cos(this.angle); } } Try it Yourself »Making Turns
We also want to be able to make left and right turns. Make a new property calledmoveAngle
, which indicates the current moving value, or rotation angle. In thenewPos()
method calculate theangle
based on themoveAngle
property:Example
Set the moveangle property to 1, and see what happens: function component(width, height, color, x, y) { this.width = width; this.height = height; this.angle = 0; this.moveAngle = 1; this.speed = 1; this.x = x; this.y = y; this.update = function() { ctx = myGameArea.context; ctx.save(); ctx.translate(this.x, this.y); ctx.rotate(this.angle); ctx.fillStyle = color; ctx.fillRect(this.width / -2, this.height / -2, this.width, this.height); ctx.restore(); } this.newPos = function() { this.angle += this.moveAngle * Math.PI / 180; this.x += this.speed * Math.sin(this.angle); this.y -= this.speed * Math.cos(this.angle); } } Try it Yourself »Use the Keyboard
How does the red square move when using the keyboard? Instead of moving up and down, and from side to side, the red square moves forward when you use the "up" arrow,and turns left and right when pressing the left and right arrows.Example
Try it Yourself » <script> var myGameArea; var myGamePiece; //var myGameArea2; //var myGamePiece2; function startGame() { myGameArea = new gamearea(""); myGamePiece = new component(20, 20, "red", 15, 110); myGameArea.start(); // myGameArea2 = new gamearea("2"); // myGamePiece2 = new component(myGameArea2, "50px", "Consolas", "red", 100, 15, "text"); // myGamePiece2.text = "\u00BB"; // myGameArea2.start(); } function gamearea(x) { this.canvas = document.createElement("canvas"); this.canvas.width = 320; this.canvas.height = 180; document.getElementById("canvascontainer" + x).innerHTML = ""; document.getElementById("canvascontainer" + x).appendChild(this.canvas); this.context = this.canvas.getContext("2d"); this.frameNo = 0; this.start = function() { this.interval = setInterval(updateGameArea, 20); } this.stop = function() { clearInterval(this.interval); } this.clear = function(){ this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); } } function component(width, height, color, x, y, type) { this.type = type; this.width = width; this.height = height; this.speed = 0; this.angle = 0; this.moveangle = 0; this.x = x; this.y = y; this.update = function() { ctx = myGameArea.context; ctx.save(); ctx.translate(this.x, this.y); ctx.rotate(this.angle); if (this.type == "text") { ctx.font = this.width + " " + this.height; ctx.fillStyle = color; width = ctx.measureText(this.text).width; height = ctx.measureText("m").width ctx.fillText(this.text, width/ -2, height/2); } else { ctx.fillStyle = color; ctx.fillRect(this.width / -2, this.height / -2, this.width, this.height); } ctx.restore(); } this.newPos = function() { this.x += this.speed * Math.sin(this.angle); this.y -= this.speed * Math.cos(this.angle); this.angle += this.moveangle } } var rotation; function updateGameArea(y) { myGameArea.clear(); myGameArea.frameNo++; myGamePiece.speed = 1; if (myGameArea.frameNo == 1) { rotation = 0; document.getElementById("playagain").disabled = true; // document.getElementById("playagain2").disabled = true; } rotation++; if (rotation == 40) { myGamePiece.moveangle = 1 * Math.PI / 180; } if (rotation == 130) { myGamePiece.moveangle = 0; } if (myGameArea.frameNo >= 520) { myGamePiece.speed = 0; myGamePiece.moveangle = 0; document.getElementById("playagain").disabled = false; // document.getElementById("playagain2").disabled = false; } myGamePiece.newPos(); myGamePiece.update(); // myGameArea2.clear(); // myGameArea2.frameNo++; // myGamePiece2.speed = 1; // if (rotation == 40) { // myGamePiece2.moveangle = 1 * Math.PI / 180; // } if (rotation == 130) { // myGamePiece2.moveangle = 0; rotation = 0; } // if (myGameArea2.frameNo >= 520) { // myGamePiece2.speed = 0; // myGamePiece2.moveangle = 0; // } // myGamePiece2.newPos(); // myGamePiece2.update(); } function clickPlayagain() { myGameArea.stop(); // myGameArea2.stop(); startGame(); } startGame(); </script>