html5游戏制作博客,【翻译】制作你的第一个HTML 5游戏

最后你将创建的产品:

HTML 5以超乎任何人想象的速度发展。强大而专业的解决方案已经被开发出来了……甚至是在游戏的世界。今天,你可以利用 和HTML5 的canvas 标签来创建你的第一个游戏。

什么是Box2D?

Box2D是用来开发游戏和应用的一个流行的开源2D物理引擎。它主要是用C 来写的,已经被很多社区志愿者翻译成为各种语言。

用同样的方法和对象,你可以用各种语言来写你的游戏,比如C(iPhone/iPad),Actionscript 3.0(Web),Html 5(Web) ,等等。

第一步-创建项目

要开始开发你的例子,首先要下载Html 5 的Box2D引擎。接下来,按照下面所示的结构创建一个新的HTML文件(从box2d-js项目复制js和lib目录到你的游戏目录中)。

现在,你还应该在HTML文件中包含一些必须的文件来使用Box2D:

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

13.

14.

15.

16.

17.

18.

19.

20.

21.

22.

23.

24.

25.

26.

27.

28.

29.

30.

31.

32.

33.

34.

35.

36.

37.

38.

39.

40.

41.

42.

43.

44.

45.

46.

47.

48.

49.

50.

51.

52.

53.

54.

55.

56.

57.

58.

59.

60.

61.

62.

63.

64.

65.

66.

67.

是的,这是一个数量巨大的HTTP请求!

请注意,开发的时候,强烈建议你把这些源文件都集中到一个文件中。

接下来,创建两个新的script文件在/js/目录下,分别命名为"box2dutils.js"和"game.js"。

box2dutils.js—这是用一些例子的box2dlib中复制粘贴过来的,它对于绘制的函数很重要。

game.js—这是游戏自身的js。我们在这里创建平台,播放器,应用键盘交互等。

复制粘贴以下代码到box2dutils.js中。不要着急!我一点一点解释!

1.   function drawWorld(world, context) {

2.       for (var j = world.m_jointList; j; j = j.m_next) {

3.           drawJoint(j, context);

4.       }

5.       for (var b = world.m_bodyList; b; b = b.m_next) {

6.           for (var s = b.GetShapeList(); s != null; s = s.GetNext()) {

7.               drawShape(s, context);

8.           }

9.       }

10.     }

11.     function drawJoint(joint, context) {

12.         var b1 = joint.m_body1;

13.         var b2 = joint.m_body2;

14.         var x1 = b1.m_position;

15.         var x2 = b2.m_position;

16.         var p1 = joint.GetAnchor1();

17.         var p2 = joint.GetAnchor2();

18.         context.strokeStyle = '#00eeee';

19.         context.beginPath();

20.         switch (joint.m_type) {

21.         case b2Joint.e_distanceJoint:

22.             context.moveTo(p1.x, p1.y);

23.             context.lineTo(p2.x, p2.y);

24.             break;

25.

26.         case b2Joint.e_pulleyJoint:

27.             // TODO

28.             break;

29.

30.         default:

31.             if (b1 == world.m_groundBody) {

32.                 context.moveTo(p1.x, p1.y);

33.                 context.lineTo(x2.x, x2.y);

34.             }

35.             else if (b2 == world.m_groundBody) {

36.                 context.moveTo(p1.x, p1.y);

37.                 context.lineTo(x1.x, x1.y);

38.             }

39.             else {

40.                 context.moveTo(x1.x, x1.y);

41.                 context.lineTo(p1.x, p1.y);

42.                 context.lineTo(x2.x, x2.y);

43.                 context.lineTo(p2.x, p2.y);

44.             }

45.             break;

46.         }

47.         context.stroke();

48.     }

49.     function drawShape(shape, context) {

50.         context.strokeStyle = '#000000';

51.         context.beginPath();

52.         switch (shape.m_type) {

53.         case b2Shape.e_circleShape:

54.             {

55.                 var circle = shape;

56.                 var pos = circle.m_position;

57.                 var r = circle.m_radius;

58.                 var segments = 16.0;

59.                 var theta = 0.0;

60.                 var dtheta = 2.0 * Math.PI / segments;

61.                 // draw circle

62.                 context.moveTo(pos.x   r, pos.y);

63.                 for (var i = 0; i

64.                     var d = new b2Vec2(r * Math.cos(theta), r * Math.sin(theta));

65.                     var v = b2Math.AddVV(pos, d);

66.                     context.lineTo(v.x, v.y);

67.                     theta  = dtheta;

68.                 }

69.                 context.lineTo(pos.x   r, pos.y);

70.

71.                 // draw radius

72.                 context.moveTo(pos.x, pos.y);

73.                 var ax = circle.m_R.col1;

74.                 var pos2 = new b2Vec2(pos.x   r * ax.x, pos.y   r * ax.y);

75.                 context.lineTo(pos2.x, pos2.y);

76.             }

77.             break;

78.         case b2Shape.e_polyShape:

79.             {

80.                 var poly = shape;

81.                var tV = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[0]));

82.                 context.moveTo(tV.x, tV.y);

83.                 for (var i = 0; i

84.                    var v = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[i]));

85.                     context.lineTo(v.x, v.y);

86.                 }

87.                 context.lineTo(tV.x, tV.y);

88.             }

89.             break;

90.         }

91.         context.stroke();

92.     }

93.

94.     function createWorld() {

95.         var worldAABB = new b2AABB();

96.         worldAABB.minVertex.Set(-1000, -1000);

97.         worldAABB.maxVertex.Set(1000, 1000);

98.         var gravity = new b2Vec2(0, 300);

99.         var doSleep = true;

100.      var world = new b2World(worldAABB, gravity, doSleep);

101.      return world;

102.  }

103.

104.  function createGround(world) {

105.      var groundSd = new b2BoxDef();

106.      groundSd.extents.Set(1000, 50);

107.      groundSd.restitution = 0.2;

108.      var groundBd = new b2BodyDef();

109.      groundBd.AddShape(groundSd);

110.      groundBd.position.Set(-500, 340);

111.      return world.CreateBody(groundBd)

112.  }

113.

114.  function createBall(world, x, y) {

115.      var ballSd = new b2CircleDef();

116.      ballSd.density = 1.0;

117.      ballSd.radius = 20;

118.      ballSd.restitution = 1.0;

119.      ballSd.friction = 0;

120.      var ballBd = new b2BodyDef();

121.      ballBd.AddShape(ballSd);

122.      ballBd.position.Set(x,y);

123.      return world.CreateBody(ballBd);

124.  }

125.

126.  function createBox(world, x, y, width, height, fixed, userData) {

127.      if (typeof(fixed) == 'undefined') fixed = true;

128.      var boxSd = new b2BoxDef();

129.      if (!fixed) boxSd.density = 1.0;

130.

131.      boxSd.userData = userData;

132.

133.      boxSd.extents.Set(width, height);

134.      var boxBd = new b2BodyDef();

135.      boxBd.AddShape(boxSd);

136.      boxBd.position.Set(x,y);

137.      return world.CreateBody(boxBd)

138.    }

第二步-开发游戏

打开你之前创建的index.html文件,在body元素里添加一个canvas元素(600*400)。这就是我们应用HTML 5绘图的API的地方。

当然,到了这里,我们应该插入game.js和box2dutils.js。

1.

2.

HTML就是这样!现在我们的乐趣在于Javascript.

打开game.js,插入一下代码:

1.   // some variables that we gonna use in this demo

2.   var initId = 0;

3.   var player = function(){

4.       this.object = null;

5.       this.canJump = false;

6.   };

7.   var world;

8.   var ctx;

9.   var canvasWidth;

10.     var canvasHeight;

11.     var keys = [];

12.

13.     // HTML5 onLoad event

14.     Event.observe(window, 'load', function() {

15.         world = createWorld(); // box2DWorld

16.         ctx = $('game').getContext('2d'); // 2

17.         var canvasElm = $('game');

18.         canvasWidth = parseInt(canvasElm.width);

19.         canvasHeight = parseInt(canvasElm.height);

20.         initGame(); // 3

21.         step(); // 4

22.

23.     // 5

24.         window.addEventListener('keydown',handleKeyDown,true);

25.         window.addEventListener('keyup',handleKeyUp,true);

26.     });

Box2DWorld-这是我们存在的地方

好吧,让我们弄清楚这个代码块做什么!

Box2DWorld是Box2D提供的一个核心类。他的功能很简单:

将一切结合到一个类中。在Box2DWorld中,有你的游戏或应用的bodies定义和碰撞管理。

打开game.js和box2dutils.js文件,在box2dutils.js中查找createWorld()函数。

1.   function createWorld() {

2.       // here we create our world settings for collisions

3.       var worldAABB = new b2AABB();

4.       worldAABB.minVertex.Set(-1000, -1000);

5.       worldAABB.maxVertex.Set(1000, 1000);

6.       // set gravity vector

7.       var gravity = new b2Vec2(0, 300);

8.       var doSleep = true;

9.       // init our world and return its value

10.         var world = new b2World(worldAABB, gravity, doSleep);

11.         return world;

12.     }

创建box2DWorld很简单。

回到game.js

参照上述两块代码中的注释数字。

在第二处,我们用选择API获取canvas元素的上下文(看起来像jQuery或者MooTools 选择器,是吗? )

在第三处,有一个新的有趣的函数:initGame(). This is where we create the scenery.

把下面的代码复制粘贴到game.js。我们再一起回头看它。

1.   function initGame(){

2.       // create 2 big platforms

3.       createBox(world, 3, 230, 60, 180, true, 'ground');

4.       createBox(world, 560, 360, 50, 50, true, 'ground');

5.

6.       // create small platforms

7.       for (var i = 0; i

8.           createBox(world, 150 (80*i), 360, 5, 40 (i*15), true, 'ground');

9.       }

10.

11.         // create player ball

12.         var ballSd = new b2CircleDef();

13.         ballSd.density = 0.1;

14.         ballSd.radius = 12;

15.         ballSd.restitution = 0.5;

16.         ballSd.friction = 1;

17.         ballSd.userData = 'player';

18.         var ballBd = new b2BodyDef();

19.         ballBd.linearDamping = .03;

20.         ballBd.allowSleep = false;

21.         ballBd.AddShape(ballSd);

22.         ballBd.position.Set(20,0);

23.         player.object = world.CreateBody(ballBd);

24.

25.     }

26.

27.     Inside box2dutils.js, we've created a function, called createBox. This creates a static rectangle body.

28.

29.     function createBox(world, x, y, width, height, fixed, userData) {

30.         if (typeof(fixed) == 'undefined') fixed = true;

31.         //1

32.     var boxSd = new b2BoxDef();

33.         if (!fixed) boxSd.density = 1.0;

34.         //2

35.         boxSd.userData = userData;

36.         //3

37.         boxSd.extents.Set(width, height);

38.

39.         //4

40.         var boxBd = new b2BodyDef();

41.         boxBd.AddShape(boxSd);

42.         //5

43.         boxBd.position.Set(x,y);

44.         //6

45.         return world.CreateBody(boxBd)

46.     }

Box2DBody

一个Box2DBody拥有一些特性:

它可以是静态的(不受碰撞影响),运动的(但不是由于碰撞,比如你可以用胡彪移动它),或者动态的(与一切交互)。

必须有一个形状定义,并且说明这个对象是怎样出现的。

必须有至少两个fixture,说明他们碰撞后是怎样交互的。

许多引擎中设置的位置是中心,而不是左上角。

回顾这段代码:

在这里,我们创建了一个形状定义,它可以是一个正方形或者长方形,还设置了它的密度(它被强制移动或旋转的频率)。

我们设置了userDate,通常我们会设置图形对象,但是在这个例子中,我只设置了字符串作为碰撞对象的类型标识符。这个参数不影响物理算法。

设置我的box尺寸的一半(从位置点或者中心点到角落的一条线)。

我们创建了物体的定义并且为它添加了形状定义。

设置位置。

在世界中创建物体并且返回它的值。

创建球体玩家

我已经直接在game.js的编码中创建了玩家(球)。它和创建方形遵循一样的步骤,但是,这次,它是一个球。

1.   var ballSd = new b2CircleDef();

2.       ballSd.density = 0.1;

3.       ballSd.radius = 12;

4.       ballSd.restitution = 0.5;

5.       ballSd.friction = 1;

6.       ballSd.userData = 'player';

7.       var ballBd = new b2BodyDef();

8.       ballBd.linearDamping = .03;

9.       ballBd.allowSleep = false;

10.         ballBd.AddShape(ballSd);

11.         ballBd.position.Set(20,0);

12.         player.object = world.CreateBody(ballBd);

所以,我们怎样一步一步创建一个物体?

创建形状,fixtures或者sensor。

创建物体定义。

将形状,fixtures或者sensor添加到物体中。(这篇文章中不解释)。

在世界中创建物体。

Box2DCircle

就像我前面提到的,这和创建一个box遵循同样的步骤,但是现在你需要设置一些新的属性。

半径--这是从圆中心到边界上任一点线的长度。

restitution--球在和其它物体碰撞的时候怎样失去或获得force

摩擦—这个球会怎么滚。

Box2DBody的更多属性

damping可以减缓物体速度—有线性阻尼和有角阻尼。

sleep  在box2D中,物体可以通过休眠来解决性能问题。比如,我们假设你现在在开发一个平台游戏,并且是一个6000*400的屏幕上。

为什么我们需要管屏幕外的物体的物理表现?不需要。这就是关键!所以正确的选择是让它们休眠,来提高你的游戏的性能。

我们已经创建了世界;你可以试验目前为止的代码,你会看到玩家落在西边的平台上。

现在,如果你试着跑这个例子,你应该好奇,为什么这个页面和白纸一样简单。

任何时候都要记住:Box2D不渲染,它只提供物理计算。

第三步-渲染时间

接下来,让我们来渲染Box2DWorld。

打开game.js,添加下面代码:

1.   function step() {

2.

3.       var stepping = false;

4.       var timeStep = 1.0/60;

5.       var iteration = 1;

6.       // 1

7.       world.Step(timeStep, iteration);

8.       // 2

9.       ctx.clearRect(0, 0, canvasWidth, canvasHeight);

10.         drawWorld(world, ctx);

11.         // 3

12.         setTimeout('step()', 10);

13.        }

这里我们实现了:

指示box2dWorld完成物理模拟。

清空canvas重新绘制。

10微秒执行一次stup()函数

有了这一小段代码,我们现在可以实现物理和绘制。

你可以自己试验一下,注意看这个落下的小球,跟下面一样。

Box2dutils.js里的drawWorld

1.   function drawWorld(world, context) {

2.       for (var j = world.m_jointList; j; j = j.m_next) {

3.           drawJoint(j, context);

4.       }

5.       for (var b = world.m_bodyList; b; b = b.m_next) {

6.           for (var s = b.GetShapeList(); s != null; s = s.GetNext()) {

7.               drawShape(s, context);

8.           }

9.       }

10.     }

我们上面写的是一个调试函数,利用html5中绘图的API将我们的世界画到canvas这块画布上。

第一层循环绘制所有的关节。我们在这里并没有用到关节,它们对于第一个例子来说有点复杂,但是,不管怎样,它们对于游戏是必不可少的。它们让你能创建非常有趣的物体。

第二层循环绘制所有的物体。

1.   function drawShape(shape, context) {

2.       context.strokeStyle = '#000000';

3.       context.beginPath();

4.       switch (shape.m_type) {

5.       case b2Shape.e_circleShape:

6.           {

7.               var circle = shape;

8.               var pos = circle.m_position;

9.               var r = circle.m_radius;

10.                 var segments = 16.0;

11.                 var theta = 0.0;

12.                 var dtheta = 2.0 * Math.PI / segments;

13.                 // draw circle

14.                 context.moveTo(pos.x   r, pos.y);

15.                 for (var i = 0; i

16.                     var d = new b2Vec2(r * Math.cos(theta), r * Math.sin(theta));

17.                     var v = b2Math.AddVV(pos, d);

18.                     context.lineTo(v.x, v.y);

19.                     theta  = dtheta;

20.                 }

21.                 context.lineTo(pos.x   r, pos.y);

22.

23.                 // draw radius

24.                 context.moveTo(pos.x, pos.y);

25.                 var ax = circle.m_R.col1;

26.                 var pos2 = new b2Vec2(pos.x   r * ax.x, pos.y   r * ax.y);

27.                 context.lineTo(pos2.x, pos2.y);

28.             }

29.             break;

30.         case b2Shape.e_polyShape:

31.             {

32.                 var poly = shape;

33.                var tV = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[0]));

34.                 context.moveTo(tV.x, tV.y);

35.                 for (var i = 0; i

36.                    var v = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[i]));

37.                     context.lineTo(v.x, v.y);

38.                 }

39.                 context.lineTo(tV.x, tV.y);

40.             }

41.             break;

42.         }

43.         context.stroke();

44.     }

我们遍历对象的每一个顶点,并用线绘制(Context.MoveTo和Context.LineTo)。现在,它对于一个例子很有用,但是在实践中并不是很有用。当你使用图形的时候,你只需要注意物体位置,并不需要像这个项目一样去遍历顶点。

第四步—交互

一个游戏没有交互就是电影,而如果一个电影有交互就成了一个游戏。(O(╯□╰)o,强烈不赞同!)

让我们来写方向键控制跳和移动的功能。

添加这些代码到game.js

1.   function handleKeyDown(evt){

2.       keys[evt.keyCode] = true;

3.   }

4.

5.   function handleKeyUp(evt){

6.       keys[evt.keyCode] = false;

7.   }

8.

9.   // disable vertical scrolling from arrows :)

10.     document.οnkeydοwn=function(){return event.keyCode!=38 && event.keyCode!=40}

我们设置了一个数组来记录用户的每一个按键操作handleKeyDown和handleKeyUp。通过

document.onKeydown,我们禁止了浏览器本身上下键的滚屏功能。你以前玩过这样的HTML

5游戏吗?当你跳的时候,玩家,障碍和物体全部从屏幕小时?现在这不再是一个问题。

将下面这小段代码添加到stup()函数的开头。

1.   handleInteractions();

function handleInteractions(){

1.       // up arrow

2.       // 1

3.       var collision = world.m_contactList;

4.       player.canJump = false;

5.       if (collision != null){

6.          if (collision.GetShape1().GetUserData() == 'player' || collision.GetShape2().GetUserData() == 'player'){

7.              if ((collision.GetShape1().GetUserData() == 'ground' || collision.GetShape2().GetUserData() == 'ground')){

8.                  var playerObj = (collision.GetShape1().GetUserData() == 'player' ? collision.GetShape1().GetPosition() :  collision.GetShape2().GetPosition());

9.                  var groundObj = (collision.GetShape1().GetUserData() == 'ground' ? collision.GetShape1().GetPosition() :  collision.GetShape2().GetPosition());

10.                     if (playerObj.y

11.                         player.canJump = true;

12.                     }

13.                 }

14.             }

15.         }

16.         // 2

17.         var vel = player.object.GetLinearVelocity();

18.         // 3

19.         if (keys[38] && player.canJump){

20.             vel.y = -150;

21.         }

22.

23.         // 4

24.         // left/right arrows

25.         if (keys[37]){

26.             vel.x = -60;

27.         }

28.         else if (keys[39]){

29.             vel.x = 60;

30.         }

31.

32.         // 5

33.         player.object.SetLinearVelocity(vel);

34.     }

上面的代码最复杂的是第一块。我们检查碰撞,写了一个条件来判断shape1和shape2是否是玩家,如果是的话,我

们再判断shape1或者shape2是不是地面物体。如果是的话,那么玩家就在撞击地面。接下来,我们检查玩家是否在地面上,如果是的话,那么玩家可以

跳。

第二块那行代码,我们检索玩家的LinearVelocity。

第三块和第四块代码验证是否按下了方向键,并且调整速度矢量。因此在第五块代码里,我们为玩家设置了新的速度矢量。

交互就做完了!但是我们没有目标,只是跳,跳,跳……跳!

第五步—“你赢了”消息

在你的LinearVelocity的开头添加下面代码

1.   if (player.object.GetCenterPosition().y > canvasHeight){

2.       player.object.SetCenterPosition(new b2Vec2(20,0),0)

3.   }

4.   else if (player.object.GetCenterPosition().x > canvasWidth-50){

5.       showWin();

6.       return;

7.   }

第一个条件判断是否落下了,是否需要返回起点(西边平台)。

第二个条件检查玩家是否到达第二个平台赢得游戏。下面是showWin()函数

1.   function showWin(){

2.       ctx.fillStyle    = '#000';

3.       ctx.font         = '30px verdana';

4.       ctx.textBaseline = 'top';

5.       ctx.fillText('Ye! you made it!', 30, 0);

6.       ctx.fillText('thank you, andersonferminiano.com', 30, 30);

7.       ctx.fillText('@andferminiano', 30, 60);

8.   }

就是这样!你刚才完成了一个简单的用HTML 5和Box2D写的游戏,恭喜你!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值