国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

【Unity】中如何統(tǒng)一管理回調(diào)函數(shù)(利用觀察者模式)

2019-11-09 14:34:57
字體:
供稿:網(wǎng)友

這次的內(nèi)容有點(diǎn)類似設(shè)計(jì)模式里的觀察者模式。但是和常規(guī)意義上的觀察者模式也不是完全一致,所以各位就不要咬文嚼字啦!咦?設(shè)計(jì)模式?!不懂!沒關(guān)系,說不定你以前就用過。

開場白

我們來想象一個場景。在加載一個模型時,你需要從網(wǎng)上下載,但是你并不知道下載需要花費(fèi)多少時間。你所知道的是,當(dāng)下載完成后,就可以把模型放在特定位置上,開始游戲。那么,我們怎樣才能判斷下載完成呢?一個簡單的方法是,在每一幀的時候都判斷下載是否完成,完成后就可以繼續(xù)后面的工作。因此,我們可以這樣做,我們告訴一個管理器,嗨,你幫我盯著點(diǎn),看下載完了沒有,完了就叫我一聲,好讓我執(zhí)行XXX函數(shù)。我們今天要做的,就是構(gòu)造這樣一個管理器。

實(shí)現(xiàn)

注意,下面的代碼依賴于之前所講到的單例模式。我們不防把上面這樣一件工作成為一個計(jì)數(shù)器——Timer(這個名字可能不太恰當(dāng)),把需要被通知者成為觀察者——Oberver,而像下載管理器這樣的對象成為一個主題——Subject。首先,我們來定義觀察者和主題對象。TimerObserverOrSubject.cs如下:[csharp] view plain copy PRint?using UnityEngine;  using System.Collections;    public class TimerObserverOrSubject : MonoBehaviour {            virtual protected void OnDestroy ()      {          if(Singleton.IsCreatedInstance("TimerController"))          {              (Singleton.getInstance("TimerController") as TimerController).ClearTimer(this);          }      }  }  TimerObserverOrSubject.cs的內(nèi)容非常簡單,它的工作就是在該腳本被析構(gòu)時,及時地從計(jì)數(shù)器管理器里面刪除涉及這個對象的所有Timer。計(jì)數(shù)器管理器的腳本——TimerController.cs如下:[csharp] view plain copy print?using UnityEngine;  using System.Collections;  using System.Collections.Generic;     public class TimerController : MonoBehaviour {            public delegate void OnCallBack(object arg);      public delegate bool OnIsCanDo(object arg);            public class Timer {          public TimerObserverOrSubject m_Observer;          public OnCallBack m_Callback = null;          public object m_Arg = null;                    public TimerObserverOrSubject m_Subject;          public OnIsCanDo m_IsCanDoFunc = null;           public object m_ArgForIsCanDoFunc = null;                    public float m_PassTime = 0;                    public Timer(TimerObserverOrSubject observer, OnCallBack callback, object arg,               TimerObserverOrSubject subject, OnIsCanDo isCanDoFunc, object argForIsCanDo) {              m_Observer = observer;              m_Callback = callback;              m_Arg = arg;                             m_Subject = subject;              m_IsCanDoFunc = isCanDoFunc;              m_ArgForIsCanDoFunc = argForIsCanDo;                            m_PassTime = 0;                  }                    public Timer(TimerObserverOrSubject observer, OnCallBack callback, object arg, float time) {              m_Observer = observer;              m_Callback = callback;              m_Arg = arg;                            m_Subject = null;              m_IsCanDoFunc = null;              m_ArgForIsCanDoFunc = null;                            m_PassTime = time;          }          }      private List<Timer> m_Timers = new List<Timer>();      private List<Timer> m_NeedRemoveTimer = new List<Timer>();      private List<Timer> m_CurRunTimer = new List<Timer>();             /// <summary>      /// Sets the timer.      /// </summary>      /// <param name='observer'>      /// The TimerObserverOrSubject you need to listen      /// </param>      /// <param name='callback'>      /// The callback when condition is true.      /// </param>      /// <param name='arg'>      /// Argument of the callback.      /// </param>      /// <param name='observer'>      /// The TimerObserverOrSubject you need to observe      /// </param>      /// <param name='isCanDoFunc'>      /// The condition function, must return a boolean.      /// </param>      /// <param name='argForIsCanDo'>      /// Argument for condition function.      /// </param>      public void SetTimer(TimerObserverOrSubject observer, OnCallBack callback ,object arg,          TimerObserverOrSubject subject, OnIsCanDo isCanDoFunc,object argForIsCanDo) {          if (observer == null || subject == null || callback == null || isCanDoFunc == null) return;                    if (isCanDoFunc(argForIsCanDo)) {              callback(arg);              return;          }                    Timer timer = new Timer(observer, callback, arg, subject, isCanDoFunc, argForIsCanDo);               m_Timers.Add(timer);      }             /// <summary>      /// Sets the timer.      /// </summary>      /// <param name='observer'>      /// The TimerObserverOrSubject you need to listen      /// </param>      /// <param name='callback'>      /// The callback when time is up.      /// </param>      /// <param name='arg'>      /// Argument of the callback.      /// </param>      /// <param name='timepass'>      /// Timepass before calling the callback.      /// </param>      public void SetTimer(TimerObserverOrSubject observer, OnCallBack callback , object arg, float timepass) {          if (observer != null && callback != null) {                         Timer timer = new Timer(observer, callback, arg, timepass);              m_Timers.Add(timer);          }      }             /// <summary>      /// Clears all Timers of the observer.      /// </summary>      /// <param name='observer'>      /// The TimerObserverOrSubject you need to clear      /// </param>      public void ClearTimer(TimerObserverOrSubject observer) {          List<Timer> needRemovedTimers = new List<Timer>();                    foreach (Timer timer in m_Timers) {              if (timer.m_Observer == observer || timer.m_Subject) {                  needRemovedTimers.Add(timer);              }          }                    foreach (Timer timer in needRemovedTimers) {              m_Timers.Remove(timer);          }      }                 // Update is called once per frame          void Update ()       {          InitialCurTimerDict();          RunTimer();          RemoveTimer();          }            private void InitialCurTimerDict() {          m_CurRunTimer.Clear();                    foreach (Timer timer in m_Timers) {              m_CurRunTimer.Add(timer);          }      }            private void RunTimer() {          m_NeedRemoveTimer.Clear();                    foreach (Timer timer in m_CurRunTimer) {                      if (timer.m_IsCanDoFunc == null) {                  timer.m_PassTime =  timer.m_PassTime - Time.deltaTime;                  if (timer.m_PassTime < 0) {                      timer.m_Callback(timer.m_Arg);                      m_NeedRemoveTimer.Add(timer);                  }              } else {                  if (timer.m_IsCanDoFunc(timer.m_ArgForIsCanDoFunc)) {                      timer.m_Callback(timer.m_Arg);                      m_NeedRemoveTimer.Add(timer);                  }              }             }      }            private void RemoveTimer() {          foreach (Timer timer in m_NeedRemoveTimer) {              if (m_Timers.Contains(timer)) {                  m_Timers.Remove(timer);              }          }      }    }  首先,它定義了回調(diào)函數(shù)的類型:[csharp] view plain copy print?public delegate void OnCallBack(object arg);  public delegate bool OnIsCanDo(object arg);  關(guān)于C#的委托機(jī)制,如果有童鞋不了解,請?jiān)斠姽俜轿臋n。簡單來說,委托類似一個函數(shù)指針,常被用于回調(diào)函數(shù)。然后,定義了一個數(shù)據(jù)類型Timer用于保存一個計(jì)數(shù)器的各個信息。接下來,就是TimerController的兩個重要的SetTimer函數(shù)。我們先看第一個SetTimer函數(shù):[csharp] view plain copy print?/// <summary>  /// Sets the timer.  /// </summary>  /// <param name='observer'>  /// The observer to observe the subject  /// </param>  /// <param name='callback'>  /// The callback when condition is true.  /// </param>  /// <param name='arg'>  /// Argument of the callback.  /// </param>  /// <param name='subject'>  /// The subject you need to observe  /// </param>  /// <param name='isCanDoFunc'>  /// The condition function, must return a boolean.  /// </param>  /// <param name='argForIsCanDo'>  /// Argument for condition function.  /// </param>  public void SetTimer(TimerObserverOrSubject observer, OnCallBack callback ,object arg,      TimerObserverOrSubject subject, OnIsCanDo isCanDoFunc,object argForIsCanDo) {      if (observer == null || subject == null || callback == null || isCanDoFunc == null) return;            if (isCanDoFunc(argForIsCanDo)) {          callback(arg);          return;         }            Timer timer = new Timer(observer, callback, arg, subject, isCanDoFunc, argForIsCanDo);           m_Timers.Add(timer);  }  根據(jù)函數(shù)說明可以看出,它負(fù)責(zé)建立一個計(jì)數(shù)器,當(dāng)subject的isCanDoFunc(argForIsCanDo)函數(shù)返回true時,通知observer,執(zhí)行observer的callback(arg)函數(shù)。第二個SetTimer函數(shù)更簡單:[csharp] view plain copy print?/// <summary>  /// Sets the timer.  /// </summary>  /// <param name='observer'>  /// The observer to observe the subject  /// </param>  /// <param name='callback'>  /// The callback when time is up.  /// </param>  /// <param name='arg'>  /// Argument of the callback.  /// </param>  /// <param name='timepass'>  /// Timepass before calling the callback.  /// </param>  public void SetTimer(TimerObserverOrSubject observer, OnCallBack callback , object arg, float timepass) {      if (observer != null && callback != null) {                     Timer timer = new Timer(observer, callback, arg, timepass);          m_Timers.Add(timer);      }  }  它負(fù)責(zé)建立一個計(jì)數(shù)器,在timepass的時間后,通知observer,執(zhí)行observer的callback(arg)函數(shù)。Update()函數(shù)里面負(fù)責(zé)檢查所有Timer是否可以觸發(fā)以及是否需要刪除。

例子

在這個例子里,我們需要在程序開始運(yùn)行5秒后,打印一些信息。當(dāng)然這個的實(shí)現(xiàn)有很多方法,這里我們使用今天實(shí)現(xiàn)的TimerController來實(shí)現(xiàn)。TimerSample.cs的內(nèi)容如下:[csharp] view plain copy print?using UnityEngine;  using System.Collections;    public class TimerSample : TimerObserverOrSubject {            private TimerController m_TimerCtr = null;            private bool m_IsCanDisplay = false;            private string m_DisplayContent = "Hello, candycat!";            // Use this for initialization      void Start () {          m_TimerCtr = Singleton.getInstance("TimerController") as TimerController;                    //m_TimerCtr.SetTimer(this, Display, m_DisplayContent, 5);                    m_TimerCtr.SetTimer(this, Display, null, this, IsCanDisplay, null);                    StartCoroutine(DelayDisplay());      }            void Display(object arg) {          if (arg == null) {              Debug.Log(m_DisplayContent);          } else {              string content = arg as string;                        Debug.Log(content);          }      }            bool IsCanDisplay(object arg) {          return m_IsCanDisplay;      }            IEnumerator DelayDisplay() {          yield return new WaitForSeconds(5.0f);                    m_IsCanDisplay = true;      }            // Update is called once per frame      void Update () {            }  }  首先,它向TimerController請求注冊了一個計(jì)時器。這里,它的條件是IsCanDisplay函數(shù),它返回bool值m_IsCanDisplay。而這個值將會在5秒后,通過協(xié)同函數(shù)DelayDisplay來由false置為true。當(dāng)其為true時,TimerController就將通知TimerSample調(diào)用Display函數(shù)。我們將第16行代碼注釋解開,并將18-20行代碼注釋掉,則可以達(dá)到相同的效果。

結(jié)束語

C#的委托機(jī)制還是非常常用的,使用Unity的童鞋最好還是了解一下。關(guān)于TimerController的執(zhí)行效率,由于它是每一幀都要去判斷所有的condition函數(shù),所以應(yīng)當(dāng)讓condition函數(shù)中的邏輯盡可能簡單。好了,這次就到這里,如果有更好的想法,或者這里的代碼有什么問題,都非常歡迎指正。謝謝閱讀!
發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 邵武市| 中牟县| 长泰县| 大余县| 忻州市| 嵩明县| 克拉玛依市| 璧山县| 东台市| 桂林市| 临沭县| 同德县| 如东县| 印江| 涪陵区| 安顺市| 金平| 黔东| 东乌| 沿河| 桂平市| 兴宁市| 雷山县| 滨海县| 米林县| 甘孜县| 三门峡市| 东平县| 平陆县| 高碑店市| 宽城| 诸城市| 宕昌县| 平邑县| 洪湖市| 济宁市| 五常市| 德钦县| 梧州市| 淮南市| 静海县|