我们在做大型3D游戏时,资源比较大的一部分就是场景,场景包含很多模型和贴图,还有Lightmap图片和数据,为了将一个场景打包成一个assetbundle以方便动态加载,可以把整个场景打成一个assetbundle,场景中的材质和贴图以及LightMap打到另一个assetbundle里面,这种方式最简单,但是打出来的场景包会很大,相当于场景中所有的模型都打了进去,如果其他场景没有用到这个模型还好,如果用到了的话,就是极大的浪费。


所以基于将包最小化,我们需要区分哪个是共用的,哪些是这个场景独有的。

共用( 其他场景也会用到 ) 的有:模型、贴图、材质。

此场景独有的:Lightmap贴图的数据,场景中的一些其他模型,还有碰撞,寻路等数据。


所以我们在打包场景之前,就需要把这些共用的剔除,剔除的方式有很多种,有人是把要剔除的模型写到一个配置文件中,里面包含了模型的坐标、缩放、旋转数据,还有lightmapIndex, lightmapScaleOffset数据。当然除了把这个场景中公有部分写到配置中,还可以写一个脚本 ,脚本上加一个public string sceneInfo ,然后把剔除的数据转成json,xml或其他格式存到sceneInfo中,这样还节省一个配置文件。当然你还可以用序列化方式(ScriptableObject)存这些数据,这样读取来更方便。

既然我们找到了公共部分,以及剔除的方式,那么下一步就是写一个编辑器脚本来做这个步骤,下面是我的步骤:

1、对上面的脚本加一个字段,例如叫public Transform[] dynamicObjects,是一个数组,如果当前场景中需要剔除的对象,都拖到这个数组中。

2、遍历上面数组中的对象,把它的坐标、旋转、缩放,layer等信息记下来,还要遍历它下面所有的Renderer的lightmapIndex,scaleOffset等数据,也记下来,最后形成一个数据存到sceneInfo中,这样sceneInfo就保存了要剔除的一些模型的数据。

3、删除共用对象,对剩下和这个场景打成一个assetbundle,叫场景包。

4、对这个场景独有的贴图、材质、lightmap等打成一个包,叫场景资源包

5、对共用对象进行打包,可以一个模型(包含模型、贴图、材质等)打一个包,叫场景资源共用包。

6、如果你sceneInfo中还记录了共用模型在编辑器的路径 ,那么打包此场景完成后,你还可以还原这些模型,方便查看是否能还原正确。


最后就是还原这个场景,那就简单多了,我们需要加载这个场景资源包和场景包,再加载这个场景中共用部分。

下面是生成配置的参考代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif

public class SceneInfo : MonoBehaviour {
	[System.Serializable]
	public class InfoVO{
		public string goName;//gameobject 的名字 
		public Vector3 pos;//位置
		public Vector3 sc;//缩放
		public Vector3 rot;//旋转
		public string prefabPath;//prefab在unity工程中的路径

		public string[] renderNames;
		public int[] lightmapIndex;
		public Vector4[] lightmapScaleOffset;
	}

	public Transform[] dynamicTrans;
	public bool hasLightmap = true;//当前场景是否有lightmap
	public InfoVO[] configs;

	// Use this for initialization
	void Start () {
		
	}


	#if UNITY_EDITOR

	[CustomEditor(typeof(SceneInfo))]
	public class SceneInfoEditor : Editor {

		private SceneInfo _sceneInfo;

		void OnEnable(){
			_sceneInfo = target as SceneInfo;
		}

		public override void OnInspectorGUI ()
		{
			serializedObject.Update();
			EditorGUILayout.PropertyField(serializedObject.FindProperty("dynamicTrans"), true);
			EditorGUILayout.PropertyField(serializedObject.FindProperty("hasLightmap"), true);
			EditorGUILayout.Space();
			EditorGUILayout.PropertyField(serializedObject.FindProperty("configs"), true);
			serializedObject.ApplyModifiedProperties();
			EditorGUILayout.Space();
			EditorGUILayout.BeginHorizontal();
			if(GUILayout.Button("生成配置")){
				CreateSceneConfig();
			}
			EditorGUILayout.EndHorizontal();
		}

		/// <summary>
		/// 生成配置
		/// </summary>
		void CreateSceneConfig(){
			if(_sceneInfo){
				_sceneInfo.configs = new InfoVO[_sceneInfo.dynamicTrans.Length];
				for(int i = 0; i<_sceneInfo.dynamicTrans.Length;++i){
					Transform rootTran = _sceneInfo.dynamicTrans[i];
					InfoVO infoVo = new InfoVO();
					infoVo.goName = rootTran.name;
					infoVo.pos = rootTran.localPosition;
					infoVo.sc = rootTran.localScale;
					infoVo.rot = rootTran.localEulerAngles;

					Object prefab = PrefabUtility.GetPrefabParent(rootTran);
					if(prefab){
						infoVo.prefabPath = AssetDatabase.GetAssetPath(prefab);
					}
					if(_sceneInfo.hasLightmap){
						Renderer[] re = rootTran.GetComponentsInChildren<Renderer>();
						if(re!=null){
							infoVo.renderNames = new string[re.Length];
							infoVo.lightmapIndex = new int[re.Length];
							infoVo.lightmapScaleOffset = new Vector4[re.Length];
							for(int j= 0 ;j<re.Length;++j){
								Renderer render = re[j];
								infoVo.renderNames[j] = render.name;
								infoVo.lightmapIndex[j] = render.lightmapIndex;
								infoVo.lightmapScaleOffset[j] = render.lightmapScaleOffset;
							}
							_sceneInfo.configs[i] = infoVo;
						}
					}
				}
				EditorUtility.SetDirty(_sceneInfo);
				UnityEditor.SceneManagement.EditorSceneManager.SaveOpenScenes();
			}
		}

	}

	#endif
}