2015-06-22 16:42:55 u011355822 阅读数 1789
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

热更新技术,是当前网络游戏的一项必要功能, 在此只讨论Assetbundle资源的更新, 不讨论代码的更新。

一、进行资源梳理切分,哪些资源是独立性较好的,依赖链清晰的,可以归纳到一个包的,把资源依赖的层级关键画好。

二、将资源分为两大类,一类为内置Resources资源,一类为Assetbundle资源

    内置Resources资源或者叫AssetsLibrary,由Unity自行管理,不能进行更新(或者说不能直接修改)。

    而Assetbundle资源为加部自定义加载项,可以进行更新。

三、Assetbundle资源打包

BuildPipeline.PushAssetDependencies();

var options = = BuildAssetBundleOptions.CollectDependencies
  | BuildAssetBundleOptions.UncompressedAssetBundle
  | BuildAssetBundleOptions.DeterministicAssetBundle;

BuildPipeline.BuildAssetBundle(.., "base.unity3d", options)

BuildPipeline.BuildAssetBundle(.., "level.unity3d", options)

BuildPipeline.PopAssetDependencies();

这样一来, level.unity3d 与 base.unity3d 中重复的资源, 将打在base.unity3d 中, level.unity3d 将不会重复打包此资源

四、加载Assetbundle资源

// 只需一句即可
AssetBundle.CreateFromFile(...)

完成



下面说一下需要注意的地方:

一、可以写一个交叉引用的扫描工具,扫描Assetbundle与Resources交叉引用的资源,这部分资源会被重复打包,浪费了空间,内存,效率。所以可以进行文件调整

二、注意打包选项 BuildAssetBundleOptions.UncompressedAssetBundle, 如果要使用Assetbundle.CreateFromFile,必须要使用这个选项,打包后的文件比较大,但是没办法,是不压缩的。

三、注意打包选项BuildAssetBundleOptions.DeterministicAssetBundle,这是Unity的一个坑爹选项,官方文档中有以下描述

... if you have a lot of objects in the asset bundle it will increase the potential for hash conflicts. Unity will give an error and not build the asset bundle in that case...

如果发生打包失败的情况(曾经碰到过,报hash collision错误),那么可能有以下几种解决方案(不是and的关系,是or的关系):

1、去掉这个选项,将所有的Assetbundle依赖包全部更新

2、手工改一下这个冲突资源的GUID试一下(修改方式很简单,去改meta文件)

3、分包,原来是2000个文件一个Assetbundle包,现在切为2个Assetbundle文件,一个包1000个文件

4、联系Unity公司解决


2018-04-09 12:07:21 bingheliefeng 阅读数 3738
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

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



没有更多推荐了,返回首页