Unity 计时器的实现

发表于2018-10-10
评论0 8.9k浏览
在Unity中,Time类的功能较少,例如Time.time在Awake等函数中无法获取,并且没有计时器概念等。考虑到项目可能用到计时器,例如说在游戏中记录时间等,为此这篇文章就给大家分享下如何实现计时器。

计时器的作用就是简单的计时,简单的代码如下。我们可以放在Update里面计时,同时我们也可以通过协程来计时,2种代码如下:
<code class="language-plain">      private void Update() 
{
	totalTime -= Time.deltaTime;  
	 if (totalTime <= 0)  
		 {  
			 //todo:计时结束
			 }
	}</code>  

这是放在Update里面的那种,还有另外一种:
     private float totalTime1 = 4;
        private IEnumerator Do()
        {
            while (true)
            {
                totalTime1 -= Time.deltaTime;
                if (totalTime1 <= 0)
                {
                    //todo:计时结束
                    yield break;
                }
                yield return null;
            }
        }

后面一种通过协程来实现的同样都比较简单,如果我们需要对一个拾取的加速道具计时的话,我们需要写一个speedTotalTime。如果再拾取一个无敌道具我们岂不是又要在这个类中添加另外一个变量么,要是这样写下去这个类就会变得很乱了么,所以写计时器的目的是为了能够让他变成一个工具,耦合性变得最低,这样所有需要用到计时器的项目都可以用到它,这里我们建一个基本的Timer类
 public class Timer
    {
        public float Duration;
        public float LeftTime;
        private Action _updateAction;
        private Action _callAction;
        private bool _isPause;
        public Timer(float duration,Action updateAction=null,Action callAction=null,Action intiAction = null)
        {
            LeftTime = duration;
            Duration = duration;
            if (intiAction != null) intiAction.Invoke();
            _updateAction = updateAction;
            _callAction = callAction;
            _isPause = false;
        }
        public void OnUpdate(float deltaTime)
        {
            LeftTime -= deltaTime;
            if (LeftTime <= 0)
            {
                if (_callAction != null)
                    _callAction.Invoke();
            }
            else
            {
                if (_updateAction != null && !_isPause)
                    _updateAction.Invoke();  
            }
        }
        public void SetTimerTrick(bool b)
        {
            _isPause = b;
        }
    }

Timer类基本必须的2个变量一个是这个计时器总共持续时间,还有就是这个计时器走了多长时间(即我们得知道还剩余多少时间),这些都是一个计时器都是最基本的,还有就是对计时器初始化时我们所需做的事件,计时器计时的我们所需做的操作,计时器计时结束的回调。同样我们需要对每个计时器进行管理,所以我们需要一个管理器。我们起名为TimerManager,既然是管理器那么他就存在2个基本的方法,AddTimer和RemoveTimer。所以具体代码如下:
public class TimerManager : Singleton<TimerManager>
    {
        private List<Timer> _timers;
        private Dictionary<string, Timer> _timerDict;
        private void Awake()
        {
            _timers = new List<Timer>();
            _timerDict = new Dictionary<string, Timer>();
        }
        private void Update()
        {
            for (int i = 0; i < _timers.Count; i++)
            {
                _timers[i].OnUpdate(Time.deltaTime);
            }
        }
        public void AddTimer(string str,Timer timer)
        {
            if (_timerDict.ContainsKey(str))
            {
                _timerDict[str].LeftTime += _timerDict[str].Duration;
            }
            else
            {
                _timerDict.Add(str, timer);
                _timers.Add(timer);
            }
        }
        public void RemoveTimer(string str)
        {
            var timer = _timerDict[str];
            if (timer != null)
            {
                _timers.Remove(timer);
                _timerDict.Remove(str);
            }
        }
    }

AddTimer主要的作用是往管理器中添加一个计时器,如果我们拾取一个加速道具的时候,在加速还没有结束的时候我们又拾取一个加速道具这时我们需要把道具的持续时间延长这时就需要对计时器的LeftTime变长。所以 _timerDict[str].LeftTime  +=_timerDict[str].Duration;如果机器是管理器。最后就是一个Update方法来统一管理计时器的计时了。

接下来就是运用一下计时器了。这里我们用道具来测试一下代码。当我们在拾取道具的时候我们需要判断是拾取加速道具还是无敌道具等其他的一些道具,这时候的判断就就得用ifelse来判断这样做的。当我们新添加一种道具的时候我们岂不是又要修改原先的代码了,所以我们得用面向对象的思想来解决这个问题。我们建一个基类:
  public abstract class BaseProp:MonoBehaviour
    {
        public float Duration;
        public abstract void Excute(Transform t);
    }

然后我们的加速道具就应该继承这个基类了
  public class SpeedProp : BaseProp
    {
        public override void Excute(UnityEngine.Transform t)
        {
            var entity = t.GetComponent<TestPlayerEntity>();
            TimerManager.Instance.AddTimer("Speed",new Timer(Duration,null, () =>
            {
                DecreaseSpeed(entity);
                TimerManager.Instance.RemoveTimer("Speed");
            }, () =>
            {
                IncreaseSpeed(entity);
            }));
        }
        private void IncreaseSpeed(TestPlayerEntity entity)
        {
            if (entity != null)
            {
                entity.MoveSpeed = 3;
            }
        }
        private void DecreaseSpeed(TestPlayerEntity entity)
        {
            if (entity != null)
            {
                entity.MoveSpeed = 1;
            }
        }
    }

接着我们的玩家类在碰到道具的时候只需
      public void OnTriggerEnter2D(Collider2D collision)
        {
            if (collision.transform.CompareTag("Prop"))
            {
                collision.GetComponent<BaseProp>().Excute(this.transform);
            }
        }
这样就避免了一长串的判断语句了同时解决了代码的耦合性了。好了关于实现计时器就介绍这么多,希望能帮到大家。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引