一些简单的物理计算

一些关于基础物理效果的 AS1.0 实现方法,摘自 Robert PennerProgramming Marcomedia Flash MX,这是我的第一本 Flash 入门书。

(提示:以下代码中的 Vector 是一个 AS1.0 的自定义类,这个 Vector 类的代码请到作者的网站 下载

一、运动学

位置

// 用二维向量表示的位置
pos = new Vector(2, 5)

位移

// 计算位移向量
pointA = new Vector(2, 3);
pointB = new Vector(4, 2);
disp = pointB.minusNew(pointA);
trace(disp); // 输出:[2,-1]

距离

// 继续前面的例子
trace(disp.getLength()); // 输出:2.2360679774997

速度

  1. 恒定速度的运动

     // 恒定速度的运动
     pos = new Vector(0, 0);
     vel = new Vector(2, 3);
     this.onEnterFrame = function() {
         pos.plus(vel);
         this._x = pos.x;
         this._y = pos.y;
     }
    
  2. 随机速度的运动

     // 随机速度的运动
     pos = new Vector(200, 200);
     vel = new Vector(0, 0);
     this.onEnterFrame = function() {
         vel.reset(random(9)-4, random(9)-4);
         pos.plus(vel);
         this._x = pos.x;
         this._y = pos.y;
     }
    
  3. 以鼠标控制速度的运动

     // 以鼠标控制速度
     pos = new Vector(200, 200);
     vel = new Vector(0, 0);
     this.onEnterFrame = function() {
         vel.reset(this._xmouse/10, this._ymouse/10);
         pos.plus(vel);
         this._x = pos.x;
         this._y = pos.y;
     };
    

速率

vel = new Vector(3,4);
speed = vel.getLength();
trace(speed); // 输出:5

加速度

  1. 恒定加速度的运动

     // 恒定加速度的运动
     pos = new Vector(0, 0);
     vel = new Vector(0, 0);
     accel = new Vector(1, 0);
     this.onEnterFrame = function() {
         vel.plus(accel);
         pos.plus(vel);
         this._x = pos.x;
         this._y = pos.y;
     }
    
  2. 随机加速度的运动

     // 随机加速度的运动
     pos = new Vector(200, 200);
     vel = new Vector(0, 0);
     accel = new Vector(0, 0);
     this.onEnterFrame = function() {
         accel.reset(random(5)-2, random(5)-2);
         vel.plus(accel);
         pos.plus(vel);
         this._x = pos.x;
         this._y = pos.y;
     }
    
  3. 以鼠标控制加速度的运动

     // 以鼠标控制加速度
     pos = new Vector(200, 200);
     vel = new Vector(0, 0);
     accel = new Vector(0, 0);
     this.onEnterFrame = function() {
         accel.reset(this._xmouse/100, this._ymouse/100);
         vel.plus(accel);
         pos.plus(vel);
         this._x = pos.x;
         this._y = pos.y;
     };
    

二、力

牛顿第一定律静止物体总是保持静止,运动的物体总是保持其运动速度和方向,直到有不平衡外力迫使它改变这种状为止。

合力

    // 声明力向量
    forceA = new Vector(2, 0);
    forceB = new Vector(-3, 2);
    forceC = new Vector(0, 1);

    // 对力向量求和
    forceNet = new Vector(0, 0);
    forceNet.plus(forceA);
    forceNet.plus(forceB);
    forceNet.plus(forceC);
    trace(forceNet); // 输出:[-1,3]

    // ------
    // 对力向量求和 (简便方法)

    forceNet = forceA.plusNew(forceB);
    forceNet.plus(forceC);
    trace(forceNet); // 输出:[-1,3]

牛顿第二定律合力作用于物体所产生的加速度的大小与合力的大小成正比,与物体的质量成反比,方向与合力相同。

Flash 中力驱动的运动

  1. 计算作用于物体的力

     // 定义力
     forceA = new Vector(1, 2);
     forceB = new Vector(3, 4);
    
  2. 将力相加求出合力

     // 计算合力
     forceNet = forceA.plusNew(forceB);
    
  3. 计算因合力而产生的加速度

     // 计算加速度
     // 质量 mass 是已定义的变量
     accel = forceNet.scaleNew(1/mass);
    
     // 有时,对制作动画来说,运动物体的质量并不重要。你可以假设物体是质量为 1 的粒子,从而忽略质量的影响。当质量为 1 时,加速度等于合力。
    
     // 计算加速度
     accel = forceNet;
    
  4. 将加速度与速度相加

     // 计算新的速度
     // vel 是已定义的速度变量
     vel.plus(accel);
    
  5. 将速度与位置相加

     // 计算新的位置
     // pos 是已定义的位置变量
     pos.plus(vel);
    
  6. 将物体移动到新位置

     // 刷新影片片段的位置
     mc._x = pos.x;
     mc._y = pos.y;
    

摩擦力

动摩擦例子:

    // 计算一维的动摩擦
    vel = 0;
    kFrictionAccel = .8; // 动摩擦所产生的加速度

    this.onEnterFrame = function() {
        velX += accelX;
        accelX = 0;
        if (velX > 0) {
            velX = Math.max(0, velX-kFrictionAccel);
        } else if (velX < 0) {
            velX = Math.min(0, velX + kFrictionAccel);
        }
        this._x += velX;
    };

    this.onMouseDown = function() {
        accelX = 15;
    };

在这个例子运行时,对象静止不动,直到鼠标点击产生一个加速度。此时,对象在短时间内加速运动,但动摩擦力使它减速最终停下来。代码的逻辑如下:如果水平速度大于 0(即对象正在向右运动),动摩擦方向相反——反方向(向左)。此时,速度应减去摩擦加速度的值 (velX-kFrictionAccel)。然而,摩擦不能改变速度的方向。也就是说,如果 velX 原来为正数,在减去 kFrictionAccel 之后不能变为负数。摩擦可以使物体速度减为 0,但不能使物体向相反的方向运动。因此,代码中使用 Math.max() 和 Math.min() 确保减法结果不会小于 0。

静摩擦实现静摩擦的逻辑:

(1) 检查物体是否静止 (速度为 0)。
(2) 计算作用于物体的合力。
(3) 计算作用于物体的最大静摩擦力。
(4) 如果合力小于最大静最大静摩擦力,不作任何操作。
(5) 否则,相应地对物体加速。

流体摩擦精确地模拟流体摩擦需要大量复杂计算。但如果我们不需要很高的物理精度,在动态 Flash 动画中,我们可以使用近似方案产生较为逼真的摩擦效果。我们可以使用一般的原则:速度越大,由于摩擦而造成的速度损失也越大。流体摩擦可以定义为单位时间内速度损失的百分比。在 Flash 中,基本的时间单位是帧。因此,我们可以定义流体摩擦为每帧速度损失的百分比。下面的代码实例演示了如何在 ActionScript 中实现这个效果:

    // 一维流体摩擦的计算
    velX = 0;
    fluidFriction = .07;

    this.onEnterFrame = function() {
        velX += accelX;
        accelX = 0;
        velX *= (1 - fluidFriction);
        this._x += velX;
    };

    this.onMouseDown = function() {
        accelX = 15;
    };

这段脚本类似于前面动摩擦力的例子。但有两点区别,首先是摩擦变量的声明:

fluidFriction = .07;

流体摩擦定义百分比,本例中,每帧速度降低 7%。另一个区别是摩擦影响速度的一行代码:

velX *= (1 - fluidFriction);

这使 velX 减少 fluidFriction 指定的百分比。可以看出,流体摩擦的代码比动摩擦更简单更快速。如果你更关心执行速度,可以使用流体摩擦。如果你需要逼真的物理模拟,可以在物体滑动时使用动摩擦,在物体在液体、气体或太空(通常太空中的气体很少)中运动时使用流体摩擦。两种摩擦与变形相比,动摩擦产生二次缓出,而流体摩擦产生指数缓出。

引力

空间中的引力在任何两个物体间都存在引力——甚至在两粒灰尘之间也存在引力。然而只有在大量物质聚集在一起时(就像行星恒星),积累起来的引力可以明确的感受到。

经典的计算两个物体之间引力的公式为:

F=GMm/r²

在 Flash 中,我们主要关心效果,而不是物理的精确度。因此,我们不必使用正式是万有引力常数 G。如过不必要,我们甚至不必在计算中考虑物体的质量。

这个计算中重要的是:两个物体之间,引力的大小随它们之间的距离 r 的变化而变化,其关系为平方反比。ActionScriopt,模拟引力的例子:

// 根据鼠标点击的位置计算引力
this.pos = new Vector(150, 100);
this.vel = new Vector(5, 0);
this.accel = new Vector(0, 0);
this.friction = 0;// 引力属性
this.anchor = new Vector(250, 200);

this.strength = 5000;this.doForce = function() {
    // 计算引力
    this.netForce = this.pos.minusNew(this.anchor);
    var r = this.netForce.getLength();
    this.netForce.setLength(-this.strength/(r*r));
};

this.move = function() {
    // 计算新的位置
    this.vel.plus(this.netForce);
    this.vel.scale(1-this.friction);
    this.pos.plus(this.vel);

    // 刷新显示
    this._x = this.pos.x;
    this._y = this.pos.y;

};

this.onEnterFrame = function() {
    this.doForce();
    this.move();
};

this.onMouseDown = function() {
    this.anchor.reset(this._parent._xmouse, this._parent._ymouse);
};

这段代码应放在一个影片片段内部,这个片段应该有可见的图形,如一个球。还需要包含 Vector 类 (vector_class.as)。 在这个例子运行时,你可以点击鼠标使该影片片段绕着点击的位置转动。

地表附近的引力在地球上,我们所体验的引力是恒定的力,作用于垂直的方向上。引力为我们确定了垂直的方向。什么方向是“下”?引力将我们拉向地球的中心。因此,“下”就是指向地球中心的方向。这个方向正好与地球表面垂直。

对空间中两个物体的引力来说,各自的质量都会影响引力和加速度的大小。然而对地球表面的物体来说,不同质量的物体所受到的引力而产生的加速度却是基本相同的。因为表面引力作用总是沿着竖直方向,所以很容易在 ActionScript 运动过程中实现它。我们在影片开始部分定义引力加速度,然后直接在计算过程中使用它。

下面的代码演示了表面引力的实现:

// 由于表面引力而加速下落
this.pos = new Vector(this._x, this._y);
this.vel = new Vector(0, 0);
this.friction = 0;// 引力属性

this.gravAccel = new Vector(0, .5);this.move = function() {

    // 计算新的位置
    this.vel.plus(this.gravAccel);
    this.vel.scale(1-this.friction);
    this.pos.plus(this.vel);

    // 刷新显示
    this._x = this.pos.x;
    this._y = this.pos.y;

};

this.onEnterFrame = function() {
    this.doForce();
    this.move();
};

this.onMouseDown = function() {
    this.pos.reset(this._parent._xmouse, this._parent._ymouse);
    this.vel.reset(0, 0);
};

在这段脚本运行时,每次鼠标点击都会将影片片段放在新的位置上,从这个位置它向下作自由落体运动。