Unity游戏框架设计之消息管理器

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)
        {
            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))
        {
            return;
        }
        MessageConfig messageConfig = _messageSet[messageName];
        StartCoroutine(SleepTime(messageConfig.DelayTime, () => StartCoroutine(messageConfig.Message)));
    }

    public void RemoveMessage(string messageName)
    {
        if (!_messageSet.ContainsKey(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 参数中,必须先对当前执行环境进行检查,才能执行消息处理的代码。因为消息可能是延迟的,而延迟后的执行环境可能并不满足消息处理的条件。比如消息依赖的某些对象被禁用或销毁,此时应当停止消息处理,而是退出函数,甚至直接删除消息。action 执行环境检查代码推荐格式如下。

try
{
	if (gameObject == null || gameObject.IsDestroyed() || !gameObject.activeInHierarchy)
	{
		MessageManager.Instance.RemoveMessage(MessageName);
		MessageName = "";
		return;
	}
}
catch (MissingReferenceException)
{
	MessageManager.Instance.RemoveMessage(MessageName);
	MessageName = "";
	return;
}

后记

由于个人能力有限,文中不免存在疏漏之处,恳求大家斧正,一起交流,共同进步。

  • 12
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值