参考资料
前言
1.Unity打包发布策略
按需打包:打包发布程序后,unity会自动将场景需要引用到的资源打包到安装包里,没有用到的不会跟进去。
(我们在编辑器里看到的Asset中的文件结构只是工作于编辑器环境下的,在游戏中unity会重新组织文件结构)
2.动态加载资源
如此打包就会有问题:场景中一开始没有用到的资源就不会被打包,但在游戏中我们却要动态加载它(如换装系统)
3.GUID与fileID
Assets目录中的每个资源都有与之对应的meta文件,文件中记录了GUID,相当于身份证
FileID(LocalID),表示的就是资源内部的依赖关系
资源加载方式
1.面板拖拽
(最简单,但通常不用)
实现方式:
定义public属性的变量,在面板上拖拽赋值,在Update中实例化
2.AssetDatabase
(只能在编辑器中使用,打包后就不能用了)
Assets文件夹
在编辑器中,Unity用到的所有资源都放在Assets
文件夹下
Assets下的资源除特殊文件夹,或者在会打入包内的场景中引用的资源,其余资源不会被打入包中。
实现方式:
需要完整路径及后缀名
AssetDatabase.LoadAssetAtPath("Assets/x.txt");
GameObject DBobj = Instantiate(
UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>("Assets/GameData/Prefabs/cube.prefab"));
3.Resources
(保留文件夹,全部打包,最大2G,不建议使用)
实现方式:
不加后缀名,路径是否写全都可以,倘若Assets文件夹下包含多个Resources文件夹,运行时会被当成一个
1.获取第一个匹配条件的对象
GameObject loadObj = Instantiate(Resources.Load("Prefabs/cube"))as GameObject;
2.获取所有符合条件的对象
Object[] assets = Resources.LoadAll("fileName");
TextAsset[] assets = Resources.LoadAll("fileName");
3.卸载资源
Resources.UnloadAsset(Object);//卸载对应Object
Resources.UnloadUnusedAssets();//卸载所有没有被引用以及实例化的Object
4.AssetBundle
(就是一个资源压缩包,方便分包发布和版本升级,推荐使用)
打包方式:
- 1.指定资源为AssetBundle属性:工程窗口选中资源,检视面板右下角分别设置 文件名 和 后缀
- 2.编译器扩展,实现一键打包AB包
using UnityEngine;
using UnityEditor;
using System.IO;
public class CreateAssetBundles : MonoBehaviour
{
[MenuItem("AssetBundles/Build AssetBundles")] //菜单栏显示
static void BuildAssetBundle()
{
string dir = "AssetBundles"; //相对路径
if(!Directory.Exists(dir)) //判断是否存在
{
Directory.CreateDirectory(dir);
}
BuildPipeline.BuildAssetBundles(dir,
BuildAssetBundleOptions.None,
BuildTarget.StandaloneWindows64);
}
}
方法参数详解(BuildPipline.BuildAssetBundle)
(a). Build的路径(只要是在硬盘上都可以的)
(b). BuildAssetBundleOptions(枚举类型)
a)、BuildAssetBundleOptions.None:使用LZMA算法压缩,压缩的包更小,但加载时间更长,
使用之前需要整体解压。一旦被解压,这个包会使用LZ4重新压缩。使用资源的时候不需要整体解压。
在下载的时候可以使用LZMA算法。一旦它被下载了之后,它会使用LZ4算法保存到本地上。
b)、BuildAssetBundleOption.UncompressedAssetBundle (不压缩,包大,加载速度快)。
c)、BuildAssetBundleOption.ChunkBasedCompression:(使用LZ4算法,压缩率没有LZMA高,但我们可
以加载指定资源的不用解压全部);
注意:使用LZ4算法,可以获得可以跟不压缩相媲美的加载速度,而且比不压缩的文件要小。
(c). BuildTarget:(选择Build出来的AB包需要的平台)
加载AB包的方式:
- 从文件中加载 AssetBundle.LoadFromFile
1.加载AB包,注意资源存在依赖关系的话要先加载依赖的资源
AssetBundle abShare = AssetBundle.LoadFromFile("AssetBundles/materials/mapmaterials");
AssetBundle ab = AssetBundle.LoadFromFile("AssetBundles/scene/map_001");
2.加载AB包内资源
GameObject panelPrefab = ab.LoadAsset<GameObject>("Plane");
3.实例化
Instantiate(panelPrefab);
- 从内存中加载 AssetBundle.LoadFromMemoryAsync
string path = "AssetBundles/scene/map_01";
1.异步读取LoadFromMemoryAsync
AssetBundleCreateRequest request = AssetBundle.LoadFromMemoryAsync(File.ReadAllBytes(path));
yield return request; //异步的方式必须等待完成后才继续执行
AssetBundle ab = request.assetBundle;
2.同步读取LoadFromMemory
//AssetBundle ab = AssetBundle.LoadFromMemory(File.ReadAllBytes(path));
3.实例化
GameObject gameObj = ab.LoadAsset<GameObject>("map_001");
Instantiate(gameObj);
- WWW加载(可以本地,也可以服务器)
1.检查cache 缓存是否准备好了
//www.LoadFromCacheOrDownload 这个方法是先下载存到cache的缓存空间内 然后在进行提取。
//如果第二次也进行下载一样的 则会在这个缓存空间内提取之前已经下载的数据
//开始下载前必须先判断是否准备好了,否则一直循环下去,等待准备好
while (!Caching.ready)
yield return null;
2.本地下载
//string allPath = @"E:/03_TestDemo/01_UnityDemo/ShowMap_ABPackageTest_001/AssetBundles/scene/map_01";
//WWW www = WWW.LoadFromCacheOrDownload(allPath, 1);
3.远程服务器下载
WWW www = WWW.LoadFromCacheOrDownload(@"http://localhost/AssetBundles/scene/map_01", 1); //从远程服务器上下载AB包
yield return www;
4.因万维网是非常复杂的,经常容易出现非常意外的错误,所以必须加入一个判断
if(!string.IsNullOrEmpty(www.error)) //当什么都没有时
{
Debug.Log(www.error);
yield break; //跳出协程
}
5.取到AB包
AssetBundle ab = www.assetBundle;
6.实例化
GameObject gameObj = ab.LoadAsset<GameObject>("map_001");
Instantiate(gameObj);
- UnityWebRequest
1.本地或服务器路径
//string uri = @"E:/03_TestDemo/01_UnityDemo/ShowMap_ABPackageTest_001/AssetBundles/scene/map_001";
string uri = @"http://localhost/AssetBundles/scene/map_001";
2.获取request
nityWebRequest request = UnityWebRequestAssetBundle.GetAssetBundle(uri);
yield return request.SendWebRequest(); //注意是异步方式所以必须等待他完成后再执行
3.两种获取ab对象的方式
//AssetBundle ab = DownloadHandlerAssetBundle.GetContent(request); //第一种方法
AssetBundle ab = (request.downloadHandler as DownloadHandlerAssetBundle).assetBundle; //第二种方法
4.实例化
GameObject gameObj = ab.LoadAsset<GameObject>("map_001");
Instantiate(gameObj);
各加载方式的区别
- 前两种是从本地加载的,后两种既可从本地加载也可从服务器加载
- 第一种和第二种的区别体现在加载不同压缩类型的AB包(从内存加载:加载LZMA时就把它解压,加载LZ4时还是压缩状态;从文件加载:效率高,直接把未压缩的和LZ4压缩的AB包加载,对LZMA先把其解压再放入内存中)
- 第三种弃用,建议用第四种
加载AB包中资源的方式:
- LoadAsset<>()
- LoadAllAsset()
- LoadAssetAsync<>()
- LoadAllAssetAsync()
卸载方式:
- AssetBundle.Unload(true)
卸载所有资源,即使有资源使用着(1,在关卡切换或场景切换时 2、资源没被用的时候调用);
- AssetBundle.Unload(false)
卸载所有没有被使用的资源。(这个适用时间比较多 ,可自行把控)。
- 个别资源怎么卸载,通过Resources.UnloadUnusedAssets();
打包策略:
-
逻辑实体分组(Logical Entity Grouping)
- 一个UI界面或者所有UI界面一个包(这个界面里面的贴图和布局信息一个包)
- 一个角色或者所有角色一个包(这个角色里面的模型和动画一个包)
- 所有的场景所共享的部分一个包(包括贴图和模型)
-
类型分组(Type Grouping)
所有声音资源打成一个包,所有shader打成一个包,所有模型打成一个包,所有材质打成一个包
-
并发内容分组(Concurrent Content Grouping)
把在某一时间内使用的所有资源打成一个包。可以按照关卡分,一个关卡所需要的所有资源包括角色、贴图、声音等打成一个包。也可以按照场景分,一个场景所需要的资源一个包
-
注意
- 经常更新的资源放在一个单独的包里面,跟不经常更新的包分离
- 把需要同时加载的资源放在一个包里面,例如模型,其纹理和动画
- 把其他包共享的资源(公共资源)放在一个单独的包里面
- shader字体等其他细碎并且需要常驻内存的资源打包到一起,启动游戏的时候常驻内存
- 通常情况下,1M左右的AssetBundle包加载性能最好,冗余也可以接受,但是在Unity 5.3版本之后,对于AB文件的文件大小其实不必再限定于1MB之内。使用LZ4压缩,基于其Chunk的加载特点,AB加载很快,且内存占用要比之前小很多。所以LZ4的AB其实可以考虑更加粗粒度一些。
踩过的坑:
- 如何将多个资源打成同一个包:将标签和后缀都设为一样的,取AB包的代码填的是标签.后缀,取具体物体的代码填的是物体名
- AB包名.manifest:
Assets
表示包内所有物体的路径,Dependencies
表示其他包的依赖 - AssetBundles.manifest:存放所有的AB包的信息
- 包的依赖只存在于:一个物体被打为A包,而该物体包含的“子物体”被打为B包,才会产生依赖,若该“子物体”不属于任何AB包,则不会有依赖产生
- 存在依赖时的加载顺序:A包预设,B包材质,预设包含材质,A包、B包加载的顺序无所谓,就是从A包取预设前必须加载完B包,若不这么做,会出现材质丢失,预设无材质,若材质没有被打包,则不会出现材质丢失的情况