11RIA 闪客社区 - 最赞 Animate Flash 论坛

搜索
查看: 2080|回复: 0
上一主题 下一主题

[2D 物理引擎] 【9RIA—ladeng6666】—【Box2D系列教程 新番】

[复制链接] TA的其它主题
发表于 2018-2-6 15:21:08 | 显示全部楼层 |阅读模式

【游客模式】——注册会员,加入11RIA 闪客社区吧!一起见证Flash的再次辉煌……

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
本帖最后由 TKCB 于 2018-2-6 15:32 编辑

转载:9RIA游戏开发者社区(天地会)
作者:ladeng6666(拉登大叔)
作者博客:http://www.ladeng6666.com/blog/


【Box2D系列教程-导航帖】—拉登大叔出品(总贴)



没错,Box2D可以帮我们轻松的解决物理碰撞模拟的问题,但是人类是贪婪的,我们不满足于此,并希望能够快Box2D一步,预先知道下一步或者将来,刚体的运动轨迹,就像在《愤怒的小鸟》中,当我们拉动弹弓后,可以看到小鸟将要飞行的轨迹。
1.jpg
又或者,可以让刚体听我们的话,指哪打哪,就像《弹弹堂》中攻击敌人可以百发百中。
2.png
这一节,我们就来学习一下如何对刚体运动轨迹未卜先知,进而百发百中。

本节知识点
为实现以上两个游戏中的效果。本节将相关内容分解为一下3个知识点

(该知识点源自iforce2d.net,对英文该兴趣的同学,点击 这里 查看原文)

计算刚体的位置。第n个timeStep后,刚体的位置。用于描绘小鸟飞行的轨迹。
计算刚体最大高度。以初始速度v0飞出时,可以达到的最大高度。
计算刚体初始速度。为达到某个指定位置,而需要的初始速度,实现《弹弹堂》中的百发百中。

计算刚体的位置
初中的时候,我们都学过自由落体运动,假设重力加速度为a,那么经过t秒后,物体下落的距离h可以用下面的公式计算出来

3.png
但是在Box2D的世界里,使用这个公式是不准确的。因为Box2D是一个以delta为频率的数字采样世界,无法完美无缺的还原世界中的运动轨迹。
我们知道,在物体以a为加速度进行加速运动时,t秒后物体的速度v,可以用下面的公式计算出来。

4.png
v和t之间以加速度为斜率,成连续的线性关系,如下图所示。
5.png
而在Box2D的数字世界里,每个delta之间,物体是以当时的速度vt进行匀速运动的,所以随着时间t的不断增加,速度v是以delta为单位称阶梯形上升。如下图所示:
6.png
要知道,图中的速度曲线与坐标轴形成的形状面积,就是经过时间t后,刚体运动的距离。所以Box2D模拟出来的运动距离,要比实际少一些。而缺少的部分刚好是阶梯形状缺口的面积,如下图所示:
7.png
所以,接下来针对Box2D中物体运动的距离,我们要做的是,计算阶梯形状的面积。为此,可以讲锯齿分解成一个个小的矩形,每个矩形的宽为delta,高度为vn=a*delta*n,计算出每个矩形的面积dn,然后累加起来,如下图所示:
8.png
将图中每个矩形的面积累加起来,将vn替换为tna,同时t=ndelta后,所以刚体移动的距离转换成下面的公式:
9.png
如果,刚体有初始速度v0的话,那么要在以上公式d的基础上,累加初始速度移动的距离da = v0*n*delta。公式如下:
10.png
把这个公式,定义到名为getPositionWhen()的函数中,转换成代码如下所示:
[Actionscript3] 纯文本查看 复制代码
    private function getPositionWhen(pos:b2Vec2, v0:b2Vec2, n:Number):b2Vec2{
        var newPos:b2Vec2 = new b2Vec2();
        var dx:Number,dy:Number;

        var delta:Number = 1/stage.frameRate;
        var a:Number = world.GetGravity().y;
        dy = v0.y*delta*n + delta*delta*a*(n+1)*n/2;

        dx = v0.x * delta*n;

        newPos.y = pos.y + dy;
        newPos.x = pos.x + dx;
        return newPos;
    }


参数说明如下:
    pos:刚体当前的坐标位置
    v0:刚体移动的初始速度
    n:经过的timestep数


计算最大高度
虽然,在移动距离上,Box2D模拟出的结果与实际有些差异。但在速度上,经过时间t = n * delta后,刚体的速度还是符合下面的公式的:

11.png
我们知道,因为受到重力的作用,刚体在上升过程中,会渐渐慢下来,最终速度vt=0,此时刚体到达最大高度。根据上面的公式,可以计算出vt=0时,经过的timestep数量n:
12.png
然后将计算出的n作为参数,传递一个getPositionWhen(),既可以返回最大高度位置。
把以上计算过程,定义到名为getHighestPosition()函数中,代码如下所示:

[Actionscript3] 纯文本查看 复制代码
    private function getHighestPoint(pos:b2Vec2,v0:b2Vec2):b2Vec2{
        var delta:Number = 1/stage.frameRate;
        var a:Number = world.GetGravity().y;
        var n:Number = -v0.y/delta /a;
        return getPositionWhen(pos,v0,n);
    }


计算刚体初始速度
人类得到的越多,就越是贪婪。已经知道了如何计算刚体的位置,以及最大高度,但我们更想知道,如果已知某个坐标位置p,要以多大的速度发射炮弹,可以百步穿杨,击中目标位置p。
实现这一点并不困难,我们假定炮弹到达目标位置时,速度刚好为0,即目标位置为最大高度。那么根据getHighestPosition()函数中的公式,我们可以得知,初始速度v0和到达目标位置,所经过的timestep数n的关系为:

13.png
假设炮弹发射位置与目标位置p的垂直距离为d,根据getPositionWhen()中的公式,可以得到n,与距离d的关系为:
14.png

将n替换为 –v0/a/delta后,可以到只包含未知数v0的一个一元二次方程,具体如下:
15.png
根据一元二次方程的求解公式:
16.png
将速度与距离公式对应整理成一元二次方程求解公式形式,可以轻松的得到v0的求解结果。
17.png
解方程后,我们可以得到两个结果,分别表示向上和向下的速度。因为AS3的坐标系统中,y>0是向下的,所以这里v0.y<0的结果。
将以上计算过程,集成到名为getVelocityToPosition()函数中,代码如下:

[Actionscript3] 纯文本查看 复制代码
    private function getVelocityForPosition(from:b2Vec2,to:b2Vec2):b2Vec2{
        var dy:Number = to.y-from.y;
        var dx:Number = to.x -from.x;

        if ( dy >= 0 )
            return new b2Vec2();

        var delta:Number = 1 / stage.frameRate;
        var aGravity:Number = delta * delta * world.GetGravity().y; // m/s/s

        var a:Number = 0.5 / aGravity;
        var b:Number = 0.5;
        var c:Number = dy;

        var quadraticSolution1:Number = ( -b - Math.sqrt( b*b - 4*a*c ) ) / (2*a);
        var quadraticSolution2:Number = ( -b + Math.sqrt( b*b - 4*a*c ) ) / (2*a);

        var vy:Number = quadraticSolution1;
        if ( vy > 0  ){
            vy = quadraticSolution2;
        }

        var vx:Number = dx/(-vy/aGravity*delta);

        return new b2Vec2(vx,vy*stage.frameRate);
    }
}


举个栗子
好了,明白了计算刚体运动估计的算法和公式,下面该上示例了。

2015-09-13 计算刚体运动轨迹.zip (415.86 KB, 下载次数: 1)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

站长推荐 上一条 /1 下一条

感谢所有支持论坛的朋友:下面展示最新的5位赞助和充值的朋友……更多赞助和充值朋友的信息,请查看:永远的感谢名单

SGlW(66139)、 anghuo(841)、 whdsyes(255)、 longxia(60904)、 囫囵吞澡(58054)

下面展示总排行榜的前3名(T1-T3)和今年排行榜的前3名的朋友(C1-C3)……更多信息,请查看:总排行榜今年排行榜

T1. fhqu1462(969)、 T2. lwlpluto(14232)、 T3. 1367926921(962)  |  C1. anghuo(147)、 C2. fdisker(27945)、 C3. 囫囵吞澡(58054)



快速回复 返回顶部 返回列表