场景数据驱动加载-侠岚

思想:场景由一些渲染设置(雾、环境光、光晕等)、天空盒子材质球、地形、场景中的静态模型、烘焙的灯光贴图等构成的,在Uinty使用编辑器将这些内容设置好,排列好,渲染好,随后编写一个方法将这些设置或者是物体序列化成一个json文件,资源细化成一个一个prefab并以材质为分类命名好。当加载场景的时候,就加在那么一个json文件,设置场景中的渲染设置,还有将那些静态的模型物件加载出来,同一个材质的放置到同一的父物体下(用于动态的静态批处理),设置好物体的位置、旋转和缩放等。加载lightmap,渲染灯光,从而完成一个场景的加载。使用数据驱动的形式,当需要改变场景的某一个物体时,就可以直接修改配置文件和资源就行了,可以达到场景的热更新和一些渲染氛围的改变,满足节日性的更新。


一、序列化编辑好的场景为json数据

[MenuItem ("Custom/Scene/Selected GameObjects And To Json")]
static void SaveAllGameObjectToJson()
{
    if (m_GameObjects == null)
    {
        m_GameObjects = new Dictionary<string, GameObjectResInfo>();
    }
            
    string path_out = "Assets/Resources/Terrains/";
    Directory.CreateDirectory( path_out );
    char[] delimiter = {'/', '.'};
    string[] words = EditorApplication.currentScene.Split(delimiter);
            
    string sceneName = words[words.Length-2];
    string Json_out = path_out + FormatString(sceneName) + "/";
    if ( !Directory.Exists(Json_out) )
        Directory.CreateDirectory(Json_out);

    string[] files = Directory.GetFiles(Json_out, "*.*");
    foreach (string fileName in files)
    {
        if ( !fileName.Contains("obstruct") )
            File.Delete( fileName );
    }

    string strJsonFileName = Json_out + FormatString(sceneName) + "_json.txt";
    FileStream fs = new FileStream (strJsonFileName,FileMode.Create);
    StreamWriter out_stream = new StreamWriter (fs);

    //序列化的数据有
    /**
        * RenderSettings.fog 是否开启雾效
        * RenderSettings.fogMode.ToString()  雾的模式(线性、指数、指数的平方)
        * RenderSettings.fogColor.rgba  雾的颜色
        * RenderSettings.fogStartDistance 线性模式雾的起始距离
        * RenderSettings.fogEndDistance  线性模式雾的结束距离
        * RenderSettings.fogDensity  雾的强度
        * 
        * RenderSettings.ambientLight.rgba 环境光的颜色
        * RenderSettings.haloStrength  光晕强度
        * RenderSettings.flareStrength  耀光的强度
        * RenderSettings.skybox  字符串保存 天空盒子材质
        */

    //天空盒子材质的序列化
    UnityEngine.Material skyMat = RenderSettings.skybox;
    if (skyMat != null)
    {
        string skyPath = AssetDatabase.GetAssetPath(skyMat);
        string skyMatPath = Json_out + FormatString(skyMat.name) + ".mat";
        AssetDatabase.CopyAsset( skyPath, skyMatPath );
        //skyMat.name 保存材质的名字 对应的去复制到的资源目录中查找
    }

    //序列化烘焙好的光照贴图
    foreach (LightmapData data in LightmapSettings.lightmaps)
    {
        if (data.lightmapFar != null)
        {
            string nameFar = FormatString(sceneName + "_" + data.lightmapFar.name) + "_tex";
            string lightBinPath = Json_out + nameFar;
            string lightResPath = AssetDatabase.GetAssetPath( data.lightmapFar );
            int idx = lightResPath.LastIndexOf( '.' );
            lightBinPath = lightBinPath + lightResPath.Substring( idx, lightResPath.Length-idx );
            AssetDatabase.CopyAsset( lightResPath, lightBinPath );
                    
            //data.lightmapFar.name 名字
            //nameFar 光照贴图资源名字
        }
        if (data.lightmapNear != null)
        {
            string nameNear = FormatString(sceneName + "_" + data.lightmapNear.name) + "_tex";
            string lightBinPath = Json_out + nameNear;
            string lightResPath = AssetDatabase.GetAssetPath( data.lightmapNear );
            int idx = lightResPath.LastIndexOf( '.' );
            lightBinPath = lightBinPath + lightResPath.Substring( idx, lightResPath.Length-idx );
            AssetDatabase.CopyAsset( lightResPath, lightBinPath );

            //data.lightmapNear.name 名字
            //nameNear 光照贴图资源名字
        }
    }

    //序列化场景中被选中的物体
    UnityEngine.Object[] objects = Selection.GetFiltered(typeof(GameObject), SelectionMode.DeepAssets);//获取场景中选中的物体们
    int wuJianIndex = 0;
    foreach (GameObject obj in objects)
    {
        //如果物体有材质,会以材质的名字为前缀命名(combineDC),后面加载时分组,方便静态绑定
        string strName = combineDC + obj.name + ResIndex + ".prefab";
        string prefabFile = prefabsPath + strName;
        bool isExistPrefab = false;

        if (!isExistPrefab)
        {
            //创建prefab
            UnityEngine.Object objprefab = PrefabUtility.CreateEmptyPrefab(prefabFile);
            PrefabUtility.ReplacePrefab(obj, objprefab);
        }
        //根据obj.name的命名 确定它是什么类型的物体
        //序列化的信息有
        /**
            * 物体的类型
            * 物体的名字 材质为组的命名
            * 物体预设名字
            * 物体的位置
            * 物体的旋转
            * 物体的缩放
            * 如果物体是特殊类型,保存一些组件信息,例如灯光或则相机
            * */
    }
            
    out_stream.Flush();
    fs.Close();            
}
C#


二、解析和加载场景

virtual public void Initial_local()
{
    ParseJson_local(); //解析json数据保存到对象中
        
    _LoadAssets_local(); //以材质为组加载物体 设置渲染设置

    SetSceneLightMap(m_mapTable.extralightmap);//设置光照贴图
        
    CorResMgr.Get().UnloadUnusedAssets();
}

protected virtual void _LoadAssets_local()
{
    Camera mainCam = CameraMagr.Get().GetMainCamera().GetCamera();
        
    SceneAsset asset;
    for(int i=0; i<m_AssetList.Count; i++)
    {
        asset = m_AssetList[i];
        if(asset == null)
            continue;
        switch(asset.Type)
        {
        case GameObjectType.Camera_Type:
        {
            if(asset.isLoadFinished)
                break;
            mainCam.transform.position = asset.Position;
            mainCam.transform.rotation = asset.Rotation;
            CameraMagr.Get().GetMainCamera().static_forward = mainCam.transform.forward;
            asset.isLoadFinished = true;
            break;
        }
        case GameObjectType.Light_Type:
        case GameObjectType.Water_Type:
        default:
            if(asset.isLoadFinished)
                break;
            _LoadAsset_local(asset);
            break;
        }
    }

    foreach(GameObject g in parentDic.Values)
    {
        if(g !=null && !g.name.Contains("eft") )
        {
            g.AddComponent<StaticBatch>();//静态批处理
                
        }
    }
}

protected void _LoadAsset_local(SceneAsset asset)
{
    try
    {
        UnityEngine.Object obj = CorResMgr.Get().LoadResource( asset.SourcePath );
            
        if(obj == null)
            return;
            
        //实例化 指定位置和旋转
        asset.instObject = (GameObject)UnityEngine.Object.Instantiate( obj, asset.Position, asset.Rotation );
            
        if(asset.instObject == null)
        {
            Debug.Log("Init map asset is error! path="+asset.SourcePath);
            return;
        }
            
        if(mapRoot == null)
        {
            mapRoot = new GameObject(Prefab);
            mapRoot.transform.localPosition = Vector3.zero;
            mapRoot.transform.localScale    = Vector3.one;
        }

        //根据不同的类型做不同的处理
        switch(asset.Type)
        {
        case GameObjectType.Actor_Type:
        case GameObjectType.Monster_Type:
        case GameObjectType.Generic_Type:
        {
            asset.instObject.transform.parent = mapRoot.transform;
            asset.instObject.transform.localScale = asset.Scale;
            break;
        }
        case GameObjectType.Building_Type:
        {
            string[] middleStr = asset.Name.Split('-');
            asset.instObject.transform.localScale = asset.Scale;
            if(parentDic.ContainsKey(middleStr[0]))
            {
                if(parentDic[middleStr[0]] ==null)
                {
                    GameObject go =new GameObject();
                    parentDic[middleStr[0]] = go;
                    parentDic[middleStr[0]].transform.localPosition = Vector3.zero;
                    parentDic[middleStr[0]].transform.localScale    = Vector3.one;
                    parentDic[middleStr[0]].transform.parent = mapRoot.transform;
                    parentDic[middleStr[0]].name =middleStr[0]+"Parent";
                    asset.instObject.transform.parent = parentDic[middleStr[0]].transform;
                }
                else
                {
                    asset.instObject.transform.parent = parentDic[middleStr[0]].transform;
                }
            }
            else
            {
                asset.instObject.transform.parent = mapRoot.transform;
            }
            break;
        }
            
        case GameObjectType.Light_Type:
        {
            asset.instObject.transform.parent = mapRoot.transform;
                
            Light light         = (Light)asset.instObject.GetComponent("Light");
            light.color         = asset.LightParam.Color;
            light.intensity     = asset.LightParam.Intensity;                        
            light.shadows         = (LightShadows)asset.LightParam.Shadow;
            light.shadowStrength= asset.LightParam.Strength;
            light.cullingMask = CONST_VALUE.SHADOWLAYER;
            light.tag = "dirLight";
            dirLightParam.intensity = asset.LightParam.Intensity;    
            dirLightParam.lightColor = asset.LightParam.Color;
            dirLightParam.rotation = asset.Rotation;
            WeatherSystem.Get().SetLightDir(light.transform.forward);
            break;
        }
        default:
        {
            asset.instObject.transform.parent = mapRoot.transform;
            break;
        }
        }
        asset.isLoadFinished = true;
        _InitAsset_Shader(asset);
    }
    catch(System.Exception exp)
    {
        Debug.LogWarning("_LoadAsset_local Error! path="+asset.SourcePath+" exp="+exp.Message);
        asset.isLoadFinished = false;
    }
}

public void SetSceneLightMap (string name = null)
{
    //场景的光照贴图
    if(m_LightmapList == null)
        return;
    List<LightmapData>    listLight = new List<LightmapData>();
        
    for(int i=0; i<m_LightmapList.Count; i++)
    {
        LightmapSet lightMap = m_LightmapList[i];
            
        LightmapData lightData = null;
        //far 
        if(!lightMap.m_bFarLoaded)
        {
            if(lightMap.m_NameFar == null || lightMap.m_NameFar == string.Empty)
            {
                lightMap.m_bFarLoaded = true;
            }
            else
            {
                if (!string.IsNullOrEmpty(name)) 
                {
                    lightMap.m_SourcePathFar = "Terrains/ExtraLightmap/" + name.Split('|')[i];
                }
                Texture2D texAsset = (Texture2D) CorResMgr.Get().LoadResource(lightMap.m_SourcePathFar);
                    
                if(texAsset != null)
                {
                    if(lightData == null)
                        lightData = new LightmapData();
                        
                    lightData.lightmapFar = texAsset;
                    lightMap.m_bFarLoaded = true;
                }
            }
        }
            
        //near
        if(!lightMap.m_bNearLoaded)
        {
            if(lightMap.m_NameNear == null || lightMap.m_NameNear == string.Empty)
            {
                lightMap.m_bNearLoaded = true;
            }
            else
            {
                Texture2D texAsset = (Texture2D) CorResMgr.Get().LoadResource(lightMap.m_SourcePathNear);
                    
                if(texAsset != null)
                {
                    if(lightData == null)
                        lightData = new LightmapData();
                        
                    lightData.lightmapNear = texAsset;
                    lightMap.m_bNearLoaded = true;
                }
            }
        }
        //fill
        if(lightData == null)
            return;
        listLight.Add(lightData);
    }
        
    //fill light map
    LightmapSettings.lightmaps = listLight.ToArray();
#if UNITY_5
    LightmapSettings.lightmapsMode = LightmapsMode.NonDirectional;
#endif
}
C#

首页 我的博客
粤ICP备17103704号