6.2 摩擦力
假设有一张纸,将它撕碎后用力丢向空中。纸片会受到重力向下的牵引(y 轴),当我们松手后,纸片的 x 轴起初运动得非常快,但很快 x 轴的运动速度又归为零。
很显然,这里面没有负的加速度,但是纸片的速度向量却发生了改变,这就是摩擦力,阻力或阻尼。虽然它不是一种严格意义上的力,但作用是相同的,因为它改变了物体的速度。原理是,摩擦力只改变速度向量中的速度,而不会改变运动的方向。
那么如何使用代码来实现摩擦力呢?这里有两种方法。就像生活中很多事情一样,有一种正确的方法和一种简易的方法。我们会分别讲述这两种方法,首先从“正确”的方法开始吧。
6.2.1 摩擦力,正确的方法
摩擦力是与速度向量相反的力,假设有一个摩擦力的数值,就可以将它从速度向量中减去。事实上,是从速度向量的量值或速度中减去,不能只是简单地从 x,y 轴上减去。这样做的话,如果物体沿着一定角度运动,其中的一个分速度会提前到达零,使得物体继续垂直或水平地运动一会儿,结果看起来非常奇怪。
所以,我们要做的就是根据速度和方向找出角速度。使用 vx 和 vy 的平方和开平方后求出速度(是的,这就是勾股定理,第三章的内容)。再使用, Math.atan2(vy, vx) 求出角度,代码如下:
var speed:Number = Math.sqrt(vx * vx + vy * vy); var angle:Number = Math.atan2(vy, vx);
然后就可以从速度向量中减去速度。如果摩擦力大于速度,速度就变为零,计算代码如下:
if (speed > friction) { speed -= friction; } else { speed = 0; }
这样一来,还需要使用正弦和余弦将角速度转换回 vx 和 vy,如下:
vx = Math.cos(angle) * speed; vy = Math.sin(angle) * speed;
工作量不小吧?这里是全部文档类的内容 Friction1.as:
package { import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.Event; public class Friction1 extends Sprite { private var ball:Ball; private var vx:Number = 0; private var vy:Number = 0; private var friction:Number = 0.1; public function Friction1() { init(); } private function init():void { stage.scaleMode = StageScaleMode.NO_SCALE; stage.align=StageAlign.TOP_LEFT; ball = new Ball(); ball.x = stage.stageWidth / 2; ball.y = stage.stageHeight / 2; vx = Math.random() * 10 - 5; vy = Math.random() * 10 - 5; addChild(ball); addEventListener(Event.ENTER_FRAME, onEnterFrame); } private function onEnterFrame(event:Event):void { var speed:Number = Math.sqrt(vx * vx + vy * vy); var angle:Number = Math.atan2(vy, vx); if (speed > friction) { speed -= friction; } else { speed = 0; } vx = Math.cos(angle) * speed; vy = Math.sin(angle) * speed; ball.x += vx; ball.y += vy; } } }
这就是反比例速度向量的代码。摩擦力设置为0.1,然后给小球一个随机的 x,y 速度向量。 在 onEnterFrame 方法中,speed 和 angle 的计算方法同前面所介绍的。 如果 speed 小于 friction 则相减,否则让 speed 等于零。然后,重新计算 vx 和 vy ,最后将它们加入到坐标上。
总共用了十几行代码和四个三角函数才完成,大家也许能提出更好的写法,但却不能简化这个计算过程。我想你一定同意去看看那个简便的方法吧。
6.2.2 摩擦力,简便方法
大家也许都猜到了,简便的方法不像前面的方法那样精确,但是我敢打赌不会有人会注意到这些细微的变化。只用两行简单的乘法即可搞定,我们所要做的就是用摩擦力乘以 x,y 速度向量,摩擦力常用的值大约为 0.9 或 0.8。因此,在每一帧, vx 和 vy 的值都将变为上一次的 80% 或 90%。理论上,速度向量会无限接近零,但永远不会等于零。在实际应用中,计算机计算如此小的数字的能力是有限的,所以最终都会取整为零。
这种方法最好的一点是速度向量永远不会变为负数, 所以不需要进行判断。同样, x,y 的速度向量也是同比率变化的,所以不需要再将进行繁琐的转换。
只需要将前面例子中的 firction 变量设为 0.9,然后按如下代码改变 onEnterFrame 方法(可在文档类 Friction2.as 中找到):
private function onEnterFrame(event:Event):void { vx *= friction; vy *= friction; ball.x += vx; ball.y += vy; }
的确简便了不少吧!测试几次,能看出与之前的不同吗?平心而论,这个效果看起来更真实。
6.2.3 摩擦力应用
让我们回到熟悉的飞船上,现在让宇宙空间具有摩擦力。在 ShipSim2.as 类中加入friction 变量:
private var friction:Number = 0.97;
其它的类变量继续延用,还要改变 onEnterFrame 方法,如下(ShipSimFriction.as):
private function onEnterFrame(event:Event):void { ship.rotation += vr; var angle:Number = ship.rotation * Math.PI / 180; var ax:Number = Math.cos(angle) * thrust; var ay:Number = Math.sin(angle) * thrust; vx += ax; vy += ay; vx *= friction; vy *= friction; ship.x += vx; ship.y += vy; var left:Number = 0; var right:Number = stage.stageWidth; var top:Number = 0; var bottom:Number = stage.stageHeight; if (ship.x - ship.width / 2 > right) { ship.x = left - ship.width / 2; } else if (ship.x + ship.width / 2 < left) { ship.x = right + ship.width / 2; } if (ship.y - ship.height / 2 > bottom) { ship.y = top - ship.height / 2; } else if (ship.y < top - ship.height / 2) { ship.y = bottom + ship.height / 2; } }
只加了三行代码,感觉就不一样了。
任何使用速度向量的地方都可以加入摩擦力。比如在物体的旋转上(变量 vr )应用摩擦力,会使旋转的速度慢下来直至停止。大家可以在第五章的旋转箭头中试验一下。
这个手段可以应用在所有的物体上,比如轮盘,电风扇或飞船推进器。
文件下载(已下载 2317 次)发布时间:2011/6/27 上午9:34:51 阅读次数:7328