文/邪讓多杰

需求環(huán)境

在上一級的【解決方案】文章中,我們設(shè)計出了動態(tài)加載資源的業(yè)務(wù)流程,而這一節(jié),我們就通過一些簡單的代碼,來實(shí)現(xiàn)出業(yè)務(wù)流程中的效果。

吸取之前文章的經(jīng)驗,如果按照正式項目的規(guī)格開發(fā),本篇文章就會非常冗余,所以我們優(yōu)化一下,僅僅針對技術(shù)點(diǎn)進(jìn)行講解與釋放,具體與工程相關(guān)的,我們就不再文章中講解,但你可以在Github的工程中找到它們。、

現(xiàn)在,我們先回顧一下之前所設(shè)計出的業(yè)務(wù)流程。

162711ng6h96irtttrus6g.png
那么,在這個業(yè)務(wù)流程中,我可以定義出在游戲運(yùn)行時,資源有三種狀態(tài):

1、未加載

2、已經(jīng)加載

3、已可以釋放

三種狀態(tài)了某個資源此時的最佳使用環(huán)境,也就是說,接下來需要使用的資源,我就放到池中,而接下來很長一段時間內(nèi)不需要使用的資源,我就徹底釋放掉。以確保程序的內(nèi)存總是在可控范圍之內(nèi)。

設(shè)計

為了達(dá)到這樣的目的,我們就需要劃分三個模塊去做。

1、最基礎(chǔ)的資源加載,與池。

2、資源加載的自動記錄過程。

3、資源加載的動態(tài)釋放與加載過程。



首先,池,因為我們是模擬,所以這個就比較容易實(shí)現(xiàn),在現(xiàn)實(shí)工程中,則可能需要考慮不同資源類型的具體邏輯。

///

/// 池

///

Dictionary Stack> PoolDict = new Dictionary();

///

/// 正在工作的資源對象

///

Dictionary int> WorkingPool = new Dictionary();

首先是2個定義,一個是回收池,一個是工作區(qū),工作區(qū)用來反向查資源的ID,同時,也檢測是否有資源是通過其他方法加載的,理論上,游戲內(nèi)不應(yīng)該存在其他的途徑來加載資源。

接下來,就是2份邏輯代碼,一個是創(chuàng)建資源,它用到了之前我們實(shí)現(xiàn)的資源管理器,另一個是回收資源。
?

  1. ///
  2. ?
  3. ?
  4. /// 池
  5. ///
  6. Dictionary Stack> PoolDict = new Dictionary();
  7. ?
  8. ///
  9. /// 正在工作的資源對象
  10. ///
  11. Dictionary int> WorkingPool = new Dictionary();
  12. 首先是2個定義,一個是回收池,一個是工作區(qū),工作區(qū)用來反向查資源的ID,同時,也檢測是否有資源是通過其他方法加載的,理論上,游戲內(nèi)不應(yīng)該存在其他的途徑來加載資源。
  13. 接下來,就是2份邏輯代碼,一個是創(chuàng)建資源,它用到了之前我們實(shí)現(xiàn)的資源管理器,另一個是回收資源。
  14. ///
  15. ?
  16. ?
  17. /// 得到資源,如果池子里有,直接拿,否則創(chuàng)建
  18. ///
  19. ///資源類型,方便上級使用
  20. ///資源id
  21. ///
  22. public T getObj(int _id)
  23. where T : Object
  24. {
  25. Object temp = null;
  26. //池子里有就取一個
  27. if (PoolDict.ContainsKey(_id)
  28. PoolDict[_id].Count > 0)
  29. ?
  30. temp = PoolDict[_id].Pop();
  31. ?
  32. //如果池子里沒有,就創(chuàng)建一個新的
  33. temp = DJAssetsManager.GetInstance().Load(_id);
  34. ?
  35. if (temp as T == null)
  36. {
  37. Debug.LogError(代碼寫錯了或資源配錯了,傳入的資源id與希望得到的類型不匹配);
  38. Debug.Break();
  39. return null;
  40. }
  41. ?
  42. //加入工作池
  43. WorkingPool.Add(temp,_id);
  44. ?
  45. return (T)temp;
  46. }
  47. ?
  48. ///
  49. /// 回收資源
  50. ///
  51. public void recObj(Object _obj)
  52. {
  53. if (WorkingPool.ContainsKey(_obj))
  54. {
  55. //正?;厥?br />
  56. int id = WorkingPool[_obj];
  57. WorkingPool.Remove(_obj);
  58. ?
  59. if (PoolDict.ContainsKey(id) == false)
  60. PoolDict.Add(id, new Stack());
  61. ?
  62. PoolDict[id].Push(_obj);
  63. }
  64. else
  65. {
  66. //不屬于池管理的資源直接刪除掉。不過得打出警告,按理說不應(yīng)該存在
  67. Debug.LogWarning(檢測到非法創(chuàng)建的資源: + _obj.name);
  68. Destroy(_obj);
  69. }
  70. }


PS: 在文章代碼中并沒有對預(yù)制體進(jìn)行管理,這其實(shí)是不好的,最好手動的控制他們的加載與釋放。

資源生命周期的自動記錄

要記錄資源的生命周期,首先我們得確定自己的游戲形勢,如果是大世界類型的游戲,我們需要根據(jù)區(qū)域范圍來確定資源表,那么如果是副本類型的,我們就需要以副本為單位記錄一份資源表。

并且,有的資源我們希望是動態(tài)加載的,而有的資源,比如主角的特效,模型,音頻等等,我們更希望它們是常駐的。所以,我們還需要區(qū)分一份資源是否需要動態(tài)加載。



知道了需求后,我們就可以對自動記錄表進(jìn)行設(shè)計。為了講解清晰,我盡量的保持任何一個元素都只是為了測試,不與業(yè)務(wù)邏輯掛鉤。

在工程中,你可以到之前我們創(chuàng)建過的DJAssetsDefine 命名空間,里面我們新添加了這一次需要使用到的記錄表。

?

?

  1. [System.Serializable]
    • public class AssetPreConfig
      • {
        • ///
          • ?
          • ?
          • /// 資源ID
            • ///
              • public int AssetId;
                • ?
                • ///
                  • /// 加載時間
                    • ///
                      • public float LordTime;
                        • ?
                        • ?
                        • ///
                          • /// 下一個次同類資源的加載時間,-1 就是再也沒有加載過了
                            • ///
                              • public float NextTime = -1;
                                • }

代碼如下:

?

?

  1. ///
    • ?
    • ?
    • /// 得到一個克隆體
      • ///
        • ///資源id
          • /// 是否預(yù)加載
            • ///
              • private Object getClone(int _id, bool _isPre = false)
                • {
                  • //預(yù)加載直接返回新的
                    • if (_isPre) return Object.Instantiate(PoolDict[_id].pre); ;
                      • ?
                      • //池里有從池里拿
                        • if (PoolDict[_id].Pools.Count > 0)
                          • {
                            • currentIndex+= 1;
                              • return PoolDict[_id].Pools.Pop();
                                • }
                                  • ?
                                  • //記錄下這次加載
                                    • AutoLog(_id);
                                      • ?
                                      • //返回一個新的
                                        • return Object.Instantiate(PoolDict[_id].pre);
                                          • }

第一次

此時記錄表內(nèi)的內(nèi)容

162714lwpjg1e4mfjbw1ep.png
第二次

162715p46dt08ffddc88fc.png
可以看到,前兩次的資源都有預(yù)加載,所以時間上間斷了。而第三次資源,卻比第一次還要多,因為中間發(fā)生了資源刪除事件。

第三次

162716onisr453unzm6m8i.png
這一次,沒有任何資源是在使用時才被加載的,前2份資源也不會“輕易”的放棄了自己生命,而是等待這第3份的調(diào)用。徹底完成了優(yōu)化的過程。

結(jié)束語

如果和業(yè)務(wù)邏輯相結(jié)合,我們所演示的功能是不夠的,但卻構(gòu)建了整個自動化的資源加載與釋放的核心框架,使得我們在項目后續(xù)的開發(fā)過程中,盡可能的不會在IO方面遇到困難。

同時,如果我們能繼續(xù)對這部分的工作進(jìn)行優(yōu)化,還能制作出更平緩的游戲資源IO流程,提供更好的游戲性能。

銳亞教育

銳亞教育,游戲開發(fā)論壇|游戲制作人|游戲策劃|游戲開發(fā)|獨(dú)立游戲|游戲產(chǎn)業(yè)|游戲研發(fā)|游戲運(yùn)營| unity|unity3d|unity3d官網(wǎng)|unity3d 教程|金融帝國3|8k8k8k|mcafee8.5i|游戲蠻牛|蠻牛 unity|蠻牛