Unity3D之暂停


Unity3D可以通过Time.timeScale实现暂停、快进和慢进功能。
关于Time.timeScale和Update()普遍有个误区,认为当Time.timeScale = 0时,Update()停止工作。
在FPS = 60、FixedTimestep = 0.02f 的条件下测试了一下:

  • 当Time.timeScale = 0时,Update()和LateUpdate()每秒执行60次,FixedUpdate()不执行,Time.deltaTime = 0.0167f。
  • 当Time.timeScale = 0.5f时,Update()和LateUpdate()每秒执行60次,FixedUpdate()每秒执行25次,Time.deltaTime = 0.0167f。
  • 当Time.timeScale = 1时,Update()和LateUpdate()每秒执行60次,FixedUpdate()每秒执行50次,Time.deltaTime = 0.0083f。
  • 当Time.timeScale = 2时,Update()和LateUpdate()每秒执行60次,FixedUpdate()每秒执行100次,Time.deltaTime = 0.0333f。

结论,Update()和LateUpdate()每秒执行FPS次,FixedUpdate()每秒执行Time.timeScale/FixedTimestep次,Time.deltaTime = Time.timeScale/FPS。
所以,Update()和LateUpdate()的执行与Time.timeScale无关。
Time.timeScale改变的是Time.time和Time.deltaTime,由于FixedUpdate()的执行是与时间相关的,所以FixedUpdate()会受到影响。


从上面的测试中,我们得知当Time.timeScale = 0时,Update()还是会正常执行的,只是Time.deltaTime = 0造成了如transform.position += Vector3.forward * 60 * Time.deltaTime不执行的假象。
如果Update()中有transform.position += Vector3.forward这种语句,物体将持续运动,不会暂停。
我们将通过以下脚本解决暂停问题:

using UnityEngine;
using System.Collections;

public class PauseObject : MonoBehaviour
{
    protected bool _isPause = false;

    public virtual bool Pause()
    {
        if (_isPause) return false;

        _isPause = true;

        return true;
    }

    public virtual bool Resume()
    {
        if (!_isPause) return false;

        _isPause = false;

        return true;
    }
}

将需要暂停的脚本继承PauseObject,然后在继承后的脚本Update()里写:

void Update()
{
    if (_isPause) return;

    DoingSomething();
}

也可以在Pause()中添加enable = false,Resume()添加enable = ture解决,这样就不用在每个继承的脚本中判断_isPause了。
但是设置enable会触发OnEnable()或OnDisable(),如果代码中某些功能依赖于OnEnable()或OnDisable()则会造成一些麻烦,为了保险起见,最好不要这样做。

然后编写PauseController:

using UnityEngine;
using System.Collections;

public class PauseController : MonoBehaviour
{
    static PauseObject[] _pauseObjects;

    public static void Pause()
    {
        Time.timeScale = 0;

        _pauseObjects = FindObjectsOfType<PauseObject>();
        foreach(PauseObject pauseObject in _pauseObjects)
        {
            pauseObject.Pause();
        }
    }

    public static void Resume()
    {
        Time.timeScale = 1;

        if (null == _pauseObjects) return;

        foreach(PauseObject pauseObject in _pauseObjects)
        {
            if (pauseObject != null) pauseObject.Resume();
        }
    }
}

暂停调用PauseController.Pause(),恢复调用PauseController.Resume()
在Resume()中最好不要使用_pauseObjects = FindObjectsOfType<PauseObject>();
一是防止不必要的开销,二是如果在暂停后场景中新添了可暂停物体,那么会带了一些不必要的麻烦,即使在PauseObject做了状态判断。
if (pauseObject != null)是为了防止在暂停后,场景有物体销毁造成pauseObject为空。


好了,现在我们已经实现暂停和恢复功能了,但还是有点问题。
一般在暂停后,我们会弹出一个暂停窗口,暂停窗口需要一个弹出的动画。
此时Time.deltaTime = 0,暂停窗口不会有位移,所以我们需要自己实现一个计时器:

using UnityEngine;
using System.Collections;

public class PauseRealTime : MonoBehaviour
{
    static float _deltaTime;
    static float _curPointTime;
    static float _lastPointTime;
    static bool _isInstance = false;

    public static float time
    {
        get
        {
            if (!_isInstance) Instance();
            return _curPointTime;
        }
    }

    public static float deltaTime
    {
        get
        {
            if (!_isInstance) Instance();
            return _deltaTime;
        }
    }

    static void Instance()
    {
        _isInstance = true;

        GameObject obj = new GameObject("Pause Real Time");
        obj.AddComponent<PauseRealTime>();

        DontDestroyOnLoad(obj);
    }

    void Start()
    {
        _curPointTime = Time.realtimeSinceStartup;
    }

    void LateUpdate()
    {
        _lastPointTime = _curPointTime;
        _curPointTime = Time.realtimeSinceStartup;
        _deltaTime = Mathf.Clamp01(_curPointTime - _lastPointTime);
    }
}

把Time.deltaTime替换为PauseRealTime.deltaTime就好了。
写完发现NGUI自己也实现了一个计时器RealTime,有NGUI的同学,也可以直接调用RealTime.deltaTime。
这样就实现了暂停后的动画。


目前在项目中还不需要暂停后播放Animator或Animation,所以还未实现。
等有时间时再补完这部分。

下篇将写不使用Time.timeScale实现的暂停机制。

转载于:https://www.cnblogs.com/lainian/p/3869325.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值