Epoch Development Blog 6 - Float in Physics Engine Issues

 • 

Unity 的物理引擎使用的基础是 float - 无论是 Vector3,Vector2,Quaternion... 等等,都使用 float。而 float 有一个非常大的问题就是精度丢失,特别是在做 Epoch 这种大尺度规模的游戏的时候 —— 尽管我们可以所有东西都缩小到 0.01 倍,但不可避免的是 float 仍然限制 Unity 内同时出现非常大和非常小的物体 —— 例如星球和战斗机。
如果读过 EVE Online 的 Dev Blogs,会发现他们也提到过类似的问题,Titan 舰长 18 千米(更不用提最近新出的 Citadel ),而小型飞船只有 52 米。在用同样的坐标系的情况下,如何保证两种飞船都能在渲染和物理引擎上有同样的表现,本身就是一件非常有挑战性的事情。
回到 Epoch 上,Unity 的物理引擎使用 float,2^23 = 8388608,一共七位,这意味着最多能有 7 位有效数字。如果我们设定 1 unit = 10 meter,那么整个宇宙最多只能有 10^2 - 10^3 km 的大小。而一个超级缩小版星球的半径为 10^2 km —— 星球自身就已经填满了场景,游戏基本上没办法玩了。有关场景大小的讨论还可见 Unity: coordinates and scales

Vector3(100,100,100) 100
Vector3(10000,10000,10000) 100
除此以外,对于物理引擎来说,小数位是绝对不能低于 3 位的,否则就会出现各种 jittering,比如摄像机追踪飞船的时候,飞船本身如果使用 AddRelativeForce() 进行加速,就会有非常剧烈的抖动(如图),并且也不能通过 Rigidbody.Interpolate 或者 Vector3.SmoothDamp 消除,这对于 Motion Blur 是完全不能忍受的。而要保证小数位是不低于 3 位,整数位也就只有 3 - 4 位可以用了 —— 场景大小瞬间砍去一半。 所以,解决方法就是 The Floating Origin Solution —— 即之前提过的 World Streamer 使用的方法:隔一段距离就重置世界坐标到新的 (0,0,0)。这个方法的描述可见 [Unite 2013 - Building a new universe in Kerbal Space Program](https://www.youtube.com/watch?v=mXTxQko-JH0) 以及 [C# - Is a custom coordinate system possible in Unity](http://gamedev.stackexchange.com/questions/110349/is-a-custom-coordinate-system-possible-in-unity)。 [wiki.unity3d.com](http://wiki.unity3d.com/index.php/Floating_Origin) 上有一个现成的示例脚本,其中的操作是在 LateUpdate() 中完成的。这个脚本目前来说唯一的问题就是会影响系统自带的 Trail Render,因为自带的 Trail Render 并没有整体移动的功能,也没有办法让 Trail Mesh 作为某个 Transform 的 child,所以解决办法只能是等学校补考过后自己写一个 Trail Render 好了。