關於 web service, unity, blogger 等軟體工程筆記

Loading inform pattern

Edit icon 沒有留言
loading

記錄在 Unity 中,實作載入任務的 patterns。在任務開始前開啟工作提示 (like activity inform 或是載入進度條),結束後關閉工作提示 。工作提示可以是開啟顯示進度條,或是其他任何可以提示使用者的顯示物件,在這次範例使用手持平台用的 ActivityIndicator,細節請參考 Unity 文件

// 手機平台限定 (Android, IOS...),會出現像首圖的動畫,提示目前有工作正在運作
// 開啟
Handheld.StartActivityIndicator();

// 關閉
Handheld.StopActivityIndicator();

因此載入任務範例如下,使用加載網路資源為範例,但可以是其他需等待的工作,例如檔案存取 (File read/ write),關卡載入 (Level Load) 等等:

IEnumerator DoTask() {
   Handheld.StartActivityIndicator();

   // Do task
   var req = UnityWebRequest.Get("http://example.com");
   yield return req.Send();

   // Do other tasks...

   Handheld.StopActivityIndicator();
}

考慮任務失敗的錯誤處理,在處理完錯誤後,關閉工作提示:

IEnumerator DoTask2() {
   Handheld.StartActivityIndicator();

   // Do task
   var req = UnityWebRequest.Get("http://example.com");
   yield return req.Send();
   if (req.isError) {
      // TODO: Handle error
      Handheld.StopActivityIndicator();
      yield break;
   }

   // Do other tasks...

   Handheld.StopActivityIndicator();
}

但這樣有點不乾淨,多個錯誤處理結束都需要關閉工作提示,這要不就得複製/貼上這麼多段的關閉工作提示的程式碼?或許改用 goto 可以讓程式碼乾淨許多:

IEnumerator DoTask3() {
   Handheld.StartActivityIndicator();

   // Do task
   var req = UnityWebRequest.Get("http://example.com");
   yield return req.Send();
   if (req.isError) {
      // TODO: Handle error
      goto END_TASK;
   }

   // Do other tasks...

   END_TASK:
   Handheld.StopActivityIndicator();
}

但 goto 不是每個人都可以接受的,因此想到 try/ finally:

IEnumerator DoTask4() {
   Handheld.StartActivityIndicator();
   try {

      // Do task
      var req = UnityWebRequest.Get("http://example.com");
      yield return req.Send();
      if (req.isError) {
         // TODO: Handle error
         yield break;
      }

      // Do other tasks...

   } finally {
      Handheld.StopActivityIndicator();
   }
}

但既然使用 try/ finally,就不得不想到 using 以及 IDisposable,因此建立 TaskIndicator 改成以下:

struct TaskIndicator: System.IDisposable {
   public static TaskIndicator Create() {
      Handheld.StartActivityIndicator();
      return new TaskIndicator();
   }

   public void Dispose() {
      Handheld.StopActivityIndicator();
   }
}

IEnumerator DoTask5() {
   using (TaskIndicator.Create()) {
      // Do task
      var req = UnityWebRequest.Get("http://example.com");
      yield return req.Send();
      if (req.isError) {
         // TODO: Handle error
         yield break;
      }

      // Do other tasks...

   }
}

簡單明瞭,用了 using 來處理關閉工作提示,在離開 using block 會呼叫 TaskIndicator.Dispose。未來也容易擴充,例如加入設定 Progress 的功能:

partial struct TaskIndicator: System.IDisposable {
   float progress;
   public float Progress {
      get {
         return this.progress;
      }
      set {
         this.progress = value;

         // TODO: Display progress on UI system
      }
   }
}

IEnumerator DoTask6() {
   using (var indicator = TaskIndicator.Create()) {
      ...
      indicator.Progress = 0.5f;
      ...
   }
}

或者採用 Controller & View 分離,採用更簡單的方式,多一層 Coroutine 處理:

IEnumerator DoTaskAndIndicator(IEnumerator task) {
   Handheld.StartActivityIndicator();
   yield return this.StartCoroutine(task);
   Handheld.StopActivityIndicator();
}

沒有留言: