4.质点动力学——牛顿运动定律

目前为止我们已经根据物理运动学知识创建了一个最基本的物理引擎,只需设置质点的初位置、初速度和加速度,物理引擎就会计算出任意时刻质点的速度和位置,现在我们将要讨论物体的加速度是由什么决定的,这个规律就是大名鼎鼎的牛顿第二定律,这在物理上属于动力学。

物理知识

牛顿第二定律的内容为:物体的加速度与所受合外力成正比,与物体的质量成反比,公式为:

ΣF=ma

在二维平面上,此方程可以表示成:

ΣFx=max

ΣFy=may

其中ΣFx表示x方向上的合力,ax表示x方向上的加速度,其中ΣFy表示y方向上的合力,ay表示y方向上的加速度,这种处理方法在高中物理中叫做正交分解法,其本质就是将矢量运算分解成同方向上的标量运算。

高中物理的知识告诉我们:力学问题通常有两类,一是根据物体的运动情况求它的受力情况,二是根据物体的受力情况求它的运动情况,对于物理引擎来说,我们关注的就是第二种情况,你只需指定物体所受的力,我们就可以根据牛顿第二定律求出物体的加速度,再根据初始速度和初始位移,通过运动学方程就可以解出物体在任意时刻的速度和位置了。

重力

若物理只受重力作用,因为重力G=mg,根据牛二定律,a=G/m=g,式中的g称为重力加速度,在地球表面为9.8m/s2,在赤道上这个值要比两极小一些,通常取9.8就可以了,在高中物理计算中通常取10m/s2让计算更加简单。

上面的结论还揭示了一个更重要的事实:若物体只受重力,那么轻重物体下落的快慢是相同的,这一点在几百年前就由伽利略通过比萨斜塔上的实验证明了(虽然比萨斜塔实验的真实性令人怀疑,但伽利略作为物理学的开山鼻祖是无可辩驳的)。

若你想实现卫星的运动,重力加速度就不是一个常量,而是与地球球心的距离成反比。例如,距地球表面高度正好为地球半径的高空的重力加速度只有地表的四分之一。

在Stun2DPhysics中的实现

基于以上物理知识,我们想要在引擎中添加新的代码。

一、在Body类中添加有关力的属性

internal Vector2 force = Vector2.Zero; 

/// 
/// 在下一次更新时施加在Body上的力。
/// 在每次更新调用后都会清零,因此这个值必须在更新前被调用。
/// 这个属性是只读的。
/// 
/// 力。
public Vector2 Force
{
    get { return force; }
}

以及对应的方法:

/// 
/// 在Body上施加一个力。
/// 
/// 力
public void ApplyForce(Vector2 amount)
{
    force.X = force.X + amount.X;
    force.Y = force.Y + amount.Y;
}

/// 
/// 清除施加在Body上的力。
/// 在每次更新后都要调用这个方法。
/// 
public void ClearForce()
{
    force.X = 0;
    force.Y = 0;
}

还要添加一个属性表示这个Body是否忽略重力的影响,在物理问题中,若研究的是带电粒子在电场中的运动,因为粒子所受的电场力远大于重力,这种情况下往往会忽略重力,但是要强调一点,忽略重力并不表示忽略质量,这也是高中生常犯的物理错误:

/// 
/// 获取或设置是否忽略重力,默认为false。
/// 
public bool IgnoreGravity = false; 

二、在Body类中添加有关质量的属性

internal float mass = 1; 
internal float inverseMass = 1;

/// 
/// 获取或设置Body的质量。 
/// 
/// 质量不能为0。
public float Mass
{
    get { return mass; }
    set 
    {
        if (value == 0) 
            throw new ArgumentException("质量不能为0", "value"); 
        
        mass = value;
        if (isStatic)
            inverseMass = 0;
        else inverseMass = 1f / value; 
    }
}

/// 
/// 获取Body的质量倒数(1/Mass) 
/// 
public float InverseMass { get { return inverseMass; } } 

除了新建质量Mass这个属性,我们还设置了质量的倒数InverseMass,理由主要有两个:

1.InverseMass在模拟一个质量为无穷大的物体常常很有用。有些物体无论施加多大的力都不会移动,这对游戏中那些不会移动的物体是很有用的:例如游戏中的墙和地板不会发生移动。我们无法直接将质量设置为无穷大,但可以通过将isStatic属性设置为true,程序会自动将InverseMass设置为0表示无穷大的质量。

2.将原来公有的Acceleration属性变为私有,这样我们就不能直接设置Body的加速度,而是通过Body上施加的力计算得出的,代码如下:

private Vector2 acceleration = Vector2.Zero;
        
/// 
/// 由引擎在内部调用,对速度进行积分运算。 
/// 
/// 时间间隔。
internal void IntegrateVelocity(float dt) 
{
    // 计算加速度 
    acceleration.X = force.X * inverseMass;
    acceleration.Y = force.Y * inverseMass;
    [...]
 } 

由以上代码可知,计算加速度时并没有使用a等于力除以质量m,而是使用a等于F乘以质量的倒数,这样,当质量为无穷大时加速度为0,这正是我们想要的结果。

三、PhysicsSimulator.cs中的改变

因为重力加速度是所有Body共有的,所以首先在PhysicsSimulator.cs类中添加一个属性:

/// 
/// 作用在所有Body上的重力加速度,默认为向下9.8。
/// 
public Vector2 Gravity=new Vector2 (0,9.8f);

在Update方法修改为:

public void Update(float dt)
{
    // 如果时间间隔为0或者物理系统没有激活则不进行任何操作。
    if (dt == 0 || !Enabled) 
        return; 
    ApplyForces(dt);
    UpdatePositions(dt);
}

而ApplyForces(dt)方法的代码为:

/// 
/// 在所有Body上施加力。
/// 
/// 时间间隔。
private void ApplyForces(float dt)
{
    for (int i = 0; i < bodyAddList.Count; i++)
    {
        Body body = bodyAddList[i];
        
        if (body.isStatic) 
            continue;
        
        if (!body.IgnoreGravity)
        {
            body.force.X = body.force.X + (Gravity.X * body.mass);
            body.force.Y = body.force.Y + (Gravity.Y * body.mass);
        } 
        
        body.IntegrateVelocity(dt);
        body.ClearForce();
    }
}

至此,Stun2DPhysics添加了对力的支持,你只需设置Body的初始位置、初始速度和力,引擎就可以自动计算得出任意时刻的速度和位置。

文件下载(已下载 873 次)

发布时间:2011/6/8 上午10:31:21  阅读次数:6163

2006 - 2024,推荐分辨率 1024*768 以上,推荐浏览器 Chrome、Edge 等现代浏览器,截止 2021 年 12 月 5 日的访问次数:1872 万 9823 站长邮箱

沪 ICP 备 18037240 号-1

沪公网安备 31011002002865 号