狀態加載模塊

前言

        而達到項目優化的效果,在3維場景中漫遊的時候,同大多的的2維場景遊戲一樣,需要在不同的區域需要加載出不同的資源對象。然而2維場景遊戲和僞3D的類型的遊戲由於視角有限及資源相對較小,加載資源基本都是計算視角區域,進行計算並並加載相關的資源。在3維場景中大多數還是整體加載爲主以細節資源加載爲輔助,一是能在進入場景後就可眼觀全局,二是在進入一個小區域或房間後可以看到具體的資源。爲些本文將介紹一個用於快速加載一種狀態下的資源的模塊,這個模塊將不僅僅用於場景漫遊,而將用於所有使用狀態模式的3維程序。

一、功能介紹

如圖1.1所示,利用ScrptObject製作了一個可視的窗口,如按扭所示,提供了prefab和bundle兩種資源加載的方式。在配製過程中,可以直接將prefab資源拖動到其中,在不同的scriptObject之間也可以相互拖動。一但將資源放置其中,相關的信息會自動進行填充,除非需要重置其座標或旋轉到指定的位置。由於狀態有包含交叉等情況,所以這裏也會也狀態一和狀態二都需要共用狀態的情況,如果你傳入到狀態一那麼狀態一和共用狀態的資源都會加載出來。


(圖1.1)

二、基本狀態

由於狀態的相互引用,爲防止狀態一引用共用狀態,同時共用狀態又引用狀態一的問題,所以做了一定的處理。一當填充一個狀態下所需要的資源時,如果已經存在那麼就不會重複加載了,腳本源碼如下:

  private StateItem[] LoadBundleListGroupsItems(string stateName, List<string> loadedKeys = null)
        {
            if (loadedKeys == null)
            {
                loadedKeys = new List<string>() { stateName };
            }
            else if (!loadedKeys.Contains(stateName))
            {
                loadedKeys.Add(stateName);
            }
            else
            {
                return null;
            }

            List<StateItem> items = new List<StateItem>();
            var find = bundleList.FindAll(x => x.stateName == stateName);
            if (find != null)
            {
                var groups = find .ToArray();

                foreach (var bitem in groups)
                {
                    foreach (var sitem in bitem.itemList)
                    {
                        if (!string.IsNullOrEmpty(sitem.assetName) && !string.IsNullOrEmpty(sitem.assetBundleName))
                        {
                            items.Add(sitem);
                        }
                    }
                    ///subState
                    foreach (var item in bitem.subStateNames)
                    {
                        var subItems = LoadBundleListGroupsItems(item, loadedKeys);
                        if (subItems != null)
                        {
                            items.AddRange(subItems);
                        }
                    }
                }
            }
            return items.ToArray();
        }
    }
一但記錄了指定的資源,就不會重複記錄了。當需要一個狀態下加載同一個預製體到不同的位置,也不會有問題,因爲ID中包含了座標的信息的HashCode.但一次狀態加載如果加載了兩個完全相同的資源就會報錯,當然這也是爲了防止資源重複加載。

三、緩存功能

在狀態進行切換的時候,如果加載的比較慢,而且用戶需要來回切換,那麼最後能有暫時不銷燬指定對象的方式,則會自動進行隱藏操作。控制的腳本如下:

 /// <summary>
        /// 計算當前需要下載的資源
        /// </summary>
        /// <param name="state"></param>
        private void ResetLoadingState()
        {
            //停止正在下載的資源
            itemLoadCtrl.CansaleLoadAllLoadingObjs();
           
            needDownLand.Clear();
            var loadedKeys = new string[loadedDic.Count];
            loadedDic.Keys.CopyTo(loadedKeys, 0);

            ///刪除新狀態下不再需要的對象
            foreach (var item in loadedKeys)
            {
                var info = CurrentItems.Find(x => x.ID == item);
                if (info == null)
                {
                    if (loadedDic[item] != null)
                    {
                        if (catchStates.Contains(lastState))
                        {
                            loadedDic[item].gameObject.SetActive(false);
                            if (log) Debug.Log("隱藏1:" + item);
                        }
                        else
                        {
                            delyDestroyObjects.Add(loadedDic[item]);
                            loadedDic.Remove(item);
                            if (log) Debug.Log("銷燬1:" + item);
                        }
                    }
                }
                else
                {
                    loadedDic[item].gameObject.SetActive(true);
                    if (log) Debug.Log("保留:" + item);
                }
            }

            ///記錄需要加載的資源
            for (int i = 0; i < CurrentItems.Count; i++)
            {
                var info = CurrentItems[i];
                if (!loadedDic.ContainsKey(info.ID))
                {
                    needDownLand.Enqueue(info);
                }
            }
        }

    }
實現這種功能後的效果如圖3.1所示


四、資源類型

由於一些工程資源比較少,加載的時候也都是本機,所以沒有必要使得AssetBundle,但有些工程需要從AssetBundle資源加載,並實現動態更新,所以這個模塊也將支持兩種加載的方式,在使用的時候,如果沒有AssetBundleTools這個庫,就只能使用Prefab加載了,爲防止報錯可將宏定義AssetBundleTools刪除。

其中數據模型上,資源包加載和預製體加載都使用 以下這個類爲模板:

    public abstract class StateItem
    {
        protected string _id;
        public abstract string ID { get; }
        public bool reset;
        public Vector3 position;
        public Vector3 rotation;
    }
可以自行定義座標和放旋轉等信息,如果需要其他信息,可自行添加。不過要在對應的繪製腳本中修改相應的繪製方式。

五、引擎Bug

在開發以上這個模塊的時候,遇到了非常難過的一天。最終發現了一個非同尋常的Bug,目前在unity5.3和unity5.6上都會出現,在編輯器完美運行而在打包出來後,不論是pc端還是webgl端都不能正常運行。主要是序列化的問題。從發現問題到找到問題後,提煉bug如下

一個可序列化的類:

public class Main : MonoBehaviour {
    public Child item;
}
一個父級帶宏定義的類:

using UnityEngine;

public class Parent
{
#if UNITY_EDITOR
    public int a;
#endif
}
[System.Serializable]
public class Child:Parent
{
    public string c;
}


在空場景中將Main腳本掛在一個對象上,將a輸入任意的一個負數,然後打包運行,你將會發現和

sharedassets0.assets' is corrupted

The file 'resources.assets' is corrupted

類似的問題,反正就是資源加載失敗了

六、源碼說明

由於開發過程積累的小的非核心的模塊都放置在github上面了,興趣的同行可以參看源碼:

https://github.com/zouhunter/StateLoader


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章