思想:场景由一些渲染设置(雾、环境光、光晕等)、天空盒子材质球、地形、场景中的静态模型、烘焙的灯光贴图等构成的,在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();
}
二、解析和加载场景
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
}