AssetBundle系列——場景資源之解包(二)


本篇接着上一篇繼續和大家分享場景資源這一主題,主要包括兩個方面:

(1)加載場景

場景異步加載的代碼比較簡單,如下所示:

    private IEnumerator LoadLevelCoroutine()
    {
        string url = "ftp://127.0.0.1/TestScene.unity3d";
        int verNum = 1;

        WWW wwwForScene = WWW.LoadFromCacheOrDownload(url, verNum);
        while (wwwForScene.isDone == false)
            yield return null;

        AssetBundle bundle = wwwForScene.assetBundle;
        yield return Application.LoadLevelAsync("TestScene");
        wwwForScene.assetBundle.Unload(false);
    }

(2)加載場景物件

主要包含以下細分步驟:

a、下載並解析場景配表,得到場景物件信息。場景物件的數據結構如下所示:

public class XmlSceneGameobjectProp
{
    // Mesh信息
    public class MeshInfo
    {
        public string name;
        public string shader;

        public bool hasColor = false;
        public Vector4 color;

        public bool isStatic = true;
        public int lightmapIndex;
        public Vector4 lightmapTilingOffset;
    }

    public string name;
    public string group;
    // Transform信息
    public float posX, posY, posZ;
    public float rotX, rotY, rotZ;
    public float scaleX, scaleY, scaleZ;
    // Mesh列表,一個模型可以包含多個MeshRenderer
    public List<MeshInfo> LstMesh = new List<MeshInfo>();
}

xml解析的主體代碼如下所示:

 private void ParseChildNode(XmlElement xmlGroup, XmlElement xmlChild)
    {
        SceneGameobjectProp newChild = new SceneGameobjectProp();
        newChild.group = xmlGroup.GetAttribute("name");
        newChild.name = xmlChild.GetAttribute("name");
        // 注冊資源名字
        if (lstRes.Contains(newChild.name) == false)
        {
            lstRes.Add(newChild.name);
        }

        // Tranform節點
        XmlNode xmlTransform = xmlChild.SelectSingleNode("Transform");
        // MeshRenderer節點
        XmlNode xmlMeshRenderer = xmlChild.SelectSingleNode("MeshRenderer");

        if (xmlTransform != null && xmlTransform is XmlElement)
        {
            CXmlRead goReader = new CXmlRead(xmlTransform as XmlElement);
            newChild.posX = goReader.Float("posX", 0f);
            newChild.posY = goReader.Float("posY", 0f);
            newChild.posZ = goReader.Float("posZ", 0f);
            newChild.rotX = goReader.Float("rotX", 0f);
            newChild.rotY = goReader.Float("rotY", 0f);
            newChild.rotZ = goReader.Float("rotZ", 0f);
            newChild.scaleX = goReader.Float("scaleX", 1f);
            newChild.scaleY = goReader.Float("scaleY", 1f);
            newChild.scaleZ = goReader.Float("scaleZ", 1f);
        }

        if (xmlMeshRenderer != null && xmlMeshRenderer is XmlElement)
        {
            foreach (XmlNode node in xmlMeshRenderer.ChildNodes)
            {
                if ((node is XmlElement) == false)
                    continue;

                SceneGameobjectProp.MeshInfo mesh = new SceneGameobjectProp.MeshInfo();
                mesh.name = (node as XmlElement).GetAttribute("Mesh");
                mesh.shader = (node as XmlElement).GetAttribute("Shader");
                XmlNode xmlLightmap = node.SelectSingleNode("Lightmap");
                if (xmlLightmap != null && xmlLightmap is XmlElement)
                {
                    CXmlRead reader = new CXmlRead(xmlLightmap as XmlElement);
                    mesh.isStatic = reader.Bool("IsStatic", true);
                    mesh.lightmapIndex = reader.Int("LightmapIndex", -1);
                    mesh.lightmapTilingOffset = new Vector4(reader.Float("OffsetX", 0f), reader.Float("OffsetY", 0f), reader.Float("OffsetZ", 0f), reader.Float("OffsetW", 0f));
                }
                XmlNode xmlColor = node.SelectSingleNode("Color");
                if (xmlColor != null && xmlColor is XmlElement)
                {
                    CXmlRead reader = new CXmlRead(xmlColor as XmlElement);
                    mesh.hasColor = reader.Bool("hasColor", false);
                    mesh.color = new Vector4(reader.Float("r", 0f), reader.Float("g", 0f), reader.Float("b", 0f), reader.Float("a", 0f));
                }
                newChild.LstMesh.Add(mesh);
            }
        }

        lstGameObjectProp.Add(newChild);
    }

b、加載場景物件asset

同時開啟多個Coroutine進行WWW的LoadFromCacheOrDownload操作,經測試開啟的WWW線程越多,速度會越快,但是需要考慮實際的機器或平台的承載能力。

注意,我這兒的WWW操作是直接從緩存里面載入內存,而不是從網上下載。所有更新的游戲物件,可以在游戲開始的時候一次從網上Download到Cache,這樣,在游戲過程中就不需要從網上Download資源了,wifi下載3G玩,爽歪歪~

如果一定要在此處從網上Download資源的話,線程數最好設為5個,很多平台有自己的限制,比如有的網頁瀏覽器只能同時開6個等等......

    // 同時開啟的Coroutine的數目
    private const int ThreadNum = 100;
    // 記錄每個加載線程的進度,只有每個線程都加在結束了,場景加載才算完成
    private int[] arrThreadProggress = new int[ThreadNum];

    // 加載完成后的回掉
    public delegate void LoadFinishDelegate();
    public LoadFinishDelegate OnLoadFinish = null;
  
    // 需要下載的資源列表
    private List<string> lstRes = new List<string>();
    // 是否加載完畢的標記
    private bool hasFinished = false;

    private void LoadAsset()
    {for (int i = 0; i < ThreadNum; ++i)
        {
            CoroutineProvider.Instance().StartCoroutine(LoadAssetCoroutine(i));
        }
    }

    private IEnumerator LoadAssetCoroutine(int threadIndex)
    {
        while (arrThreadProggress[threadIndex] < lstRes.Count)
        {
            // 載入資源
            string name = lstRes[arrThreadProggress[threadIndex]];
            GameApp.GetResourceManager().LoadAsync(GlobalSetting.SceneAssetPath + name, typeof(GameObject)); while (GameApp.GetResourceManager().IsResLoaded(GlobalSetting.SceneAssetPath + name) == false)
            {
                yield return null;
            }
            arrThreadProggress[threadIndex] += ThreadNum;
        }
        // 線程資源下載完畢,進行加載回掉
        if (IsLoadFinished() && hasFinished == false)
        {
            hasFinished = true;if (OnLoadFinish != null)
            {
                OnLoadFinish();
            }
        }
    }

上面的黑體標出的代碼是是實際的加載代碼,具體實現已經在帖子“AssetBundle系列——資源的加載、簡易的資源管理器”中講解過了,此處不再贅述。

c、實例化場景物件

  // 實例化
    GameObject goIns = GameObject.Instantiate(asset) as GameObject;
    goIns.name = goProp.name;

    // 設置父節點
    GameObject goGroup = null;
    dicGroupGameobject.TryGetValue(goProp.group, out goGroup);
    if (goGroup != null)
        goIns.transform.parent = goGroup.transform;
    else
        goIns.transform.parent = goRoot.transform;

    // 設置Transform
    goIns.transform.position = new Vector3(goProp.posX, goProp.posY, goProp.posZ);
    goIns.transform.eulerAngles = new Vector3(goProp.rotX, goProp.rotY, goProp.rotZ);
    goIns.transform.localScale = new Vector3(goProp.scaleX, goProp.scaleY, goProp.scaleZ);

    // 設置Shader、Lightmap
    int index = 0;
    int meshCount = goProp.LstMesh.Count;
    foreach (MeshRenderer mr in goIns.gameObject.GetComponentsInChildren<MeshRenderer>(true))
    {
        if (mr.sharedMaterial != null)
        {
            if (index < meshCount)
            {
                SceneGameobjectProp.MeshInfo meshProp = goProp.LstMesh[index];
                mr.sharedMaterial.shader = Shader.Find(meshProp.shader);
                if (meshProp.hasColor)
                    mr.sharedMaterial.color = meshProp.color;
                bool isStatic = meshProp.isStatic;
                mr.gameObject.isStatic = isStatic;
                if (isStatic)
                {
                    mr.lightmapIndex = meshProp.lightmapIndex;
                    mr.lightmapTilingOffset = meshProp.lightmapTilingOffset;
                }
            }
            index++;
        }
    }

   本帖主要是關於assetbundle的處理方法,關於物體材質的實例化邏輯,有很多種做法,我這只是提供了其中一種做法。其中有一點需要注意的,就是material和sharedMaterial的區別,上面實例化中的代碼,我用的是sharedMaterial來設置,使用material是有問題的,因為每一次對material的賦值會導致生成一個materil的instance產生。

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM