Unity游戏框架设计之消息管理器
简单介绍
消息管理器又可以称为任务管理器,主要解决延迟执行某些代码的问题。比如,我们希望一些代码可以延迟指定的时间后才执行,或者我们希望一些代码可以在固定的时间执行,又或者我们希望一些代码可以每隔一段时间就执行一次。消息管理器就是为了实现上述功能而开发的。下述的消息管理器是基于 Unity 的协程进行实现。
代码设计
public class MessageManager : SingletonMono<MessageManager>
{
private readonly Dictionary<string, MessageConfig> _messageSet = new();
private void CreateMessage<T>(string messageName, float delayTime, float intervalTime, int cycleCount, Action<T> action, T param)
{
if (FloatUtils.IsEqualsTo(intervalTime, 0f) && cycleCount <= -1)
{
LogManager.Instance.DebugWarning($"无法创建间隔时间为 0 的无限循环消息。messageName={messageName}");
return;
}
IEnumerator task = CycleMessage(messageName, 0f, action, param);
MessageConfig messageConfig = new MessageConfig(task, delayTime, intervalTime, cycleCount);
_messageSet.Add(messageName, messageConfig);
}
public void PublishDelayMessage<T>(string messageName, float delayTime, Action<T> action, T param)
{
CreateMessage(messageName, delayTime, float.MaxValue, 1, action, param);
PublishMessage(messageName);
}
public void PublishFiniteCycleMessage<T>(string messageName, float delayTime, float intervalTime, int cycleCount, Action<T> action, T param)
{
CreateMessage(messageName, delayTime, intervalTime, cycleCount, action, param);
PublishMessage(messageName);
}
public void PublishInfiniteCycleMessage<T>(string messageName, float delayTime, float intervalTime, Action<T> action, T param)
{
CreateMessage(messageName, delayTime, intervalTime, -1, action, param);
PublishMessage(messageName);
}
public void PublishTimingMessage<T>(string messageName, float fixedTime, Action<T> action, T param)
{
CreateMessage(messageName, fixedTime - Time.realtimeSinceStartup, float.MaxValue, 1, action, param);
PublishMessage(messageName);
}
private void PublishMessage(string messageName)
{
if (!_messageSet.ContainsKey(messageName))
{
LogManager.Instance.DebugWarning($"消息不存在。messageName={messageName}");
return;
}
MessageConfig messageConfig = _messageSet[messageName];
StartCoroutine(SleepTime(messageConfig.DelayTime, () => StartCoroutine(messageConfig.Message)));
}
public void RemoveMessage(string messageName)
{
if (!_messageSet.ContainsKey(messageName))
{
LogManager.Instance.DebugWarning($"消息不存在。messageName={messageName}");
return;
}
MessageConfig messageConfig = _messageSet[messageName];
StopCoroutine(messageConfig.Message);
_messageSet.Remove(messageName);
}
public void RemoveMessageIfExists(string messageName)
{
if (_messageSet.ContainsKey(messageName))
{
MessageConfig messageConfig = _messageSet[messageName];
_messageSet.Remove(messageName);
StopCoroutine(messageConfig.Message);
}
}
public bool ContainMessage(string messageName)
{
return _messageSet.ContainsKey(messageName);
}
private IEnumerator CycleMessage<T>(string messageName, float intervalTime, Action<T> action, T param)
{
if (!_messageSet.ContainsKey(messageName))
{
yield break;
}
MessageConfig messageConfig = _messageSet[messageName];
if (messageConfig.CycleCount >= 1)
{
messageConfig.CycleCount--;
}
else if (messageConfig.CycleCount == 0)
{
_messageSet.Remove(messageName);
yield break;
}
yield return new WaitForSeconds(intervalTime);
action.Invoke(param);
if (FloatUtils.IsEqualsTo(intervalTime, 0f) && FloatUtils.IsNotEqualsTo(messageConfig.IntervalTime, 0f))
{
messageConfig.Message = CycleMessage(messageName, messageConfig.IntervalTime, action, param);
}
else
{
messageConfig.Message = CycleMessage(messageName, intervalTime, action, param);
}
StartCoroutine(messageConfig.Message);
}
private IEnumerator SleepTime(float delayTime, Action callback)
{
if (FloatUtils.IsGreaterThan(delayTime, 0f))
{
yield return new WaitForSeconds(delayTime);
}
callback();
}
private class MessageConfig
{
public IEnumerator Message;
public readonly float DelayTime;
public readonly float IntervalTime;
public int CycleCount;
public MessageConfig(IEnumerator message, float delayTime, float intervalTime, int cycleCount)
{
Message = message;
DelayTime = delayTime;
IntervalTime = intervalTime;
CycleCount = cycleCount;
}
}
}
代码说明
(一)实现延迟消息(倒计时器)、定时消息(定时器)、有限循环消息和无限循环消息(循环调度器)。
(二)messageName 命名格式推荐 $"{ScriptName}{MethodName}{ID}" 。必须为所有的 messageName 添加唯一的 ID 后缀,防止出现冲突。唯一 ID 可以通过时间戳和递增的计数器来实现。由于在 Unity 中是单线程执行的,因此不必考虑线程安全问题,也不必考虑加锁问题。
(三)在传递的 action 参数中,必须先对当前执行环境进行检查,才能执行消息处理的代码。因为消息可能是延迟的,而延迟后的执行环境可能并不满足消息处理的条件。比如消息依赖的某些对象被禁用或销毁,此时应当停止消息处理,而是退出函数,甚至直接删除消息。
后记
由于个人能力有限,文中不免存在疏漏之处,恳求大家斧正,一起交流,共同进步。
标签:管理器,messageSet,框架,messageConfig,float,param,Unity,messageName,action From: https://www.cnblogs.com/kkelin/p/18166773