游戏架构:核心技术与面试精粹
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

2.2 时间记录

请问在Unity 3D中常见的时间分为哪几类,获取和记录它们的方法有哪些?它们的适用场景分别是什么?

问题分析

编写游戏的实时反馈型系统时,不可避免地涉及时间,比如人物行走的位移计算、战斗中的延迟产生伤害等。在编写这些功能时,我们要小心地处理对应逻辑。例如,在掉帧的低端机上,帧率会处于波动状态,这时在编写时间相关的逻辑运算时,就要防止出现与标准帧率不一致的情况。在商业项目中,通常都会编写自己的时间处理类,以得到跨设备准确无误差的时间值。

真实时间线

为了方便理解和说明,我们可以将游戏中的时间想象成一条沿着X轴正向的射线,这被称为时间线(TimeLine)。游戏的时间为沿着射线不断向正方向扩充的矩形区域,射线的原点与矩形的起始点为相对零点。

具体到真实时间线,它的起点为一个逻辑上的零点,它可以是设备的开机时间,也可以是游戏开始运行的时间,甚至是服务器下发的服务器时间。总之,它是一个在实际世界中真实存在的确定值。真实时间线(Real TimeLine)指的就是,以真实时间的流逝作为标准,进行时间统计的时间线。从重要程度上讲,它代表着绝对的时间流逝,是一切时间的参考,不同设备得到的结果一定是相同的,这也是整个时间记录的基石。

在Unity 3D中,使用Time.realtimeSinceStartup可以获取游戏运行了多长时间。为了测试功能,笔者创建了一个名为TestTime.cs的文件,挂载场景中的GameObecjt后,在其中输入如下代码:

    using UnityEngine;
    using System.Collections;
    using System.Collections.Generic;

    public class TestTime : MonoBehaviour
    {
      void OnGUI()
      {
        GUILayout.Space(20);
        GUILayout.Label("RealTime: "+Time.realtimeSinceStartup);
      }
    }

运行游戏可以看到记录时间的文字出现在游戏视窗中。

游戏时间线

游戏时间线(Game TimeLine)是应用更广泛的时间计量方式。它以游戏世界的时间流逝速度作为标准度量时间。很多情况下,它与真实时间线不同。例如,当游戏呼出暂停菜单时,真实时间仍然在流逝,而游戏时间却应该停止。又比如,在回放战斗录像时,我们希望快进整个战斗过程,这时游戏时间的流逝速度就要快于真实时间。在有些强调打击回馈感的动作游戏中,游戏时间线速率会不停地变化,让玩家在忽快忽慢中体验战斗的乐趣。

游戏时间线也可用于辅助调试。为了追查不正常的渲染,通过停止游戏时间,开发者可以冻结所有的动作和特效。这时如果有能够控制摄像机移动的工具,开发者就可以在场景中漫游,查找问题产生的原因。更进一步,通过推动游戏时间向前移动帧时间(1/FPS),开发者可以实现逐帧调试的功能。暂停游戏的另一个好处是方便定位Bug现场。结合代码报错的监控模块,可以很容易地捕捉报错现场,有利于问题的解决,在后面我们还会提到这点。

在Unity 3D中,我们可以使用Time.time来获取游戏时间,通过Time.timeScale来控制游戏时间流逝的速度。更改TestTime.cs脚本中的代码如下:

    using UnityEngine;
    using System.Collections;

    public class TestTime : MonoBehaviour
    {
      void OnGUI()
      {
        GUILayout.Label("TIme Scale:"+Time.timeScale);
          Time.timeScale=
            GUILayout.HorizontalSlider(Time.timeScale,0,2, GUILayout.Width(200));
        GUILayout.Space(20);

        GUILayout.Label("RealTime: "+Time.realtimeSinceStartup);
        GUILayout.Label("GameTime: "+Time.time);
        GUILayout.Space(20);

      }
    }

编辑好后,运行游戏。拖动游戏窗口中的拖动条,可以更改游戏时间的流逝速度。例如,笔者将其更改为0.5,则真实时间每过2s,游戏时间就过1s。另外,当在Editor中暂停游戏时,游戏时间也是不计算的。程序运行效果如图2.3所示。

图2.3

间隔时间

有了上面的概念,间隔时间就容易理解了。间隔时间的计算方法为计算两帧之间的差时,它也会用在很多场景中。例如,在计算距离时,我们通常会用间隔时间与速度相乘,这样得到的结果就是每秒的位移,而不是每帧的位移。

理想情况下,帧率不会发生改变,也是就说,逻辑计算的频率是固定的。如果游戏时间线缩放变小,即游戏变慢,则实际单位时间内运行的逻辑计算次数将增加。为了使最终结果相同,游戏时间线中的间隔时间就会变短,即游戏间隔时间与游戏时间流逝速率成正比。当然,更多情况是帧率会抖动。如果我们需要使用过去或当前的间隔时间,来预测物体未来的位置状态,则使用平均间隔时间会更加合理。

在Unity中通过Time.unscaledDeltaTime可以取到真实的间隔时间,Time.deltaTime可以取到游戏间隔时间,Time.smoothDeltaTime可以取到平均的间隔时间,这个“平均”是基于游戏时间线的。

由于OnGUI函数每帧会被调用多次,虽然每次调用的值都是本帧的值,但影响我们观察数据,因此笔者将对应输出写在了Update函数中。在TestTime.cs文件中加入如下代码:

    using UnityEngine;
    using System.Collections;

    public class TestTime : MonoBehaviour
    {
      //......
      void Update()
      {
        Debug.Log(" TimeScale:"+Time.timeScale+"\n"+
          "DeltaTime"+Time.deltaTime+"\n"+
          "Unscaled DeltaTime"+Time.unscaledDeltaTime+"\n"+
          "Smooth DeltaTime"+Time.smoothDeltaTime);

      }
    }

编辑完成后,运行代码。当调整游戏视图中的TimeScale为0.5时,可以看到游戏真实的流逝时间是0.008,而间隔时间为0.004,是实际间隔时间的一半,如图2.4所示。

图2.4

总结

深入了解时间统计的原理、时间线概念及Unity 3D对应的接口函数,我们可以更方便地设计出适合自己游戏的时间线控制类。在实际项目开发过程中,应尽早在不同性能的机器上进行时间相关的测试,以防止出现某些逻辑用错时间线,引发难以更改的问题。

扩展问题

现在有两种情况引起游戏间隔时间变长:一种是目标机器性能差,需要追赶补偿效果;另一种是断点调试,不希望影响逻辑。请问有没有办法在设计时间线时,过滤掉断点调试的情况([:gametimelong])?