Aug 11, 2011

How To Make A GAME In HTML 5 | How To Build A Game With HTML 5

download



Final Output:


Running Ball

HTML 5 is growing at the lightning speed, none of the languages in web development has grew up in this speed. Today we are going to make a simple game in HTML 5 by using Box2D and HTML 5 Canvas.


But before that, the question is



What is BOX2D?
Box2D is an open source and popular engine that simulates 2D physics for making games and applications. Primarily written in C++, it has been converted to numerous languages by community contributors.

This tutorial is divided into 5 simple steps, so lets get started



STEP - 1 Folder Structure


First of all we need to download the box2d engine for our html 5, you can download it from here. Next we create the file and folder structure. Just follow the image of file folder structure and copy paste them in respective places.
Folder Structure


After the setup of files, we will add necessary files to the html file that is index.html



[html]
<!--[if IE]><script src="lib/excanvas.js"></script><![endif]--><script type="text/javascript" src="lib/prototype-1.6.0.2.js"></script>
<!-- box2djs -->
<script type="text/javascript" src="js/box2d/common/b2Settings.js"></script><script type="text/javascript" src="js/box2d/common/math/b2Vec2.js"></script>
<script type="text/javascript" src="js/box2d/common/math/b2Mat22.js"></script><script type="text/javascript" src="js/box2d/common/math/b2Math.js"></script>
<script type="text/javascript" src="js/box2d/collision/b2AABB.js"></script><script type="text/javascript" src="js/box2d/collision/b2Bound.js"></script>
<script type="text/javascript" src="js/box2d/collision/b2BoundValues.js"></script><script type="text/javascript" src="js/box2d/collision/b2Pair.js"></script>
<script type="text/javascript" src="js/box2d/collision/b2PairCallback.js"></script><script type="text/javascript" src="js/box2d/collision/b2BufferedPair.js"></script>
<script type="text/javascript" src="js/box2d/collision/b2PairManager.js"></script><script type="text/javascript" src="js/box2d/collision/b2BroadPhase.js"></script>
<script type="text/javascript" src="js/box2d/collision/b2Collision.js"></script><script type="text/javascript" src="js/box2d/collision/Features.js"></script>
<script type="text/javascript" src="js/box2d/collision/b2ContactID.js"></script><script type="text/javascript" src="js/box2d/collision/b2ContactPoint.js"></script>
<script type="text/javascript" src="js/box2d/collision/b2Distance.js"></script><script type="text/javascript" src="js/box2d/collision/b2Manifold.js"></script>
<script type="text/javascript" src="js/box2d/collision/b2OBB.js"></script><script type="text/javascript" src="js/box2d/collision/b2Proxy.js"></script>
<script type="text/javascript" src="js/box2d/collision/ClipVertex.js"></script><script type="text/javascript" src="js/box2d/collision/shapes/b2Shape.js"></script>
<script type="text/javascript" src="js/box2d/collision/shapes/b2ShapeDef.js"></script><script type="text/javascript" src="js/box2d/collision/shapes/b2BoxDef.js"></script>
<script type="text/javascript" src="js/box2d/collision/shapes/b2CircleDef.js"></script><script type="text/javascript" src="js/box2d/collision/shapes/b2CircleShape.js"></script>
<script type="text/javascript" src="js/box2d/collision/shapes/b2MassData.js"></script><script type="text/javascript" src="js/box2d/collision/shapes/b2PolyDef.js"></script>
<script type="text/javascript" src="js/box2d/collision/shapes/b2PolyShape.js"></script><script type="text/javascript" src="js/box2d/dynamics/b2Body.js"></script>
<script type="text/javascript" src="js/box2d/dynamics/b2BodyDef.js"></script><script type="text/javascript" src="js/box2d/dynamics/b2CollisionFilter.js"></script>
<script type="text/javascript" src="js/box2d/dynamics/b2Island.js"></script><script type="text/javascript" src="js/box2d/dynamics/b2TimeStep.js"></script>
<script type="text/javascript" src="js/box2d/dynamics/contacts/b2ContactNode.js"></script><script type="text/javascript" src="js/box2d/dynamics/contacts/b2Contact.js"></script>
<script type="text/javascript" src="js/box2d/dynamics/contacts/b2ContactConstraint.js"></script><script type="text/javascript" src="js/box2d/dynamics/contacts/b2ContactConstraintPoint.js"></script>
<script type="text/javascript" src="js/box2d/dynamics/contacts/b2ContactRegister.js"></script><script type="text/javascript" src="js/box2d/dynamics/contacts/b2ContactSolver.js"></script>
<script type="text/javascript" src="js/box2d/dynamics/contacts/b2CircleContact.js"></script><script type="text/javascript" src="js/box2d/dynamics/contacts/b2Conservative.js"></script>
<script type="text/javascript" src="js/box2d/dynamics/contacts/b2NullContact.js"></script><script type="text/javascript" src="js/box2d/dynamics/contacts/b2PolyAndCircleContact.js"></script>
<script type="text/javascript" src="js/box2d/dynamics/contacts/b2PolyContact.js"></script><script type="text/javascript" src="js/box2d/dynamics/b2ContactManager.js"></script>
<script type="text/javascript" src="js/box2d/dynamics/b2World.js"></script><script type="text/javascript" src="js/box2d/dynamics/b2WorldListener.js"></script>
<script type="text/javascript" src="js/box2d/dynamics/joints/b2JointNode.js"></script><script type="text/javascript" src="js/box2d/dynamics/joints/b2Joint.js"></script>
<script type="text/javascript" src="js/box2d/dynamics/joints/b2JointDef.js"></script><script type="text/javascript" src="js/box2d/dynamics/joints/b2DistanceJoint.js"></script>
<script type="text/javascript" src="js/box2d/dynamics/joints/b2DistanceJointDef.js"></script><script type="text/javascript" src="js/box2d/dynamics/joints/b2Jacobian.js"></script>
<script type="text/javascript" src="js/box2d/dynamics/joints/b2GearJoint.js"></script><script type="text/javascript" src="js/box2d/dynamics/joints/b2GearJointDef.js"></script>
<script type="text/javascript" src="js/box2d/dynamics/joints/b2MouseJoint.js"></script><script type="text/javascript" src="js/box2d/dynamics/joints/b2MouseJointDef.js"></script>
<script type="text/javascript" src="js/box2d/dynamics/joints/b2PrismaticJoint.js"></script><script type="text/javascript" src="js/box2d/dynamics/joints/b2PrismaticJointDef.js"></script>
<script type="text/javascript" src="js/box2d/dynamics/joints/b2PulleyJoint.js"></script><script type="text/javascript" src="js/box2d/dynamics/joints/b2PulleyJointDef.js"></script>
<script type="text/javascript" src="js/box2d/dynamics/joints/b2RevoluteJoint.js"></script><script type="text/javascript" src="js/box2d/dynamics/joints/b2RevoluteJointDef.js"></script>
[/html]

Next, we will create two more scripts file inside the /js/ folder directory, named as  "box2dutils.js" and "game.js" respectively




  • box2dutils.js – This files is a copy and paste from some other online demos that come with box2dlib, and it is very important for drawing functions.

  • game.js – By the name it will be easy to recognize, it the actual game, where platform, keyboard input etc functions are set.


Copy paste the following code in "box2dutils.js" file



[html]
function drawWorld(world, context) {
for (var j = world.m_jointList; j; j = j.m_next) {
drawJoint(j, context);
}
for (var b = world.m_bodyList; b; b = b.m_next) {
for (var s = b.GetShapeList(); s != null; s = s.GetNext()) {
drawShape(s, context);
}
}
}
function drawJoint(joint, context) {
var b1 = joint.m_body1;
var b2 = joint.m_body2;
var x1 = b1.m_position;
var x2 = b2.m_position;
var p1 = joint.GetAnchor1();
var p2 = joint.GetAnchor2();
context.strokeStyle = '#00eeee';
context.beginPath();
switch (joint.m_type) {
case b2Joint.e_distanceJoint:
context.moveTo(p1.x, p1.y);
context.lineTo(p2.x, p2.y);
break;

case b2Joint.e_pulleyJoint:
// TODO
break;

default:
if (b1 == world.m_groundBody) {
context.moveTo(p1.x, p1.y);
context.lineTo(x2.x, x2.y);
}
else if (b2 == world.m_groundBody) {
context.moveTo(p1.x, p1.y);
context.lineTo(x1.x, x1.y);
}
else {
context.moveTo(x1.x, x1.y);
context.lineTo(p1.x, p1.y);
context.lineTo(x2.x, x2.y);
context.lineTo(p2.x, p2.y);
}
break;
}
context.stroke();
}
function drawShape(shape, context) {
context.strokeStyle = '#000000';
context.beginPath();
switch (shape.m_type) {
case b2Shape.e_circleShape:
{
var circle = shape;
var pos = circle.m_position;
var r = circle.m_radius;
var segments = 16.0;
var theta = 0.0;
var dtheta = 2.0 * Math.PI / segments;
// draw circle
context.moveTo(pos.x + r, pos.y);
for (var i = 0; i < segments; i++) {
var d = new b2Vec2(r * Math.cos(theta), r * Math.sin(theta));
var v = b2Math.AddVV(pos, d);
context.lineTo(v.x, v.y);
theta += dtheta;
}
context.lineTo(pos.x + r, pos.y);

// draw radius
context.moveTo(pos.x, pos.y);
var ax = circle.m_R.col1;
var pos2 = new b2Vec2(pos.x + r * ax.x, pos.y + r * ax.y);
context.lineTo(pos2.x, pos2.y);
}
break;
case b2Shape.e_polyShape:
{
var poly = shape;
var tV = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[0]));
context.moveTo(tV.x, tV.y);
for (var i = 0; i < poly.m_vertexCount; i++) {
var v = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[i]));
context.lineTo(v.x, v.y);
}
context.lineTo(tV.x, tV.y);
}
break;
}
context.stroke();
}

function createWorld() {
var worldAABB = new b2AABB();
worldAABB.minVertex.Set(-1000, -1000);
worldAABB.maxVertex.Set(1000, 1000);
var gravity = new b2Vec2(0, 300);
var doSleep = true;
var world = new b2World(worldAABB, gravity, doSleep);
return world;
}

function createGround(world) {
var groundSd = new b2BoxDef();
groundSd.extents.Set(1000, 50);
groundSd.restitution = 0.2;
var groundBd = new b2BodyDef();
groundBd.AddShape(groundSd);
groundBd.position.Set(-500, 340);
return world.CreateBody(groundBd)
}

function createBall(world, x, y) {
var ballSd = new b2CircleDef();
ballSd.density = 1.0;
ballSd.radius = 20;
ballSd.restitution = 1.0;
ballSd.friction = 0;
var ballBd = new b2BodyDef();
ballBd.AddShape(ballSd);
ballBd.position.Set(x,y);
return world.CreateBody(ballBd);
}

function createBox(world, x, y, width, height, fixed, userData) {
if (typeof(fixed) == 'undefined') fixed = true;
var boxSd = new b2BoxDef();
if (!fixed) boxSd.density = 1.0;

boxSd.userData = userData;

boxSd.extents.Set(width, height);
var boxBd = new b2BodyDef();
boxBd.AddShape(boxSd);
boxBd.position.Set(x,y);
return world.CreateBody(boxBd)
}
[/html]

Don't worry I ll explain the code further.


Now we move on to our next step



Step 2 - Game Development


Open the index.html file, which we created in the first step, after adding the head part, insert the canvas code between the body tag



[html]<canvas id="game" width="600" height="400"></canvas>[/html]

Next we have to add the reference of the two javascripts file which we created just now. So in index.html, within the head tag, add the following code in it.



[html]
<script type="text/javascript" src="js/box2dutils.js"></script><script type="text/javascript" src="js/game.js"></script>
[/html]

Now lets start developing our game, so open game.js file and add the following code in it.



[html]
// some variables that we gonna use in this demo
var initId = 0;
var player = function(){
this.object = null;
this.canJump = false;
};
var world;
var ctx;
var canvasWidth;
var canvasHeight;
var keys = [];

// HTML5 onLoad event
Event.observe(window, 'load', function() {
world = createWorld(); // box2DWorld
ctx = $('game').getContext('2d'); // 2
var canvasElm = $('game');
canvasWidth = parseInt(canvasElm.width);
canvasHeight = parseInt(canvasElm.height);
initGame(); // 3
step(); // 4

// 5
window.addEventListener('keydown',handleKeyDown,true);
window.addEventListener('keyup',handleKeyUp,true);
});
[/html]

OK, now its time for explanation of code now


BOX2DWorld is the available class from the core of box2d.Its major and simple function is to combine everything into one class. In this box2DWorld, we have the bodies definition and collisions manager of our game.


Now open the game.js and box2dutils.js and search for the function createWorld() in box2dutils.js file.



[html]
function createWorld() {
// here we create our world settings for collisions
var worldAABB = new b2AABB();
worldAABB.minVertex.Set(-1000, -1000);
worldAABB.maxVertex.Set(1000, 1000);
// set gravity vector
var gravity = new b2Vec2(0, 300);
var doSleep = true;
// init our world and return its value
var world = new b2World(worldAABB, gravity, doSleep);
return world;
}
[/html]

Its really simple to make such box2DWorld


Now again back to game.js file.


Now copy paste the following code in game.js file



[html]
function initGame(){
// create 2 big platforms
createBox(world, 3, 230, 60, 180, true, 'ground');
createBox(world, 560, 360, 50, 50, true, 'ground');

// create small platforms
for (var i = 0; i < 5; i++){
createBox(world, 150+(80*i), 360, 5, 40+(i*15), true, 'ground');
}

// create player ball
var ballSd = new b2CircleDef();
ballSd.density = 0.1;
ballSd.radius = 12;
ballSd.restitution = 0.5;
ballSd.friction = 1;
ballSd.userData = 'player';
var ballBd = new b2BodyDef();
ballBd.linearDamping = .03;
ballBd.allowSleep = false;
ballBd.AddShape(ballSd);
ballBd.position.Set(20,0);
player.object = world.CreateBody(ballBd);

}

Inside <code>box2dutils.js</code>, we've created a function, called <code>createBox</code>. This creates a static rectangle body.

function createBox(world, x, y, width, height, fixed, userData) {
if (typeof(fixed) == 'undefined') fixed = true;
//1
var boxSd = new b2BoxDef();
if (!fixed) boxSd.density = 1.0;
//2
boxSd.userData = userData;
//3
boxSd.extents.Set(width, height);

//4
var boxBd = new b2BodyDef();
boxBd.AddShape(boxSd);
//5
boxBd.position.Set(x,y);
//6
return world.CreateBody(boxBd)
}
[/html]

A Box2DBody has some unique characteristics:




  • It can be static (not affected by collisions impacts), kinematic (it isn't affected by collisions, but it can be moved by your mouse, for example), or dynamic (interacts with everything)

  • Must have a shape definition, and should indicate how the object appears

  • May have more than one fixture, which indicates how the object will interact with collisions

  • Its position is set by the center of your object, not the left top edge as many other engines do.


Reviewing the above code:



  1. Here we are creating a shape, which can be a square or rectangle or any other geometry shape. And also we setup the density(the actual movement of the object)

  2. We setup the userData, usually we setup graphics objects here, but for this game, we just setup strings that will be the identifier of the type of the object for collisions.

  3. Setup half of the size of our box.

  4. We create the body definition, and add to it the box shape definition.

  5. Setup the position.

  6. Create the body in the world and return its value.


Creating The Player Object


Paste the following code in game.js file



[html]
var ballSd = new b2CircleDef();
ballSd.density = 0.1;
ballSd.radius = 12;
ballSd.restitution = 0.5;
ballSd.friction = 1;
ballSd.userData = 'player';
var ballBd = new b2BodyDef();
ballBd.linearDamping = .03;
ballBd.allowSleep = false;
ballBd.AddShape(ballSd);
ballBd.position.Set(20,0);
player.object = world.CreateBody(ballBd);
[/html]

So now we have created body.
We have used simple steps




  1. Created Shape, Fixture and Sensor Definition.

  2. Created Body Definition

  3. Added body into the shape

  4. Created body in the world.


Simple isn't it?


So Now if we run our code, it will give us a blank white screen.


Don't panic, till now we have just setup Box2, and always remember



Box2D doesn't render; it only calculates physics.

Step 3 - Rendering Time


Now we will render the box2dworld.


Open game.js file and add the following code in it:



[html]
function step() {

var stepping = false;
var timeStep = 1.0/60;
var iteration = 1;
// 1
world.Step(timeStep, iteration);
// 2
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
drawWorld(world, ctx);
// 3
setTimeout('step()', 10);
}
[/html]

In the above code we have intructed box2dworld to achieve the following things




  1. To perform physics calculations.

  2. To clear the canvas screen and draw again on it.

  3. And to run the step() function in every 10 milliseconds.


Now if you run the code, you will see the canvas with a running ball.


Running Ball


drawWorld in box2dutils.js


Open box2dutils.js file and add the following code in it.



[html]
function drawWorld(world, context) {
for (var j = world.m_jointList; j; j = j.m_next) {
drawJoint(j, context);
}
for (var b = world.m_bodyList; b; b = b.m_next) {
for (var s = b.GetShapeList(); s != null; s = s.GetNext()) {
drawShape(s, context);
}
}
}
[/html]

The above written function is a debug function which will draw our world into the canvas, by using the graphics API provided by HTML5's Canvas API.


The very first loop will draw all joints.


The second loop will draw all bodies.



[html]
function drawShape(shape, context) {
context.strokeStyle = '#000000';
context.beginPath();
switch (shape.m_type) {
case b2Shape.e_circleShape:
{
var circle = shape;
var pos = circle.m_position;
var r = circle.m_radius;
var segments = 16.0;
var theta = 0.0;
var dtheta = 2.0 * Math.PI / segments;
// draw circle
context.moveTo(pos.x + r, pos.y);
for (var i = 0; i < segments; i++) {
var d = new b2Vec2(r * Math.cos(theta), r * Math.sin(theta));
var v = b2Math.AddVV(pos, d);
context.lineTo(v.x, v.y);
theta += dtheta;
}
context.lineTo(pos.x + r, pos.y);

// draw radius
context.moveTo(pos.x, pos.y);
var ax = circle.m_R.col1;
var pos2 = new b2Vec2(pos.x + r * ax.x, pos.y + r * ax.y);
context.lineTo(pos2.x, pos2.y);
}
break;
case b2Shape.e_polyShape:
{
var poly = shape;
var tV = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[0]));
context.moveTo(tV.x, tV.y);
for (var i = 0; i < poly.m_vertexCount; i++) {
var v = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[i]));
context.lineTo(v.x, v.y);
}
context.lineTo(tV.x, tV.y);
}
break;
}
context.stroke();
}
[/html]

Here we're looping through every vertices of the object and drawing it with lines.



Step 4 - Interactivity


In this step we will instruct the game by our keyboard events.
Add the following code to game.js file



[html]
function handleKeyDown(evt){
keys[evt.keyCode] = true;
}

function handleKeyUp(evt){
keys[evt.keyCode] = false;
}

// disable vertical scrolling from arrows</pre>
<img src="http://net.tutsplus.com/wp-includes/images/smilies/icon_smile.gif" alt=":)" />
<pre>
document.onkeydown=function(){return event.keyCode!=38 && event.keyCode!=40}
[/html]

With the help of an array we are handling the UpKey and the DownKey event. With document.onkeydown, we disable the browser's native vertical scrolling function for up and down arrows.
If you have played a html5 game and jumped during the game, the game goes off screen, but in our game, it won't



[html]handleInteractions(); [/html]

And outside, declare the function:



[html]
function handleInteractions(){
// up arrow
// 1
var collision = world.m_contactList;
player.canJump = false;
if (collision != null){
if (collision.GetShape1().GetUserData() == 'player' || collision.GetShape2().GetUserData() == 'player'){
if ((collision.GetShape1().GetUserData() == 'ground' || collision.GetShape2().GetUserData() == 'ground')){
var playerObj = (collision.GetShape1().GetUserData() == 'player' ? collision.GetShape1().GetPosition() :  collision.GetShape2().GetPosition());
var groundObj = (collision.GetShape1().GetUserData() == 'ground' ? collision.GetShape1().GetPosition() :  collision.GetShape2().GetPosition());
if (playerObj.y < groundObj.y){
player.canJump = true;
}
}
}
}
// 2
var vel = player.object.GetLinearVelocity();
// 3
if (keys[38] && player.canJump){
vel.y = -150;
}

// 4
// left/right arrows
if (keys[37]){
vel.x = -60;
}
else if (keys[39]){
vel.x = 60;
}

// 5
player.object.SetLinearVelocity(vel);
}
[/html]

The most complicated code in the above function is the first one, where we are checking the collision and writing some if else condition to determine the shape one or shape two.


On the second commented line (2), we retrieve the LinearVelocity of the player.


The third and forth commented sections verify if arrows are being pressed, and adjust the velocity vector(speed), accordingly.


In the fifth section, we setup the player with the new velocity vector.


Till now interactions are done, but if you run it now, it will only keep on jumping.



Step 5 - "You Win" Message


Add the code below to the beginning of your LinearVelocity function:



[html]
if (player.object.GetCenterPosition().y > canvasHeight){
player.object.SetCenterPosition(new b2Vec2(20,0),0)
}
else if (player.object.GetCenterPosition().x > canvasWidth-50){
showWin();
return;
}
[/html]

In this the first condition determines that if the player falls then it should be placed agin to the starting point.


The second condition is used to state if the player is in the final platform then display the winning function, which is below



[html]
function showWin(){
ctx.fillStyle = '#000';
ctx.font = '30px verdana';
ctx.textBaseline = 'top';
ctx.fillText('Yes! you made it!', 30, 0);
ctx.fillText('Thank You, www.webstutorial.com', 30, 30);
ctx.fillText('HTML 5 Rocks', 30, 60);
}
[/html]

And that's it, you have just made your own simple adventure game in HTML5.


Credit:NetTuts

download

2 comments :

  1. Black_strings014/12/11 3:20 AM

    Awesome! just awesome!

    ReplyDelete
  2. [...] break our game into pieces, which will help us to code easily. We will be using following [...]

    ReplyDelete