QFramework框架学习(三) ------ Unity3D 链式编程思想

QFramework 是一套 渐进式 的 快速开发 框架。目标是作为无框架经验的公司、独立开发者、以及 Unity3D 初学者们的 第一套框架。框架内部积累了多个项目的在各个技术方向的解决方案。学习成本低,接入成本低,重构成本低,二次开发成本低,文档内容丰富(提供使用方式以及原理、开发文档)。github:https://github.com/liangxiegame/QFramework


什么是链式编程?

我想大家应该都接触过DOTween,用起来是这样的。

  transform.DOMove(Vector3.one, 0.5f)
        .SetEase(Ease.InBack)
        .OnKill(() => Debug.Log("on killed"))
        .OnComplete(() => Debug.Log("on completed"));

像以上.XXX().YYY().ZZZ()这种写法就叫链式编程了。


DoTween链式思想里面还有个很有意思的分支,那就是委托+方法+返回This完成的链式编程。本文重点来了,那就是学习一下这个思路实现一个小巧实用的定时器。

代码

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace TimeTrigger
{
    public class Timer
    {
        static List<Timer> timers = new List<Timer>();
        Action<float> UpdateEvent;
        Action EndEvent;
        private float _time = -1;   // 用户设定的定时时长
        private bool _loop;          // 是否循环执行
        private bool _ignorTimescale;  // 是否忽略Timescale
        private string _flag;// 用户指定的定时器标志,便于手动清除、暂停、恢复
        private static TimerDriver driver = null;//拿驱动器的引用只是为了初始化驱动器
        private float CurrentTime { get { return _ignorTimescale ? Time.realtimeSinceStartup : Time.time; } }// 获得当前时间
        private float cachedTime;//缓存时间
        float timePassed;        //已经过去的时间
        private bool _isFinish = false; //计时器是否结束
        private bool _isPause = false; //计时器是否暂停

        private static bool showLog = true;  //确认是否输出Debug信息
        public static bool ShowLog { set { showLog = value; } }
        public bool IsPause // 暂停计时器
        {
            get { return _isPause; }
            set
            {
                if (value)
                {
                    Pause();
                }
                else
                {
                    Resum();
                }
            }

        }
        /// <summary>
        /// 构造定时器
        /// </summary>
        /// <param name="time">定时时长</param>
        /// <param name="flag">定时器标识符</param>
        /// <param name="loop">是否循环</param>
        /// <param name="ignorTimescale">是否忽略TimeScale</param>
        private Timer(float time, string flag, bool loop = false, bool ignorTimescale = true)
        {
            if (null == driver) driver = TimerDriver.Get; //初始化Time驱动
            _time = time;
            _loop = loop;
            _ignorTimescale = ignorTimescale;
            cachedTime = CurrentTime;
            if (timers.Exists((v) => { return v._flag == flag; }))
            {
                if (showLog) Debug.LogWarningFormat("【TimerTrigger(容错)】:存在相同的标识符【{0}】!", flag);
            }
            _flag = string.IsNullOrEmpty(flag) ? GetHashCode().ToString() : flag;//设置辨识标志符
        }
        private void Pause() // 暂停计时  
        {
            if (!_isFinish)
            {
               _isPause = true;
            }
        }
        private void Resum() // 继续计时  
        {
            if (!_isFinish&&_isPause)
            {
               cachedTime = CurrentTime-timePassed; 
                    _isPause = false;
            }
        }
        private void Update() // 刷新定时器
        {
            if (!_isFinish && !_isPause) //运行中
            {
                timePassed = CurrentTime - cachedTime;
                if (null != UpdateEvent) UpdateEvent(Mathf.Clamp01(timePassed / _time));
                if (timePassed >= _time)
                {
                    if (null != EndEvent) EndEvent();
                    if (_loop)
                    {
                        cachedTime = CurrentTime;
                    }
                    else
                    {
                        Stop();
                    }
                }
            }
        }
        private void Stop() // 回收定时器
        {
            if (timers.Contains(this))
            {
                timers.Remove(this);
            }
            _time = -1;
            _isFinish = true;
            _isPause = false;
            UpdateEvent = null;
            EndEvent = null;
        }
        #region--------------------------静态方法扩展-------------------------------------
        #region-------------添加定时器---------------
        /// <summary>
        /// 添加定时触发器
        /// </summary>
        /// <param name="time">定时时长</param>
        /// <param name="flag">定时器标识符</param>
        /// <param name="loop">是否循环</param>
        /// <param name="ignorTimescale">是否忽略TimeScale</param>
        public static Timer AddTimer(float time, string flag = "", bool loop = false, bool ignorTimescale = true)
        {
            Timer timer = new Timer(time, flag, loop, ignorTimescale);
            timers.Add(timer);
            return timer;
        }
        #endregion

        #region-------------刷新所有定时器---------------
        public static void UpdateAllTimer()
        {
            for (int i = 0; i < timers.Count; i++)
            {
                if (null != timers[i])
                {
                    timers[i].Update();
                }
            }
        }
        #endregion

        #region-------------暂停和恢复定时器---------------
        /// <summary>
        /// 暂停用户指定的计时触发器
        /// </summary>
        /// <param name="flag">指定的标识符</param>
        public static void PauseTimer(string flag)
        {
            Timer timer = timers.Find((v) => { return v._flag == flag; });
            if (null != timer)
            {
                timer.Pause();
            }
        }
        /// <summary>
        /// 恢复用户指定的计时触发器
        /// </summary>
        /// <param name="flag">指定的标识符</param>
        public static void ResumTimer(string flag)
        {
            Timer timer = timers.Find((v) => { return v._flag == flag; });
            if (null != timer)
            {
                timer.Resum();
            }
         }

        #endregion
        #region-------------删除定时器---------------
        /// <summary>
        /// 删除用户指定的计时触发器
        /// </summary>
        /// <param name="flag">指定的标识符</param>
        public static void DelTimer(string flag)
        {
            Timer timer = timers.Find((v) => { return v._flag == flag; });
            if (null != timer)
            {
                timer.Stop();
            }
        }
        /// <summary>
        /// 删除用户指定的计时触发器
        /// </summary>
        /// <param name="flag">指定的定时器</param>
        public static void DelTimer(Timer timer)
        {
            if (timers.Contains(timer))
            {
                timer.Stop();
            }
          }
        /// <summary>
        /// 删除用户指定的计时触发器
        /// </summary>
        /// <param name="completedEvent">指定的完成事件(直接赋值匿名函数无效)</param>
        public static void DelTimer(Action completedEvent)
        {
            Timer timer = timers.Find((v) => { return v.EndEvent == completedEvent; });
            if (null != timer)
            {
                timer.Stop();
            }
        }
        /// <summary>
        /// 删除用户指定的计时触发器
        /// </summary>
        /// <param name="updateEvent">指定的Update事件(直接赋值匿名函数无效)</param>
        public static void DelTimer(Action<float> updateEvent)
        {
            Timer timer = timers.Find((v) => { return v.UpdateEvent == updateEvent; });
            if (null != timer)
            {
                timer.Stop();
            }
        }
        #endregion
        #endregion

        #region-------------添加事件-------------------
        public Timer OnCompleted(Action completedEvent) //添加完成事件
        {
            if (null == EndEvent)
            {
                EndEvent = completedEvent;
            }
            return this;
        }
        public Timer OnUpdated(Action<float> updateEvent) //添加update更新事件
        {
            if (null == UpdateEvent)
            {
                UpdateEvent = updateEvent;
            }
            return this;
        }

        #endregion

        #region ---------------运行中的定时器参数修改-----------
        public void Setloop(bool loop) // 设置运行中的定时器的loop状态
        {
            if (!_isFinish)
            {
                _loop = loop;
            }
        }
        public void SetIgnoreTimeScale(bool ignoreTimescale)// 设置运行中的定时器的ignoreTimescale状态
        {
            if (!_isFinish)
            {
                _ignorTimescale = ignoreTimescale;
            }
        }
        #endregion

    }

    public class TimerDriver : MonoBehaviour
    {
        #region 单例
        private static TimerDriver _instance;
        public static TimerDriver Get
        {
            get
            {
                if (null == _instance)
                {
                    _instance = FindObjectOfType<TimerDriver>() ?? new GameObject("TimerEntity").AddComponent<TimerDriver>();
                }
                return _instance;
            }
        }
        private void Awake()
        {
            _instance = this;
        }
        #endregion
        private void Update()
        {
            Timer.UpdateAllTimer();
        }
    }
}

Tips:上面脚本为了节省篇幅,删除了很多注释和输出Log信息,可以下载下面的Demo体验更好,Demo含示例。

使用示例:


添加回调

Timer交互

Tips:使用Timer.DelTimer("12545");即可随时停止计时器,当然,DelTimer也提供了多个重载。

动画示例:


Tips: 演示了延时+是否循环+是否忽略TimeScale+回调,当然也包含Lambda表达式支持下的多参数回调。


暂停与恢复

Tips:由于所有定时器的驱动是同一个,并非Itween的那种对谁起作用就绑定在谁身上驱动。所以,如果回调中用到了游戏实体对象且对象总是显示隐藏不停切换的话,建议定时器也随之暂停和恢复.避免游戏对象不可见了,定时器依旧触发的尴尬局面(Itween是随着游戏对象的显示隐藏自动暂停和恢复的,毕竟就在那个游戏对象上驱动嘛)

写到最后:

这个计时核心算法(假装是算法哈)除了上面这个方案,还有-=(+=)Time.deltatime,还有 协程
说道协程,这里笔者也不是不想封装,有,只是后来发现协程的频繁开启停止发现有安全问题。所以摒弃了。
再说协程怎么点对点关闭呢,刚刚好,关于协程管理的笔者也恰写了点文字可以挪步瞧一瞧的;
Unity3D 协程管理
Unity3D IEnumerator 做一个通用的定时器

笔者编程实数小菜级别,互相学习咯。

Demo下载:https://pan.baidu.com/s/1pHSX6-6cTfI6KEKy2xqKmA


参考:

     1.Unity3D 使用链式编程思想的Timer Trigger

     2.Unity 游戏框架搭建 (十五) 优雅的QChain (零)

     3.Unity 游戏框架搭建 (十七) 静态扩展GameObject实现链式编程

     4.Unity 游戏框架搭建 (十八) 静态扩展 + 泛型实现transform的链式编程

猜你喜欢

转载自blog.csdn.net/dengshunhao/article/details/80996252
今日推荐