2D物理引擎 Box2D for javascript Games 第一章 Hello Box2D World

2D物理引擎 Box2D for javascript Games 第一章

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

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

官方网站也只是简单的介绍,找到一本别人翻译的 Box2d for Flash Games 。

image

没有 Javascrip t版本的啊。(υ◉ω◉υ)

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

So... ⋋(◍’Θ’◍)⋌

以前的我是 AS3 脚本程序出生, 那么唯有用我丢掉的近10年 AS3 脚本经验把它改写成Javascript版本的了

谁让我现在写的是Javascript呢。

看…我做的封面

image

完美!!!

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

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

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

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

开始前的一些说明

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

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

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

后期还会用到 createjs 用于渲染代替 Flash 中的 Graphics 类

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

感觉翻译的不是很好,我就尽量按中文版与英文版都对照着用 Javascript 重写一篇这本书吧

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 结合图形库实现渲染

第三章 刚体的交互

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

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

  • 遍历刚体并获取它的属性

  • 小结

第四章 将力作用到刚体上

  • 苹果掉落,修正

  • 力,冲量和线速率

  • 应用冲量来得到线速度

  • 应用力来获得线速度

  • 将力应用到真实的游戏中

  • 物理游戏不只是关于物理

  • 放置物理小鸟

  • 发射物理小鸟

  • 小结

第五章 碰撞处理

  • 碰撞检查

  • Box2D内建的碰撞监听

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

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

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

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

  • 小结

第六章 关节和马达

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

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

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

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

  • 通过马达控制关节

  • 通过键盘控制马达

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

  • 将它们放在一起

  • 小结

第七章

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

  • 小结

第八章 子弹和传感器

  • 感受隧道效应

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

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

第一章 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

打开浏览器调试工具

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

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

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

完整代码在 demo1-1.html

第一步成功!

Box2D 的 “命名空间” 太长了,为了方便,在 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 (译:可以理解为帧频)来进行模拟工作的。

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

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

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

    var timeStep = 1/30

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

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

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

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

    试想,当两个球移动的时候: 在真实的世界,每一个球的位置是在相同的时间更新。

    在电脑的模拟中,我们需要通过循环来更新球的位置,而每次只能一个。

    试想一下for循环每次遍历更新一个球。只要球彼此间没有发生 相互作用,一切正常运行,但是如何第二个球撞击了第一个球,谁的位置被更新了?它们会重叠,这在刚体模拟中是不可能的事情。

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

    有两种约束解算器: 速率约束解算器(velocity constraint solver)和位置约束解算器(position constraint solver)。

    速率约束解算器依据它们的在世界中的冲量来移动物理实体。

    位置约束解算器调整物理实体的位置避免重叠。

    所以,越高的便利次数,将会有更精确的模拟,但是性能会更低。我将设法处理超过 100 个物理实体设置分别设置位置 10 速率约束和位置约束解算器为 10 次迭代, Box2D 的作者推荐8次速率和 3 次位置遍历。

    具体设置多少你自己来定。

  3. 与此同时,我将使用对两个约束解算器使用10次遍 历。 我们设置了两个新变量:

    var velIterations:int = 10;
    
    var posIterations:int = 10;
    

    (译者注:先不用理解太深,不管这个“约束”先 (=◎ω◎=))

  4. 最后我们准备调用 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>
    

    现在我们有了我们自己的世界配置,然后运行。不幸的是,这是一个非常空洞的世界,它的里面没有任何东西。

    所以在下一章,我们将向世界中填充各种各样的物理实体。

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

    你可以将这行 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  池中物王二狗  阅读(7215)  评论(0编辑  收藏  举报
转载入注明博客园 王二狗Sheldon Email: willian12345@126.com https://github.com/willian12345