Unity 離線建造系統
很多遊戲,特别是養成類手遊,都會有自己獨特的建造系統,一個建造裝置的狀态循環或者說生命周期一般是這樣的:
1.準備建造,設定各項資源的投入等
2.等待一段倒計時,正在建造中
3.建造結束,選擇是否收取資源
大體上,可以将建造盒子分為以下三種狀态,每一個狀态的邏輯和顯示的頁面不同:
1 public enum BuildBoxState
2 {
3 Start,
4 Doing,
5 Complete
6 }
1 private void ShiftState(BuildBoxState state)
2 {
3 switch (state)
4 {
5 case BuildBoxState.Start:
6 Start.SetActive(true);
7 Doing.SetActive(false);
8 Complete.SetActive(false);
9
10 ResetResCount();
11 break;
12 case BuildBoxState.Doing:
13 Start.SetActive(false);
14 Doing.SetActive(true);
15 Complete.SetActive(false);
16
17 StartCoroutine(ShowBuildTime());
18 break;
19 case BuildBoxState.Complete:
20 Start.SetActive(false);
21 Doing.SetActive(false);
22 Complete.SetActive(true);
23
24 break;
25 }
26 CurState = state;
27 }
這裡值得思考的并非是狀态的切換或者基礎的按鈕偵聽,視圖資源更新等。
如何在離線一段時間後重新擷取目前對應建造盒子所處的狀态才是重點;并且如果處于建造中狀态的話,還應該能正确的顯示剩餘時間的倒計時。
一個非常常見的想法是,在建造開始時記錄一份開始建造的時間資料給伺服器或存在本地離線資料中,當下一次再登入時讀取目前系統的時間,并通過總共需要的建造時長來計算剩餘時間。
但假如總共需要的建造時長與當時投入的資源類型和量都有關系,這時就需要至少額外記載一類資料來進行計算。那麼,有沒有方法僅通過一個資料得到剩餘時長呢?
答案是,不記錄開始建造的時刻,改為記錄拟定建造完成的時刻。
如此一來,每次離線登入後,隻需要幹兩件事既可以判斷出所有狀态視圖:
1.是否存在該建造盒子ID對應的拟定建造完成時刻的資料,如果不存在,一定是處于準備狀态,即Start狀态。
2.如果存在,對比目前系統時刻與拟定建造完成時刻的資料大小,大于等于則處于完成狀态,小于則依然在建造中,并按秒顯示內插補點更新。
記錄的時刻如下:
1 public string BuildCompleteTime
3 get
5 if (PlayerPrefs.HasKey(ID.ToString()))
6 return PlayerPrefs.GetString(ID.ToString());
7 return S_Null;
8 }
9 set
10 {
11 PlayerPrefs.SetString(ID.ToString(), value);
12 PlayerPrefs.Save();
13 }
14 }
每次開始時,隻需要判斷這個資料是否存在:
1 protected override void InitState()
3 View = HudView as BuildBoxView;
4 if (BuildCompleteTime == S_Null)
5 {
6 ShiftState(BuildBoxState.Start);
7 }
8 else
9 {
10 ShiftState(BuildBoxState.Doing);
11 }
12 }
通過建造中的時刻關系自動判斷是否完成:
1 IEnumerator ShowBuildTime()
3 var ct = GetCompleteTime();
4 if (CheckBuildCompleted(ct))
6 ShiftState(BuildBoxState.Complete);
7 yield break;
9 else
11 for (; ; )
12 {
13 View.SetTime(CalNeedTime(ct));
14 yield return new WaitForSeconds(1);
15 }
16 }
17 }
當建造完成點選收取資源時,切換為準備狀态的同時,自動清空拟定建造完成時刻的資料記錄:
1 private void OnClickGet()
2 {
3 Canvas.SendEvent(new GetItemEvent());
4 ClearCompleteTime();
5 ShiftState(BuildBoxState.Start);
6 }
這裡有一個問題是,為什麼不在建造完成時就清理資料呢,因為有一種情況是,建造完成後,玩家還沒來得及點選收取,就直接進入了離線狀态,如果此時再次登入時資料已經清空,那他将做了一場無用功。
說不定直接垃圾遊戲毀我青春敗我前程了,為了避免這種情況發生,我們隻有確定玩家真正收取到資源的那一刻才能清理資料。
到此,整個建造的基礎邏輯已經梳理完畢。如果要實作快速建造的話,也隻不過是将拟定的完成時間直接設定為此刻即可。如果之前記錄的是開始建造的時刻,此時又會進行更多額外計算。
接下來,關于時間的坑這裡也略提一二吧,一開始我以為記錄時刻隻需要記錄時分秒即可,因為最多的建造時長也不超過10小時一般,遊戲要保證玩家每天登陸,不可能動用海量的時間去建造一個資源。
如若如此,策劃很可能會馬上被抓出來祭天,并被玩家評論區冰冷的口水淹沒。
但後來寫着寫着就發現了一個問題,那就是好多天沒登入的玩家怎麼辦,隻記錄時分秒根本沒辦法判斷時間的早晚,後來想一會還是把日期也記錄下來吧。
1 public struct TimeData
3 public int DayOfYear;
4 public int Hour;
5 public int Minute;
6 public int Second;
7 }
要是你問,那一年以上沒登入怎麼辦,那隻能說,你建造的資源已經被時光的齒輪碾碎了(允悲...)。後來突然想起來如果是某一年的最後一天呢...emm,還是老實寫全吧:
2 {
3 public int Year;
4 public int DayOfYear;
5 public int Hour;
6 public int Minute;
7 public int Second;
8
9 public TimeData(int year,int dayOfYear,int hour,int minute,int second)
10 {
11 Year = year;
12 DayOfYear = dayOfYear;
13 Hour = hour;
14 Minute = minute;
15 Second = second;
16 }
17 }
完整時間資料管理腳本:
View Code
完整建造腳本:
這裡用到的UI基礎類可詳見之前寫過的随筆:
https://www.cnblogs.com/koshio0219/p/12808063.html單例模式:
https://www.cnblogs.com/koshio0219/p/11203631.html補充:
通用确認彈窗:
效果:
原文位址
https://www.cnblogs.com/koshio0219/p/12988884.html