HTML5技术

零基础制作物理引擎--创造世界 - 【当耐特】

字号+ 作者:H5之家 来源:H5之家 2016-01-07 09:27 我要评论( )

写在前面 2011年在写了个物理引擎,期间重新啃起了物理课本,一晃就是5年, 当年自己写的物理引擎的代码又阅读一遍,受益匪浅,加上最近制作坦克争霸使用Box2d的思考,对物理引擎管线又有了新的认识和体会。 人除了造人,还可以是造世界,这两种时候人能够扮

写在前面

2011年在写了个物理引擎,期间重新啃起了物理课本,一晃就是5年,
当年自己写的物理引擎的代码又阅读一遍,受益匪浅,加上最近制作坦克争霸使用Box2d的思考,对物理引擎管线又有了新的认识和体会。
人除了造人,还可以是造世界,这两种时候人能够扮演上帝的角色。有人会说:“几个小球撞来撞球算哪门子世界?”引用《黑客帝国》里
男主角的话:“哪一个才是真实的世界?”在小球的眼里,它的世界就是真实的世界,只是小球无意识,意识形态的程序设计太复杂,
如果有一天意识形态能用程序表达并通过图灵测试,那么:"哪一个才是真实的世界?同样都是原子构成的世界,哪一个才是真实的世界?"。
不废话,现在就开始吧...

准备工作 运行环境

准备好一款浏览器,而且必须是现代浏览器(如Google Chrome最新版),因为物理引擎虽然支持老的浏览器,但是为了看到这个物理世界发生的一切,会在canvas里渲染刚体。

顶级NameSpace

为了纪念牛顿,使用Newton作为顶级命名空间。

var Newton = {};

Class.js

代码的分类抽象完全基于Class,使用的class.js如下所示:

.(prop) .prototype (var name in prop) { prototype[name] (() ..ret arguments)tmp)(name, prop[name]) : prop[name]() arguments)...Class.Class; };

向ES6靠齐的class.js,暴露prototype给语言使用者总是不友好的,大概的使用方式如下:

以前写过一篇文章介绍。

Vector2

Vector2,一般用来表示向量,有的时候也用来当作点来进行计算。

.(xxy() ..() ....() / invinv(v) ...(f) ff(v) ....(v) ./ (x..);

其中

除了clone方法,其余方法都不会创建新的Vector2,这里不能为了使用的代码可以连缀而创建大量的Vector2。

知识准备

[角]速度等于加速度在时间上的累加

v = a*t

[角]位移等于速度在时间上的累加

s = v*t

加速度等于过物体重心的力除以质量(最常见的物体受地球的吸引力,即重力。把物体看成质点,而且过重心先不用考虑角速度)

F = ma

运动的独立性

一个物体同时参与几种运动,各分运动都可看成独立进行的,互不影响,物体的合运动则视为几个相互独立分运动叠加的结果

如下图的运动小球:

usage

可拆分成如下三种运动分量:

usage

牛顿的世界

世界里需要模拟时间流逝,去累加速度、位移。
时间是连续的还是非连续的?到底有没有最小时间片?最小时间片是多少?现代物理依然无法给出定论。
但是在物理引擎里,时间是非连续的。

.() []./);

如上面代码所示,bodies为世界里的所有物体,bodiesLen为物体的数量。timeStep为最小时间片段。

时间流逝

.extend({ ... ... () ..()(k.(body) ..... ...

世界可以通过add方法向世界增加物体,上面的tick处理世界上发生的所有事件,目前仅仅是调用了物体自身的tick。

ticker代码如下:

(Ticker currTime ()..(currTime .timeToCall); lastTime .());

这里不使用requestAnimationFrame的,用的智能setTimeout。因为tick里面以后会包含很多逻辑,如重力处理、AABB优化、碰撞检测、碰撞处理、重叠处理、休眠处理,
requestAnimationFrame里的函数是在repaint之前调用,和复杂且耗时的程序逻辑混在一起会导致帧率下降,起反作用。

第一个物体

小球,是这个世界第一个物体。它除了不分男女,与生俱来许多属性(运动和碰撞相关的属性)。

.(option) .....)..).(...../(dt) .....(dt) ...(dt) ) ..dt / (dt) .)

上面的构造函数里,会把传入的参数覆盖默认的参数配置,并且提前计算好重力的倒数,因为重力的倒数会被经常用到。
好了。到目前为止已经完成了一款简陋的物理引擎,包含了物理引擎管线的:重力处理。下面要通过canvas把物理引擎的运作过程可视化。

渲染准备

.(selector) ..() ..(x....r.)) .).);

上面使用setTransform设置变换矩阵,能完成rotate(), scale(), translate(), or transform() 所能完成的工作。

使用下面代码测试绘制:

..PI / 180);

可以看到下面的效果:

case2

这里为了能看出旋转的角度,从圆的重心向右边r的位置画了一条线段。因为后续文章当中,也会出现矩形的刚体,同样,我们可以封装一个绘制矩形的
方法:

rect.....h / .)).).

让物理引擎跑起来

...).).......rotation); }) world.start();

上面Circle的参数里面:

按照上面一步一步,你将看到一个小球从(100,20 )的位置加速旋转掉飞下。

第一次重构

因为Newton.Circle 的大部分属性和方法,在其他的刚体中也适用,只有半径这个东西是Circle特有的,
所以将Newton.Circle改名为Newton.Body,并移除属性r,然后Newton.Circle 的代码就变成了:

.(option) ..);

加四面墙

.c1 ..).)........) ...(.) ...(.) ...(.) ...(...() world.start();

现在你可以看到一个小球在画布里,撞来撞去最后静止。

case4

world.onTick里面加了一大堆逻辑,用来处理小球与400*400的Canvas的碰撞,以及角速度和角速度的衰减,位置的矫正(重叠处理),到最后
的静止。因为世界只有圆一种刚体,所有只能先这样实现。但是上面的onTick里新加的代码,其实可以窥见物理引擎管线中的必备流程:

与鼠标互动

... ... ... c1 ....)..( evt) .....canvas.(evt))..ii.....) { ... ... ...

效果如下:

case4

因为所有的刚体都会被push进world.bodies,所有在onTick中需要遍历所有的小球进行绘制和与墙面的碰撞检测。

最后

本篇幅主要做了大量的准备工作包含class.js、ticker.js、vector2.js、render.js,真正的物理引擎的部分只占了小部分,后续的文章的占比会恰好相反。

虽然社区里有许多成熟的物理引擎,但自己实现一款物理引擎有非常多的好处:

未完待续..
下篇预告:《零基础制作物理引擎--创造力量 》

 

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

相关文章
  • vue全局配置----小白基础篇 - 星光笔

    vue全局配置----小白基础篇 - 星光笔

    2017-04-28 08:04

  • Docker基础入门及示例 - KevinCC

    Docker基础入门及示例 - KevinCC

    2017-03-16 12:01

  • Html5 基础----列表详述 - songqiaoli

    Html5 基础----列表详述 - songqiaoli

    2017-03-08 18:00

  • css基础知识之属性选择器 - Aqiaoba

    css基础知识之属性选择器 - Aqiaoba

    2017-03-02 17:00

网友点评
o