2D物理引擎 Box2D for javascript Games 第一章
我要的是能在 H5 页面上跑的 javascript 版的 Box2D 教程啊!!!
最近想学习 Javascript 版本的 Box2D JS 物理引擎,无奈搜了半天也没找到相对比较系统的资料
官方网站也只是简单的介绍,找到一本别人翻译的 Box2d for Flash Games 。
没有 Javascript 版本的啊。(υ◉ω◉υ)
我要的是能在 H5 页面上跑的 javascript 版本的教程啊!!!
So... ⋋(◍’Θ’◍)⋌
以前的我是 AS3 脚本程序出生, 那么唯有用我丢掉的近10年 AS3 脚本经验把它改写成Javascript版本的了
谁让我现在写的是Javascript呢。
看…我做的封面
完美!!!
用 Fireworks 把原来的封面做成了javascript,可见我功力了吧,请叫我美工殿下!
好吧,那就一边学,一边改成 javascript 版本了。
本系列源码持续更新中,已寄存在 github 上
开始前的一些说明
你必定假设你对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上直接下载合并好的源码 github.com/willian1234…
新建一个 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-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)。
-
在你的 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"去获得 更多的信息。
-
设置接下来的这一行代码:
var gravity = new b2Vec2(0,1.63);你也可以将参数设置为 (0, 0) 来模拟一个没有重力的环境:
var gravity = new b2Vec2(0,0); -
我们还需要告诉世界,当世界中的刚体静止时,可以允许他们进入睡眠状态,这样它们将不受作用力的影响。
一个睡眠的刚体无需模拟,它只是表示自 己的存在,并静止在它的位置上,不会对世界中的任何事物产生影响,允许 Box2D忽略它,而且因此会提升处理速度以及让我们获得更好的性能。
所以推 荐可能时让刚体睡眠。
-
添加下面的一行,它只是一个简单的布尔(Boolean)变量定义:
var sleep = true; -
最后,我们准备创建我们的第一个世界(world):
var world = new b2World(gravity,sleep); -
现在我们有一个容器来管理所有的刚体并且执行我们的动态模拟。
-
让我们来简单的回顾一下之前的代码,此刻,你的代码应该看起如下面 所示:
<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世界。让我们来看看你将怎样在它里面实 现物理效果的模拟 。
运行模拟
你需要在每一帧都进行模拟,所以首先你需要一个监听来触发每一帧
-
让我们添加一点代码:
<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 秒。
-
下面是 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 次位置遍历。
具体设置多少你自己来定。
-
与此同时,我将使用对两个约束解算器使用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。将它包含到你的项目中并运行,重力规则模拟, 管理时间步以及约束解算器。
到现在为止,你的网页上其实并没有显示任何东西。
你有一个空的世界,准备成为你游戏发生的容器。保存它然后在每一个未来的项目中使用它!