HTML5之2D物理引擎 Box2D for javascript Games 系列 第一部分

 

我要的是能在H5页面上跑的javascript版的Box2D啊!!!

最近想学习Javascript版本的Box2D JS物理引擎,无奈搜了半天也没找到相对比较系统的资料

官方网站也只是简单的介绍,API还引导向了FLASH AS3脚本。

 

我要的是能在H5页面上跑的javascript版本的教程啊!!!

 

后来搜出了一本中文版Box2D for Flash Games,脚本是AS3版本的书。是由天地会(昵称:鲁邦三世)翻译的

看,书封面

 

没有Javascript版本的啊。点解?(υ◉ω◉υ)

 

So... 我感觉上帝选中了我⋋(◍’Θ’◍)⋌

以前的我是AS3脚本程序猿出生, 那么唯有用我丢掉的几年AS3脚本经验把它改写成Javascript版本的了,谁让我现在写的是Javascript呢。

看…我做的封面

 

完美!!!

用Fireworks把原来的封面做成了javascript,可见我功力了吧,请叫我美工殿下!

我最早是做美工出生我会跟你说?(/ ̄(エ) ̄)/

 

好吧,那我就一边学,一边改成javascript版本了。

如果文章有侵权行为,那么,要么联系我删掉?

要么来告我,反正我也没钱(ミ ̄ー ̄ミ)

我的邮箱willian12345@126.com

 

本系列源码持续更新中,已寄存在github上

https://github.com/willian12345/Box2D-for-Javascript-Games

 

开始前的一些说明 

你必定假设你对javascript或者前端知识已经比较熟悉了,如果不熟悉的话你得先补一下前端知识再往下看

FLASH中的舞台对应网页中的Canvas

AS3 (ActionScript3.0)脚本对应网页中的Javascript

 

 

2D物理引擎中的一些概念名词翻译列表

rigid body :刚体


fixture :夹具


box :盒子或矩形

debug draw : 调试绘图

density :密度


friction :摩擦或摩擦系数

restitution : 恢复或恢复系数

force : 力或作用力


impulse : 冲量


linear velocity : 线速度或线速率

joint : 关节


motor : 马达


bullet : 子弹


sensor : 感应器

 

 

目录


 

 

 

第一章 Hello Box2D World

 

定义Box2D世界

运行模拟

概述

 

第二章 向世界添加刚体

 

你的第一个模拟----一个球落地

创建一个圆形形形状

创建夹具

使用调试绘制测试你的模拟

创建矩形形状

不同的刚体类型----static, dynamic 和 kinematic

密度,摩擦和恢复

创建图腾破坏者的关卡

创建复合刚体

创建定向矩形

创建各种类型的凸多边形

概述

 

第三章 刚体的交互

 

通过鼠标点击选择并销毁刚体

将自定义属性指定到刚体上

遍历刚体并获取它的属性

概述

 

第四章 将力作用到刚体上

 

苹果掉落,修正

力,冲量和线速率

应用冲量来得到线速度

应用力来获得线速度

将力应用到真实的游戏中

物理游戏不只是关于物理

放置物理小鸟

发射物理小鸟

概述

 

第五章 碰撞处理

 

碰撞检查

Box2D内建的碰撞监听

将碰撞开始和结束输出到输出窗口

检测当你要解决碰撞和当你解决了碰撞

在图腾破坏者中检测神像坠落地面

在愤怒的小鸟中销毁砖块并消灭小猪

概述

 

第六章 关节和马达

 

拾取并拖拽刚体—鼠标关节

让刚体之间保持给定的距离—距离关节

使刚体绕一个点旋转—旋转关节

当愤怒的小鸟遇见粉碎城堡

通过马达控制关节

通过键盘控制马达

让一些刚体不要发生碰撞—碰撞过滤

将它们放在一起

概述

 

第七章

使用你自己的图像资源代替调试绘图

概述

 

第八章 子弹和传感器

 

感受隧道效应

阻止隧道效应—设置刚体为子弹

通过传感器检测接触,可以允许刚体重叠

 

第一章

1、Hello Box2D World

 


 

如果你想创建2D的物理驱动游戏与应用,Box2D是最佳的有效选择。

Box2D是一个 2D刚体的仿真库,它被使用在一些最成功的游戏上,例如在iPhone上的Angry Birds 和Tiny Wings或者在Flash上的Totem Destroyer和Red Remover。

Google一下它们,你 将会发现很多热心的评论。

在我们进入Box2D世界之前,让我说明一下什么是刚体(Rigid Bodies)。

它是一块非 常坚硬的物质,任何方法都不能使它弯曲。无论你怎样用力去撞击(Hit)或投掷 (Throw)它,都无法改变它的形状。

在真实世界中,你可以将它的硬度想象成钻 石,甚至比钻石还要硬。也许你可以展开想象,假设它是来至外空间的一块不可变形 的物质。

Box2D只管理刚体(Rigid Bodies),从现在起,我们将称它为刚体(Bodies)(这句 话中文是看不出什么不同的,英文将Rigid Bodies简称为Bodies),不用当心,你将还 可以模仿不是刚性的材料,例如弹性球体。

让我们看看,你将在本章节学习到那些知识:

• 安装Box2D


• 创建你的第一个Box2D世界


• 了解重力和睡眠刚体

• 运行程序,操作时间步和约束

本章结束后,你将能创建一个空的可运行的世界,在那里你可以搭建你那了不起的物理游戏。

 

 

在网页中安装Box2D


 

你可以从官方站点下载最新的Box2D.js版本或在我的github上直接下载合并好的源码 https://github.com/willian12345/Box2D-for-Javascript-Games/

 

新建一个demo1-1.html页面

下载到Box2D.js版本后放在网页同级目录或其它目录

根据目录在网页中直接引用

<script src="Box2d.js"></script>

可比在比FLASH简单多了

网页中再添加

<script type="text/javascript">

            function init(){

            function main(){

               console.log(Box2D);     

            }

 

            main();

         }

init();

</script>

 

测试一把

 

在浏览器中打开1.html

打开浏览器调试工具,推荐用chrome浏览器。

应该能看到调试器的Console内输出Object {Collision: Object, Common: Object, Dynamics: Object}

 

init()方法可以在onload时调用, init方法内再建一个main方法

模拟FLASH中自动调用的Main.as类。这里需要手动调用。当然你也随便建方法

 

完整代码在demo1-1.html中

 

第一步成功!

为了方便,在init方法一开始内可以添加以下代码,方便对象的使用

var b2Vec2 = Box2D.Common.Math.b2Vec2

   ,b2AABB = Box2D.Collision.b2AABB

   ,b2BodyDef = Box2D.Dynamics.b2BodyDef

   ,b2Body = Box2D.Dynamics.b2Body

   ,b2FixtureDef = Box2D.Dynamics.b2FixtureDef

   ,b2Fixture = Box2D.Dynamics.b2Fixture

   ,b2World = Box2D.Dynamics.b2World

   ,b2MassData = Box2D.Collision.Shapes.b2MassData

   ,b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape

   ,b2CircleShape = Box2D.Collision.Shapes.b2CircleShape

   ,b2DebugDraw = Box2D.Dynamics.b2DebugDraw

   ,b2MouseJointDef =  Box2D.Dynamics.Joints.b2MouseJointDef

   ;

 

先不用管具体做什么用,后面会一一用到的

 

当我给本节标题命名为Hello Box2D World时,我并不只是为了创建另一个"Hello World"程序,而是我想要介绍所有Box2D模拟和事件发生的环境:世界(World)。

世界(World)是模拟发生的舞台。你想要通过Box2D物理引擎控制的所有事物必须

在世界(World)中。幸运的是,Box2D世界(World)拥有足够大的空间来容纳你 需要的任何事物,所以你无需担心世界(World)的边界(boundaries)。

你只需要 记住在电脑中的任何事物都要受到某种限制。所以,越大的世界(World),将会 消耗你的电脑越多的资源去管理它。

定义Box2D世界


与现实世界一样,Box2D世界(World)有重力(gravity),所以你需要先定义世界 重力(world gravity)。

1. 在你的main方法中,添加下面的一行代码:

var gravity = new b2Vec2(0,9.81);

这里将介绍我们的第一个Box2D数据类型:b2Vec2。(注:在javascript中可没这个数据类型,把它当成一个对象就好了

b2Vec2是一个2D的纵向量数据类型,它将储存x和y矢量分量。如你所见,构 造函数有两个参数,都是数值,代表了x和y分量。通过这种方法我们定义 gravity变量作为一个矢量,它有x=0(这意味着水平的重力)和y=-9.81(这意 味着近似的地球重力)。

物理学中说过,一个物体在地球表面自由下落的加速度近似为9.81m/s^2(米 每平方秒)也可写作"m/s/s"。所以,假设没有任何空气阻力,我们在模拟一 个真实的世界(real-world)环境。解释物体下落的原理已经超越本书的范 围,但是你可以在Google或Wikipedia中搜索"equations for a falling body"去获得 更多的信息。

 

2. 你可以设置你的游戏为下面的这行代码:


var gravity = new b2Vec2(0,1.63);

你也可以将参数设置为(0,0)来模拟一个没有重力的环境:

var gravity = new b2Vec2(0,0); 

3. 我们还需要告诉世界,当世界中的刚体静止时,可以允许他们进入睡眠状态,这样它们将不受作用力的影响。

一个睡眠的刚体无需模拟,它只是表示自 己的存在,并静止在它的位置上,不会对世界中的任何事物产生影响,允许 Box2D忽略它,而且因此会提升处理速度以及让我们获得更好的性能。

所以推 荐可能时让刚体睡眠。 


4. 添加下面的一行,它只是一个简单的布尔(Boolean)变量定义:

var sleep = true; 


5. 最后,我们准备创建我们的第一个世界(world):


var world = new b2World(gravity,sleep); 


6.现在我们有一个容器来管理所有的刚体并且执行我们的动态模拟。

7.让我们来简单的回顾一下之前的代码,此刻,你的代码应该看起如下面 所示: 

<script type="text/javascript">
         function init(){
            var b2Vec2 = Box2D.Common.Math.b2Vec2
            ,b2AABB = Box2D.Collision.b2AABB
            ,b2BodyDef = Box2D.Dynamics.b2BodyDef
            ,b2Body = Box2D.Dynamics.b2Body
            ,b2FixtureDef = Box2D.Dynamics.b2FixtureDef
            ,b2Fixture = Box2D.Dynamics.b2Fixture
            ,b2World = Box2D.Dynamics.b2World
            ,b2MassData = Box2D.Collision.Shapes.b2MassData
            ,b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape
            ,b2CircleShape = Box2D.Collision.Shapes.b2CircleShape
            ,b2DebugDraw = Box2D.Dynamics.b2DebugDraw
            ,b2MouseJointDef =  Box2D.Dynamics.Joints.b2MouseJointDef
            ;
            
            var gravity = new b2Vec2(0, 9.81);
            var sleep = true;
            var world = new b2World(gravity, sleep);
            function main(){
               
            }

            main();
         }
         init();
      </script>

现在你学习了怎样去创建并配置一个Box2D世界。让我们来看看你将怎样在它里面实 现物理效果的模拟 。

 

 

 

运行模拟


你需要在每一帧都进行模拟,所以首先你需要一个监听来触发每一帧

1. 让我们添加一点代码:

<script>
         function init(){
            var b2Vec2 = Box2D.Common.Math.b2Vec2
            ,b2AABB = Box2D.Collision.b2AABB
            ,b2BodyDef = Box2D.Dynamics.b2BodyDef
            ,b2Body = Box2D.Dynamics.b2Body
            ,b2FixtureDef = Box2D.Dynamics.b2FixtureDef
            ,b2Fixture = Box2D.Dynamics.b2Fixture
            ,b2World = Box2D.Dynamics.b2World
            ,b2MassData = Box2D.Collision.Shapes.b2MassData
            ,b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape
            ,b2CircleShape = Box2D.Collision.Shapes.b2CircleShape
            ,b2DebugDraw = Box2D.Dynamics.b2DebugDraw
            ,b2MouseJointDef =  Box2D.Dynamics.Joints.b2MouseJointDef
            ;

            var gravity = new b2Vec2(0, 9.81);
            var sleep = true;
            var world = new b2World(gravity, sleep);
            function main(){
               setInterval(updateWorld, 1000 / 60);
            }
            function updateWorld() {
               console.log("my awesome simulation runs here");
            }

            main();
         }
         init();
      </script>

 

没有什么新的,我们只是添加了一个setInterval循环定时执行,但是我们需要它有 序的运行updateWorld()方法中的模拟。

Box2D是通过模拟世界的离散时间步(step time)来进行模拟工作的。

这意味 着世界将在每一个时间步被更新。

这将取决于我们在模拟中所采用的时间 步。通常情况下,物理游戏的时间步为1/60秒。

 

2. 下面是updateWorld函数的第一行: 

var timeStep  = 1/30 

只是定义了时间步还不够。在每一步,每一个物理实体(physic entity)根据 作用于自身的作用力来更新(不包括睡眠状态)。

处理这项任务的算法叫约 束解算器(constraint solver)。

 

它是基于循环每一个约束然后解算来进的,一次一个,如果你想要学习更多的关于约束的知识,在google在搜 索"constraint algorithm"。

Where's the catch? 虽然单个约束可以被完美的解算,但是多个约束时,它 会搅乱之前已经解算的别的约束。

试想,当两个球移动的时候:在真实的世界,每一个球的位置是在相同的时间更新。在电脑的模拟中,我们需要通过循环来更新球的位置,而每次只能一个。试想一下for循环每次遍历更新一个球。只要球彼此间没有发生 相互作用,一切正常运行,但是如何第二个球撞击了第一个球,谁的位置被更新了?它们会重叠,这在刚体模拟中是不可能的事情。

通过取合适的近似值来解决这个问题,我们需要循环所有的约束不止一次。现在问题是:我们要循环多少次?

有两种约束解算器:速率约束解算器(velocity constraint solver)和位置约束解 算器(position constraint solver)。速率约束解算器依据它们的在世界中的冲量 来移动物理实体。位置约束解算器调整物理实体的位置避免重叠。

所以,越高的便利次数,将会有更精确的模拟,但是性能会更低。我设法处 理超过100个物理实体使用10次速率和位置遍历,虽然Box2D的作者推荐8次 速率和3次位置遍历。

这将由你来决定使用的值。与此同时,我将使用对两个约束解算器使用10次遍 历。 
我们设置了两个新变量: 
 

var velIterations:int=10;

var posIterations:int=10;

注:先不用理解太深,反正我也没看懂,不管这个“约束”先 (=◎ω◎=)

最后我们准备调用world变量的step方法来更新模拟。 


在updateWorld方法中使用world,我们需要把world作为类变量声明,如下所 示:



<script>
         function init(){
            var b2Vec2 = Box2D.Common.Math.b2Vec2
            ,b2AABB = Box2D.Collision.b2AABB
            ,b2BodyDef = Box2D.Dynamics.b2BodyDef
            ,b2Body = Box2D.Dynamics.b2Body
            ,b2FixtureDef = Box2D.Dynamics.b2FixtureDef
            ,b2Fixture = Box2D.Dynamics.b2Fixture
            ,b2World = Box2D.Dynamics.b2World
            ,b2MassData = Box2D.Collision.Shapes.b2MassData
            ,b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape
            ,b2CircleShape = Box2D.Collision.Shapes.b2CircleShape
            ,b2DebugDraw = Box2D.Dynamics.b2DebugDraw
            ,b2MouseJointDef =  Box2D.Dynamics.Joints.b2MouseJointDef
            ;

            
            var world;
            function main(){
               var gravity = new b2Vec2(0, 9.81);
               var sleep = true;
               world = new b2World(gravity, sleep);

               setInterval(updateWorld, 1000 / 60);
            }
            function updateWorld() {
               var timeStep = 1/30;
               var velIterations = 10;
               var posIterations = 10;
               world.Step(timeStep,velIterations,posIterations);
            }

            main();
         }
         init();
      </script>

现在我们有了我们自己的世界配置,然后运行。不幸的是,这是一个非常空

  洞的世界,它的里面没有任何东西。所以在下一章,我们将向世界中填充各

  种各样的物理实体。

 

最后还有一件事情,在每一步之后,你需要清除作用力,让模拟从 下一步再次开始。

你可以将这行world.ClearForces();代码添加到step方法之后;你最后的代 码如下所示: 

<script>
         function init(){
            var b2Vec2 = Box2D.Common.Math.b2Vec2
            ,b2AABB = Box2D.Collision.b2AABB
            ,b2BodyDef = Box2D.Dynamics.b2BodyDef
            ,b2Body = Box2D.Dynamics.b2Body
            ,b2FixtureDef = Box2D.Dynamics.b2FixtureDef
            ,b2Fixture = Box2D.Dynamics.b2Fixture
            ,b2World = Box2D.Dynamics.b2World
            ,b2MassData = Box2D.Collision.Shapes.b2MassData
            ,b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape
            ,b2CircleShape = Box2D.Collision.Shapes.b2CircleShape
            ,b2DebugDraw = Box2D.Dynamics.b2DebugDraw
            ,b2MouseJointDef =  Box2D.Dynamics.Joints.b2MouseJointDef
            ;

            
            var world;
            function main(){
               var gravity = new b2Vec2(0, 9.81);
               var sleep = true;
               world = new b2World(gravity, sleep);

               setInterval(updateWorld, 1000 / 60);
            }
            function updateWorld() {
               var timeStep = 1/30;
               var velIterations = 10;
               var posIterations = 10;
               world.Step(timeStep,velIterations,posIterations);
               world.ClearForces(); // 清除作用力
            }

            main();
         }
         init();
</script>

 

 

源码全部代码在github上的demo1-1.html中,可查看运行

概述

你刚刚学习了怎样为在网页中安装使用Box2D。将它包含到你的项目中并运行,重力规则模 拟,管理时间步以及约束解算器。

到现在为止,你的网页上其实并没有显示任何东西。

你有一个空的世界,准备成为你游戏发生的容器。保存它然后在每一个未来的项目中使用它!

 

 

 

 



注:转载请注明出处博客园:sheldon-二狗-偷饭猫(willian12345@126.com)

https://github.com/willian12345

 

posted @ 2016-05-10 15:10  池中物sheldon  阅读(6626)  评论(0编辑  收藏  举报
转载入注明博客园池中物willian12345@126.com sheldon.wang