我们在做大型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
}