本文简述了一种基于时间片(Timeslice)的对象更新(Update)方法
游戏开发中,我们一般都要进行大量的对象更新(Update)操作,拿 Unity 中的 MonoBehavior 举例,其便支持定义专门的 Update 方法来处理相关 Component 的更新:
using UnityEngine; public class CustomScript : MonoBehaviour { void Update() { // do CustomScript update here ... } }但是,当更新操作需要事件触发时(MonoBehaviour 的 Update 方法每帧都会执行,属于轮询触发),上述的方法就不太合适了,更好的一种方式是游戏逻辑自己来管理相关对象的更新触发:
using UnityEngine; public class UpdateObject { public void Update() { // do UpdateObject update here ... } }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 方法).
现在我们解决了事件触发对象更新的问题,但是当对象更新比较耗时时(轮询触发也存在这个问题),我们还是会遇到对象更新时产生的卡顿问题:
using UnityEngine; public class UpdateObject { public void Update() { // Update method may be very time cost, // like "log using Debug" Debug.Log("UpdateObject Update"); } }最普遍的解决方法就是优化 Update,使其耗时降低,但是很多情况下,这种方式也比较局限,更通用的一种方式则是分帧,即将对象的更新操作分摊到多帧中进行(而不是全部在一帧中执行完成).
实现分帧更新的方式很多,这里我使用了队列(并且做了进一步的优化,保证队列中的元素唯一):
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)的对象更新了(即分帧进行对象更新):
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 是一致的,但可以减少因为对象更新而引起的卡顿问题.
---来自腾讯云社区的---用户2615200
微信扫一扫打赏
支付宝扫一扫打赏