编程模拟自然(七):力学矢量与牛顿定律

序·后羿射日篇

  旧书有云:古者十日并出,草木焦槁,一曰后羿者射日,太阳里之九鸟皆死,救苍生于涂炭。

  (前情提要:元一直在找寻离开月亮的机会,但无名儿每天缠着元...)

  ...

  “星星挂在天边,就像梦想遥不可现...”元哼唧着。

  “猿叔你在哼什么曲子啊?”

  “...星星消失在天边,就像诺言来不及实现...”元对无名儿捂着双耳选择无视。

  “咳咳,我这是在朗诵诗歌呢!”

  ...

第零章

  “星星挂在天...你爸曾经射落过太阳?”元刚要继续深情朗诵,似乎想到了什么。

  “是的,听娘说爹地最厉害了。”

  “我不信,那你说他是怎么办到的?”元不禁抬头望了望天际那颗水蓝色星球。

  “用的是力学系统...”

  “猿叔,你手中的粒子系统就内嵌着呢!”无名儿继续回答道

第一节 随机游走

  假设你站在一根独木桥上,每 10 秒钟抛一枚硬币:如果硬币正面朝上,你向前走一步;背面朝上,则向后退一步。这就是随机游走——由一系列随机步骤构成的运动轨迹。

  然后,从独木桥转移到地面,你就可以做二维的随机游走了,不过每走一步需要抛掷两次硬币,而且需要按如下规则

第一次抛掷 第二次抛掷 结果
正面 正面 向前走一步
反面 正面 向右走一步
正面 反面 向左走一步
反面 反面 向后走一步

  这是一个很简单的规则,但是随机游走可以对现实世界中的各种现象建模:从气体分子的运动到小孩一整天的玩耍活动不一而足。

  现在我们建立 Walker 对象,它表示一个可以随机游走的对象,具有以下特点:即维持了自身数据(位置),又能够执行某些动作(比如绘制自身或者移动一步)

  首先,我们定义一个 Walker 类——Walker 对象的模板

''' <summary>
''' 表示一个随机游走对象
''' </summary>
Public Class Walker
    Public X As Integer
    Public Y As Integer
End Class

  构造函数负责对象的初始化

    ''' <summary>
    ''' 初始化当前对象的位置
    ''' </summary>
    ''' <param name="initX">指定的对象位置X分量</param>
    ''' <param name="initY">指定的对象位置Y分量</param>
    Public Sub New(initX As Single, initY As Single)
        Me.X = initX
        Me.Y = initY
    End Sub

  新建一个 Move() 方法实现控制对象移动

    ''' <summary>
    ''' 当前对象随机移动一帧
    ''' </summary>
    Public Sub RndMove()
        Dim chioce As Integer = Rnd.Next(4) '随机值0、1、2或3
        If chioce = 0 Then
            X = X + 1
        ElseIf chioce = 1 Then
            X = X - 1
        ElseIf chioce = 2 Then
            Y = Y + 1
        Else
            Y = Y - 1
        End If
    End Sub

  新建一个 Walker 实例,并在一个过程里循环调用它的 RndMove() 方法,每次调用完毕就输出到屏幕,我们就可以看到它的随机运动轨迹

第二节 向量

  这里指的是欧几里得向量,即几何向量。它的定义是:一个既有大小又有方向的几何对象。在编程中,引入向量并不会给我们添加新工作,它会简化你的代码,而对于在运动模拟中经常出现的数学运算,向量提供了很多现成的函数。

  现在,将上述 Walker 类中的变量用向量等效替换。

Imports System.Numerics
''' <summary>
''' 表示一个随机游走对象
''' </summary>
Public Class Walker
    Public Location As Vector2
    Public Shared Rnd As New Random
    ''' <summary>
    ''' 初始化当前对象的位置
    ''' </summary>
    ''' <param name="initX">指定的对象位置X分量</param>
    ''' <param name="initY">指定的对象位置Y分量</param>
    Public Sub New(initX As Single, initY As Single)
        Location = New Vector2(initX, initY)
    End Sub
    ''' <summary>
    ''' 当前对象随机移动一帧
    ''' </summary>
    Public Sub RndMove()
        Dim chioce As Integer = Rnd.Next(4) '随机值0、1、2或3
        If chioce = 0 Then
            Location = Location + New Vector2(1, 0)
        ElseIf chioce = 1 Then
            Location = Location + New Vector2(-1, 0)
        ElseIf chioce = 2 Then
            Location = Location + New Vector2(0, 1)
        Else
            Location = Location + New Vector2(0, -1)
        End If
    End Sub
End Class

  新增加速度和速度属性,并添加方法NormalMove(),

    Public Velocity As Vector2
    Public Acceleration As Vector2
    ''' <summary>
    '''  当前对象物理仿真移动一帧
    ''' </summary>
    Public Sub NormalMove()
        Velocity = Velocity + Acceleration '更新速度
        Location = Location + Velocity '更新位置
    End Sub

  容易知道三个向量间的作用关系是一种“涓滴”效应,加速度影响速度,进而影响位置。

  新建多个 Walker 实例,并在一个过程里循环调用它的 NormalMove() 方法,并记录下轨迹。

第三节 力与牛顿定律

  我们首先要了解力在现实世界中的概念,力可以指代力量的强度,比如“她用力的推动那块大石头”,或者“他有力地说出那句话”。但是这里是更书面化的概念,它源自牛顿运动定律:

  力是一个向量,它使有质量的物体产生加速。

  当看到定义的第一部分——力是一个向量,你应该由衷的感到喜悦,同位置或加速度一样,这在编程中可轻松被描述。

  结合力的概念,我们需要看看牛顿三大运动定律。

  牛顿第一运动定律通常简要的描述为:

  物体有保持静止或运动的趋势。

  这个表述遗漏了外力的作用,可以扩展为:

  除非有不均衡外力的作用,否则物体保持静止或匀速直线运动状态。

  而在编程中,我们可以这样表述牛顿第一定律:

  在平衡状态下,对象的速度向量(Vector 类型)始终都是常量。

  然后先跳过牛顿第二定律,直接牛顿第三定律,它通常表述为:

  每个作用力都有一个大小相等、方向相反的反作用力

  这个表述容易引起误解,可以更好的表述为:

  力总是成对出现的,且这两个力大小相等,方向相反

  同样地,这个表述也会引起误解,因为它看起来像是说:成对出现的力总是会互相抵消,事实并非如此,成对出现的力不是作用在同一物体上的。

  从编程角度表述牛顿第三定律:

  若要计算一个由 施加在 上的作用力 F(Vector 类型),必须额外施加一个由 作用在 上的反作用力 -F

  有时候不一定要遵循上述说法,比如模拟风力效果时,就不需要计算物体作用在空气上的反作用力,因为根本不用去模拟空气。

  下面是牛顿第二定律,它被表述为:

  合力等于质量乘以加速度:= M * A

  现在我们用编程去模拟这条定律,结合力的累加原理和牛顿第二定律,新增 ApplyForce() 方法

    Public Property Mass As Single = 10.0 '质量大小
    ''' <summary>
    ''' 指定的力作用于当前对象
    ''' </summary>
    ''' <param name="fVec">指定的力</param>
    Public Sub ApplyForce(fVec As Vector2)
        Acceleration = Acceleration + fVec / Mass
    End Sub

  更新 NormalMove() 方法,需要将加速度清零

    ''' <summary>
    '''  当前对象物理仿真移动一帧
    ''' </summary>
    Public Sub NormalMove()
        Velocity = Velocity + Acceleration '更新速度
        Location = Location + Velocity '更新位置
        Acceleration = New Vector2(0, 0) '加速度清零
    End Sub

  现在,你可以创建任意的符合物理规律的一个力,并将它作用于 Walker 对象。

后记

  “猿叔,你在做什么?”

  “起草一份计划书...”

  “我问过我娘了,她不愿多说爹地的事情。”无名儿说话时有些失落。

  “哈哈,就说是你编的嘛,射日的事我是不会相信的!”

  “你最厉害,行了吧。”

  “那是当然,我可是创世神,谁能比我厉害。”元一副不可一世的样子。

  “哟,还创世神,你咋不上天咧?”

  “....”

  “你怎么知道我要上天?”元晃了晃手中的文稿。

  中文名称:后羿工程

  英文名称:Hou Yi Project

  别称:星际载人火箭登地工程

  首席工程师:元

  工程阶段:论证中

  工程目标:实现可载人登地

  “唉,现在月球上好多偏远地方的孩子还上不起学;”无名儿努力皱了皱眉。

  “...”

  “吴刚叔每天辛苦砍树却没人给他发工资;”接着叹了口气。

  “...”

  “登地这种事又有什么意义呢?”

  “所以?”

  “你不要走呗,不然没人陪我玩了。”

附录

  预知后情,且看下回分解。了解更多请浏览目录导航

posted @ 2016-04-30 03:20  ExperDot  阅读(1248)  评论(1编辑  收藏  举报