Unity3D Gravity Engine 说明文档#1 - Getting Started

432 阅读8分钟
原文:http://nbodyphysics.com/blog/gravity-engine-doc-1-3-2-2-2/getting-started/
此篇文章仅为分享一下自己的学习笔记,其中会有很多自己的理解,并不是一个专业的翻译或可靠的说明文档
可能与原文会有较多出入,仅供参考

1. 概念

在本小结中将会介绍一些关键概念和知识点,以便后续更好的理解,但不会过多考虑细节以及特殊情况

本资源的核心类是 GravityEngine(简称GE),它会负责处理场景中所有的物理特性,相关的计算会在 FixedUpdate() 中进行。

它会通过以下几种方式检查场景中需要参与计算的物体都有哪些

  • 包含 NBodyGravitationalParticles 脚本的对象
  • 包含在 GravityEngine 提供的指定列表中初始化的对象
  • 通过调用 GravityEngine.Instance().AddBody() 初始化的对象

场景中的演算操作可以被自动触发(需要勾选 Evolve At Start),也可以通过调用 GravityEngine.Instance().SetEvolve() 去手动触发。

若要通过重力去移动对象,则需要附加一个 NBody 脚本。 这个脚本会给物体一个初始速度和质量,而位置信息则取自 Transform 脚本(只在 DIMENSIONLESS 的情况下去取)。

2. Hello World

接下来将会演示创建一个简单的实际应用场景,仅由1个行星和1个卫星组成

  1. 向场景中添加一个空的游戏对象,再添加 GravityEngine 脚本到对象中,并保持所有默认值不变。
  2. 添加一个球体到场景中,并设置它的 position = (0, 0, 0) ,后面它将作为一个行星。
  3. NBody 脚本添加到上一步创建的球体上,保持速度为 0,质量更改为 100
  4. 添加一个立方体到场景中,后面它将作为一个卫星,并设置它的 position = (5, 0, 0) 以及 mass = 1 (别忘了添加 NBody 脚本)。

为了让卫星(立方体)绕行星(球体)运行,它需要在轨道方向上有一个初始速度。所以需要设置立方体 NBody 的速度为 (0, 3, 0)

以上都设置完成后就可以运行一下游戏,观察一下我们刚刚创建的椭圆轨道了。

需要注意的是,球体本会也会发生运动 —— 因为两个物体都绕着质心旋转。球体质量越大,球体的“摆动”幅度也就越小(所以立方体的速度需要调整,才能获得一个椭圆轨道)。

如何停止“摆动”?可以试试将 FixedObject 脚本添加到球体上。

这个脚本的存在会告诉GE使用这个对象的质量来影响其他对象,FixedObject 脚本会使自己停止移动,即不受引力影响。另外这个脚本实现了 IFixedOrbit 接口,这是一种显式控制对象路径的通用方法,如 Fixed Kepler orbits 就实现了该接口。

在本小节的示例中,为了简单起见,我们将 NBody 直接添加到了包含图形脚本的游戏对象中。 一般来说,最好是使用 NBody 创建一个空的游戏对象,并将球体作为子对象添加到该对象中。 这将会允许使用更通用的变换变化方式来调整球体的各项参数(如尺寸和方向)。 如果不这样做,有时 NBody 脚本所在的对象,转换可能会由其他脚本或方法控制,这将会导致很多维护性上的问题。

3. 自定义椭圆轨道

在第2章(Hello World)中所设置的速度属性可以帮助我们获得一个轨道,但可能不是我们想要的,如果想要用这个方式去自定义轨道,会需要多次调试才可以勉强实现我们需要的效果,因此可以通过 OrbitUniversal 脚本直接将对象放置在想要的轨道上,该脚本支持配置椭圆轨道和双曲轨道。

以下我们以椭圆轨道来做演示,只需要将 OrbitUniversal 脚本添加到卫星(立方体)即可, 同时要注意设置 Center NBody 为行星(球体),具体可以参考下方的原文示例图。

GettingStartedPic.png

Scene 视图中,我们可以直观的看到轨道的形状,只需要通过调整各项轨道参数,就能看到是不是我们想要的轨道。其中:

  • 轨道尺寸通过变量 p (Semi-parameter) 控制
  • 轨道形状通过离心率(也称偏心率)控制。值为 0 则表示圆形,值越接近 1 轨道就越长,值为 1 则表示抛物线,值大于 1 则表示双曲线
  • 轨道方向通过轨道倾角以及2个旋转参数控制,建议通过实际的运行情况来观察参数变化后的实际表现

当我们把 OrbitUniversal 脚本放置在对象上时,可以发现 Inspector 视图中的 Transform 脚本发生了一些变化,具体原因可以看脚本中的注释,也就是对象的 Transform 脚本将会由 OrbitUniversal 脚本控制接管

OUtransform.png

为了达到轨道的所需速度,OrbitUniversal 脚本会自动计算好初始速度。游戏运行后,NBody 脚本将会考虑场景中所有影响重力计算的脚本,也就是说,如果场景内只有我们演示的这2个物体,那么轨道就会按照我们希望的产生出来,但是如果场景内有其他会影响重力计算的脚本,就会导致轨道受到影响或者破坏。

通过调整轨道参数不会影响轨道的生成,因为脚本会自动计算好所需速度

4. 在场景中看看生成的轨道

GravityEngine 提供了 OrbitPredictor 脚本,来帮助实现一个可视化的轨道,使用起来十分简单,只需将这个脚本添加到一个空对象中,并选择对应的 CenterObject 对象和 Body 对象即可

screen-shot-2016-08-23-at-6-33-33-am-png-300x70.png

OrbitPred-300x249.png

同时,会自动添加一个 LineRenderer 脚本,里面的各项参数都可以修改

5. 控制轨道速度

目前我们的轨道速度都是通过 OrbitUniversal 脚本去控制的,要如何自定义呢?在此之前,我们需要先明白,轨道速度是由轨道的形状和物体的质量控制的。有两种方式可以使轨道速度更快,一是更靠近中心点,二是使中心物体质量变得更大。

latex.png

如果我们想在轨道的大小和形状保持不变的同时,使轨道速度加快,可以尝试以下方法:

  1. 增加行星(球体)的质量
  2. 改变 GravityEngineMass Scale 参数(位于 Scaling 中),需要注意的是,这会改变场景中所有物体的质量

那么,质量的变化是如何影响到轨道速度的呢?轨道力学定律提供了答案。当质量增加到4的时候,轨道周期(转一圈的时间)就将会减少2倍。所以,要使速度翻倍,我们就需要四倍的质量。

如下图所示,如果我们修改 Mass Scale 的值为 4,那么卫星(立方体)的速度就会比原来快2倍。

GE_Inspector_circle-300x137.png

6. 添加一艘飞船

接下来,让我们在场景中添加一艘可被玩家控制的船。这艘船将根据重力移动,并可以根据玩家的输入改变航线。这可以通过连续的推力或基于玩家输入的瞬间推力来实现。

本节演示的飞船将从一个带有 NBody 脚本的游戏对象开始,所以首先,创建一个新的游戏对象(比如一个胶囊)然后添加 NBody 脚本,并将质量设为零,因为在行星和卫星的引力作用下,飞船的质量几乎可以忽略不计。

创建完成以后,飞船就可以在场景中定位并给出初始速度。(初始速度会由轨道脚本自动计算并设置,之后飞船就可以根据力和玩家输入的结果在任何路径上自由移动)。

将飞船的位置设置为 (15, 0, 0) ,速度设置为 (0, 3, 0) 。如果你愿意,你可以添加一个 OrbitUniversal 脚本来配置飞船的初始速度,这样 OrbitUniversal 脚本就不会在影响到飞船了(除非演算模式被设置为开普勒模式)。

现在我们需要编写一些代码来根据玩家的输入结果来改变飞船的速度。最简单的就是按下 “W” 键推进。

public class GettingStartedShip : MonoBehaviour {
    public float thrust = 1.0f;
    private NBody ship;

   // Use this for initialization
   void Start () {
        ship = GetComponent<NBody>();
        if (ship == null) {
            Debug.LogError(gameObject.name + " does not have an Nbody component");
        }
   }
	
   // Update is called once per frame
   void Update () {
        if (Input.GetKeyUp(KeyCode.W)) {
            GravityEngine.Instance().ApplyImpulse(ship, thrust * Vector3.up);
        }		
   }
}

其中,最关键的一行是

GravityEngine.Instance().ApplyImpulse(ship, thrust * Vector3.up);

函数 ApplyImpulse() 会将固定的速度变化应用到提供 Vector3NBody 对象上。需要注意的是,这是通过调用 GravityEngine 类来完成的,而不是直接更改 NBody 的速度值来完成的,这么做的原因是 GravityEngine 中的物理积分器需要更新。使用 ApplyImpulse() 是因为飞船的重力质量为零,如果对象有质量,则可以使用ApplyForce() API。

如果你不是在做一个已知飞船的真实模拟,你可以在游戏中选择一些“合适”的船舶速度变化比例。对于那些有兴趣在已知推力和飞船质量的情况下使用 GravityEngine 进行实际计算的人,原文作者将在后续更新中给出更详细的解释。