【游客模式】——注册会员,加入11RIA 闪客社区吧!一起见证Flash的再次辉煌……
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
本帖最后由 TKCB 于 2018-2-6 15:32 编辑
转载:9RIA游戏开发者社区(天地会)
作者:ladeng6666(拉登大叔)
作者博客:http://www.ladeng6666.com/blog/
【Box2D系列教程-导航帖】—拉登大叔出品(总贴)
没错,Box2D可以帮我们轻松的解决物理碰撞模拟的问题,但是人类是贪婪的,我们不满足于此,并希望能够快Box2D一步,预先知道下一步或者将来,刚体的运动轨迹,就像在《愤怒的小鸟》中,当我们拉动弹弓后,可以看到小鸟将要飞行的轨迹。
又或者,可以让刚体听我们的话,指哪打哪,就像《弹弹堂》中攻击敌人可以百发百中。
这一节,我们就来学习一下如何对刚体运动轨迹未卜先知,进而百发百中。
本节知识点
为实现以上两个游戏中的效果。本节将相关内容分解为一下3个知识点
(该知识点源自iforce2d.net,对英文该兴趣的同学,点击 这里 查看原文)
计算刚体的位置。第n个timeStep后,刚体的位置。用于描绘小鸟飞行的轨迹。
计算刚体最大高度。以初始速度v0飞出时,可以达到的最大高度。
计算刚体初始速度。为达到某个指定位置,而需要的初始速度,实现《弹弹堂》中的百发百中。
计算刚体的位置
初中的时候,我们都学过自由落体运动,假设重力加速度为a,那么经过t秒后,物体下落的距离h可以用下面的公式计算出来
但是在Box2D的世界里,使用这个公式是不准确的。因为Box2D是一个以delta为频率的数字采样世界,无法完美无缺的还原世界中的运动轨迹。
我们知道,在物体以a为加速度进行加速运动时,t秒后物体的速度v,可以用下面的公式计算出来。
v和t之间以加速度为斜率,成连续的线性关系,如下图所示。
而在Box2D的数字世界里,每个delta之间,物体是以当时的速度vt进行匀速运动的,所以随着时间t的不断增加,速度v是以delta为单位称阶梯形上升。如下图所示:
要知道,图中的速度曲线与坐标轴形成的形状面积,就是经过时间t后,刚体运动的距离。所以Box2D模拟出来的运动距离,要比实际少一些。而缺少的部分刚好是阶梯形状缺口的面积,如下图所示:
所以,接下来针对Box2D中物体运动的距离,我们要做的是,计算阶梯形状的面积。为此,可以讲锯齿分解成一个个小的矩形,每个矩形的宽为delta,高度为vn=a*delta*n,计算出每个矩形的面积dn,然后累加起来,如下图所示:
将图中每个矩形的面积累加起来,将vn替换为tna,同时t=ndelta后,所以刚体移动的距离转换成下面的公式:
如果,刚体有初始速度v0的话,那么要在以上公式d的基础上,累加初始速度移动的距离da = v0*n*delta。公式如下:
把这个公式,定义到名为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后,刚体的速度还是符合下面的公式的:
我们知道,因为受到重力的作用,刚体在上升过程中,会渐渐慢下来,最终速度vt=0,此时刚体到达最大高度。根据上面的公式,可以计算出vt=0时,经过的timestep数量n:
然后将计算出的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的关系为:
假设炮弹发射位置与目标位置p的垂直距离为d,根据getPositionWhen()中的公式,可以得到n,与距离d的关系为:
将n替换为 –v0/a/delta后,可以到只包含未知数v0的一个一元二次方程,具体如下:
根据一元二次方程的求解公式:
将速度与距离公式对应整理成一元二次方程求解公式形式,可以轻松的得到v0的求解结果。
解方程后,我们可以得到两个结果,分别表示向上和向下的速度。因为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, 下载次数: 3)
|