零基础制作物理引擎--创造力量

写在前面

上篇其实有重力,但是重力是经过重心,可以把物体看出质点,问题就变得简单,经过重心只产生线速度,不产生角速度。

这篇文章的力量其实是指:力(Force)和冲量(Impulse),不一定过重心。

边写引擎过程中,边补习牛顿经典力学体系,但是依然记得大学时候物理老师反复强调:“牛顿错了,牛顿错了,牛顿那一套只是狭义相对论在低速下的近似表现”。

所以顺带又科普了一下爱因斯坦那一套东西,后来发现,写个物理引擎用牛顿足矣。= =!

目标

本篇目标是为刚体新增两个方法,一个是创造力,一个是创造冲量。
创造力:

applyForce: function (force, point) {

}
  • 参数一:force是一个Vector2对象的实例,代表了力的沿着x、y方向上的大小。
  • 参数二:point是施力点的世界坐标

创造冲量

applyImpulse: function (impulse, point) {

}
  • 参数一:impulse是一个Vector2对象的实例,代表了冲量的沿着x、y方向上的大小。
  • 参数二:point是施放冲量点的世界坐标

准备工作

知识准备

冲量和时间的关系

I = F * t

圆盘转动惯量

m * r * r / 2 (r为半径)

长方体转动惯量

m(a a + b * b)/ 12 (a和b分别为长和宽)

更多转动惯量,请参见: https://en.wikipedia.org/wiki/List_of_moments_of_inertia

I * L / 转动惯量 = 角加速度 (L为力矩长度)

转动惯量越大,对转动的抵抗就越大,就越难让其旋转。

图解

假设一个刚体收到冲量I:

usage

冲量I可以拆分成3个冲量分量,I1、I2、I3,其中:

  • I1产生X轴和Y轴方向线速度
  • I2产生角速度和Y轴方向线速度
  • I3产生角速度和X轴方向线速度

过重心,作一条垂直于I2的垂线,垂线的长度(力矩长度)乘以 冲量分量I2 除以 转动惯量 = 角加速度,

过重心,作一条垂直于I3的垂线,垂线的长度(力矩长度)乘以 冲量分量I3 除以 转动惯量 = 角加速度,

I2和I3产生的角加速度需求累加。

程序

applyImpulse: function (impulse, point) {
    var rA = point.clone().sub(this.position);
    //如果不通过重心
    if (rA.x !== 0 || rA.y !== 0) {
        var nv = rA.clone().normalize();
        var tangent = new Newton.Vector2(nv.y, -nv.x);

        var ni = nv.multiply(nv.x * impulse.x + nv.y * impulse.y);
        this.linearVelocity.x += ni.x * this.invMass;
        this.linearVelocity.y += ni.y * this.invMass;

        var tni = tangent.multiply(tangent.x * impulse.x + tangent.y * impulse.y);
        this.linearVelocity.x += tni.x * this.invMass;
        this.linearVelocity.y += tni.y * this.invMass;
        this.angularVelocity += (rA.x * tni.y - rA.y * tni.x) * this.invRotationalInertia;
    } else {  //如果过重心
        this.linearVelocity.x += impulse.x * this.invMass;
        this.linearVelocity.y += impulse.y * this.invMass;
    }
}

上面有个条件分支,当冲量的方向经过重心的时候,只产生线速度。

因为转动惯量的倒数和质量的倒数使用非常频繁,所以在构造函数内就提前计算好,如圆的构造函数里提前求好了转动惯量的倒数:

Newton.Circle = Newton.Body.extend({
    ctor: function (option) {
        this._super(option);
        this.r = option.r;
        this.rSqu = this.r * this.r;
        this.type = Newton.CIRCLE;
        this.invRotationalInertia = 2 / (this.mass * Math.pow(this.r , 2));
    }
});

同样,在矩形的构造函数里,也提前求出来转动惯量的倒数:

this.invRotationalInertia = 12 / (this.mass * (Math.pow(this.width, 2) + Math.pow(this.height, 2)));

但是需要注意的是,如果质量发生改变的话,转动惯量也需要发生相应改变,所以可以使用Object.defineProperty()去定义mass,在set里面改变invRotationalInertia。

有了上面的applyImpulse方法,封装applyForce就会简单许多:

applyForce: function (force, point) {
    this.applyImpulse(force.clone().multiply(Newton.World.TimeStep),point);
}

冲量等于力在时间上的累加,这里提供的力的时间长度为最小时间片段。

简化代码

通过Vector2的cross方法可以把applyImpulse简化成如下代码:

applyImpulse: function (impulse, point) {
    var rA = point.clone().sub(this.position);
    this.linearVelocity.add(impulse.clone().multiply(this.invMass));
    this.angularVelocity += this.invRotationalInertia * rA.cross(impulse);
}

举个例子

var world = new Newton.World();
Newton.World.Gravity.y=0;

var box = new Newton.Rectangle({
    width: 70,
    height:30,
    position: new Newton.Vector2(100, 300),
    linearVelocity: new Newton.Vector2(0, 0),
    angularVelocity:0
});
world.add(box);

document.querySelector("#ourCanvas").addEventListener("click",function(evt){
    box.applyImpulse(new Newton.Vector2(200,-250),new Newton.Vector2(box.position.x-10,box.position.y-10))
}, false);

var render = new Newton.Render("#ourCanvas");
world.onTick(function () {
    render.clear();
    render.rect(box.position.x, box.position.y, box.width, box.height, box.rotation);
})

world.start();

这里把重力加速度设成了0,角速度和线速度都是0,所有在100,300的位置能够看到一个静止的矩形。当点击Canvas的时候,
在刚体重心的左上方10个像素的位置施加右上(200,-250)的冲量,可以看到它如下图所示的方式飞出去:

usage

最后

因为多边形的转动惯量和重心计算要比圆形和矩形复杂一下,这里先不展开,待物理引擎骨架基本建立之后再做完善。
未完待续...

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用方法:将配套的模块与DLL放到运行程序目录一起即可.比如: 用易语言新建立了一个程序,名称为[新程序.e]那么就放到和它一起的目录,添加模块即可. 搜集不宜,闲分多的请绕行. (包内无任何连接广告,纯绿色)压缩包内包括内容如下: --------------------------------以下为EDgame2d 引擎 D2D.ec 模块正式版本包括: 版本号: 1.0.5.15 大小: 628 kb 版本号: 1.0.5.15 大小: 635 kb 版本号: 1.0.6.20 大小: 652 kb 版本号: 1.0.7.20 大小: 660 kb 版本号: 1.0.7.70 大小: 653 kb 版本号: 1.0.8.70 大小: 664 kb 版本号: 1.0_学习版本 大小: 661 kb 版本号: 2.0_坏少爷完美破解(赞助版) 大小: 307 kb(最新) 版本号: 2.0_竹林深处破解(赞助版) 大小: 307 kb(最新) D2D.ec 模块扩展版本包括: 版本号: 1.0 大小: 83 kb 版本号: 1.1 大小: 86 kb 版本号: 1.2 大小: 91 kb D2D.dll 正式版本包括: 版本号: 1.0.0.1 大小: 952 kb 版本号: 1.0.5.15 大小: 824 kb 版本号: 1.0.6.20 大小: 507 kb 版本号: 1.0.7.20 大小: 417 kb 版本号: 1.0.8.70 大小: 417 kb 版本号: 1.0.8.17 大小: 433 kb 版本号: 1.0.8.28 大小: 418 kb 版本号: 1.0.11.25 大小: 427 kb 版本号: 1.0.6.20 大小: 507 kb 版本号: 1.0.0.1 大小: 846 kb 版本号: 1.0.0.1 大小: 847 kb 版本号: 1.0.0.1 大小: 925 kb 版本号: 1.0.0.1 大小: 957 kb 版本号: 1.0.0.1 大小: 961 kb 版本号: 1.1.2.7 大小: 519 kb(最新) bass.dll 正式版本包括: 版本号: 2.3.0.3 大小为: 91 kb ScriptManager.dll 正式版本包括: 版本号: 未知 大小为: 55kb --------------------------------以下为Galaxy2d 引擎 G2D.ec 版本号:4.102 大小为: 109 kb Galaxy2d.dll 版本号: 未知 大小为: 903 kb star.dll 版本号: 未知 大小为: 102 kb --------------------------------以下为Pge2d 引擎 pge32.ec 版本号: 15.316 大小为: 917kb PGE32.dll 版本号: 15.125.12.12 大小为: 1.72M
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值