前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Sweet Snippet 之 Timeslice Update

Sweet Snippet 之 Timeslice Update

作者头像
用户2615200
发布2020-05-04 15:03:21
4950
发布2020-05-04 15:03:21
举报
文章被收录于专栏:tkokof 的技术,小趣及杂念

本文简述了一种基于时间片(Timeslice)的对象更新(Update)方法

游戏开发中,我们一般都要进行大量的对象更新(Update)操作,拿 Unity 中的 MonoBehavior 举例,其便支持定义专门的 Update 方法来处理相关 Component 的更新:

代码语言:javascript
复制
using UnityEngine;

public class CustomScript : MonoBehaviour 
{
    void Update()
    {
        // do CustomScript update here ...
    }
}

但是,当更新操作需要事件触发时(MonoBehaviour 的 Update 方法每帧都会执行,属于轮询触发),上述的方法就不太合适了,更好的一种方式是游戏逻辑自己来管理相关对象的更新触发:

代码语言:javascript
复制
using UnityEngine;

public class UpdateObject
{
    public void Update()
    {
        // do UpdateObject update here ...
    }
}
代码语言:javascript
复制
using System.Collections.Generic;
using UnityEngine;

public class UpdateManager : MonoBehaviour
{
    List<UpdateObject> m_objectList = new List<UpdateObject>();

    public void UpdateRequest(UpdateObject uo)
    {
        Debug.Assert(uo != null);
        if (!m_objectList.Contains(uo))
        {
            m_objectList.Add(uo);
        }
    }

    void Update()
    {
        if (m_objectList.Count > 0)
        {
            for (int i = 0; i < m_objectList.Count; ++i)
            {
                m_objectList[i].Update();
            }

            m_objectList.Clear();
        }
    }
}

上述代码中的 UpdateManager 便用于管理相关对象的更新触发操作(通过 UpdateRequest 方法).

现在我们解决了事件触发对象更新的问题,但是当对象更新比较耗时时(轮询触发也存在这个问题),我们还是会遇到对象更新时产生的卡顿问题:

代码语言:javascript
复制
using UnityEngine;

public class UpdateObject
{
    public void Update()
    {
        // Update method may be very time cost,
        // like "log using Debug"
        Debug.Log("UpdateObject Update");
    }
}

最普遍的解决方法就是优化 Update,使其耗时降低,但是很多情况下,这种方式也比较局限,更通用的一种方式则是分帧,即将对象的更新操作分摊到多帧中进行(而不是全部在一帧中执行完成).

实现分帧更新的方式很多,这里我使用了队列(并且做了进一步的优化,保证队列中的元素唯一):

代码语言:javascript
复制
using System.Collections.Generic;
using UnityEngine;

public class UniqueQueue<T>
{
    Queue<T> m_innerQueue = new Queue<T>();
    HashSet<T> m_elementSet = new HashSet<T>();

    public int Count
    {
        get
        {
            Debug.Assert(m_innerQueue.Count == m_elementSet.Count);
            return m_innerQueue.Count;
        }
    }

    public bool Contains(T element)
    {
        return m_elementSet.Contains(element);
    }

    public T Dequeue()
    {
        if (Count > 0)
        {
            var element = m_innerQueue.Peek();
            m_elementSet.Remove(element);

            return m_innerQueue.Dequeue();
        }
        else
        {
            return default(T);
        }
    }

    public bool Enqueue(T element)
    {
        if (!m_elementSet.Contains(element))
        {
            m_innerQueue.Enqueue(element);
            m_elementSet.Add(element);
            return true;
        }

        return false;
    }

    public void Clear()
    {
        m_innerQueue.Clear();
        m_elementSet.Clear();
    }
}

基于上述的 UniqueQueue, 我们便可以编写基于时间片(Timeslice)的对象更新了(即分帧进行对象更新):

代码语言:javascript
复制
using UnityEngine;

public class TimesliceUpdateManager : MonoBehaviour
{
    [SerializeField]
    float m_maxTimeslice = 1 / 30.0f;

    UniqueQueue<UpdateObject> m_objectUniqueQueue = new UniqueQueue<UpdateObject>();

    public void UpdateRequest(UpdateObject uo)
    {
        Debug.Assert(uo != null);
        m_objectUniqueQueue.Enqueue(uo);
    }

    void Update()
    {
        if (m_objectUniqueQueue.Count > 0)
        {
            float elapsedTime = 0;

            do
            {
                var curTime = Time.realtimeSinceStartup;

                var uo = m_objectUniqueQueue.Dequeue();
                uo.Update();

                elapsedTime += Time.realtimeSinceStartup - curTime;
            } while (elapsedTime < m_maxTimeslice && m_objectUniqueQueue.Count > 0);
        }
    }
}

TimesliceUpdateManager 的使用方法和上述的 UpdateManager 是一致的,但可以减少因为对象更新而引起的卡顿问题.

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/04/29 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档