赞
踩
注意:C#代码不能打AB包,因为C#代码在执行前需要一个专门的编译过程,C#是编译语言
有些公司会自己写工具,而且是写好的工具,一般支持自定义功能
安装工具,Window -> Package Manager
输入框中搜索 Asset Bundle Browser
如果搜索不到,因为Unity版本过低,可以去Unity的AssetBundleBrower网站上下载。
下载好之后,可以在 Window 看到,直接打开
打开的界面如下
3. 如何把资源关联到AB包中
如何把资源关联到AB包中
详细的介绍可以参考官网:Unity Asset Bundle Browser 工具
window -> AssetBundleBrowser -> Build
Compression 打包压缩模式详细
一般商业上使用LZMA格式和LZ4格式的比较多
这里选择yes,会自动创建并放到该文件夹,打包成功之后可以刷新Project窗口看到该文件夹
打包后的文件:
打开Asset\StreamingAssets
文件夹:
打开AssetBundles\StandaloneWindows
文件夹:
StandaloneWindows
是主包,存储了包与包之间的关系,非常重要。没有后缀名的文件是资源文件,内部都是二进制,.manifest
文件是对应的资源文件相关的配置信息,依赖相关的信息。
Inspect 界面,查看详细信息包的
可以看到AB包大小
第一种资源来源:自动打包资源,Unity 场景中直接使的资源会随着场景被自动打包到游戏中,这些资源会在场景加载的时候由unity自动加载,这些场景只要放置在Unity工程目录的Asset文件夹下,程序不需要关系他们的打包和加载,因为这些资源都是静态加载的。(但实际开发中,我们一般都会动态创建GameObject ,资源是动态加载的,所以静态的资源一般不多)
第二种资源来源: Resources资源,指的是在Unity工程的Asset目录下面可以建一个Resources 文件夹。Resources可从脚本中按需加载资源,而不必在场景中创建资源实例用于游戏,需要加载Resources的资源时,使用 Resources.Load函数即可动态加载这些资源。这个文件夹下放置的所有资源,不论是否被场景用到,都会被打包到游戏中,这是平时开发常用的资源加载方式,但缺点是资源都直接打包到游戏中,不好做增量更新
注意:如果 Resources 文件夹是 Editor 的子文件夹,则其中的资源可通过 Editor 脚本加载,但会从构建中删除。
AssetBundle 资源:可以理解为Unity的压缩包,这个压缩包可以包含针对特定平台的资源,比如说:贴图、模型、prefab、音频甚至整个场景,我们可以把资源打包成多个独立的AB包,这些包和游戏包是分离的,可以用来做增量包,更新内容(一开始游戏没有的玩到的内容做成增量包,玩到了通过网络下载)、还可以做分包发布,减少安装包大小(比如大多页游的游戏资源是随着游戏的过程增量下载的,或者有些手游资源过大,渠道要求发布的客户端包体大小被限制在100M以内,那也可以做成增量包,玩游戏的时候通过网络下载),也可以做成增量更新,可以热更修复游戏bug
所以综上所述,AssetBundle资源更加灵活,可以实现资源的AB包热更,配合Lua语言开发游戏还可以实现热更修复bug、更新玩法等。
因为我们可以通过路径加载AB包,所以我们读取的AB包应该是copy出来在 StreamingAssets
文件夹中的AB包,而不是在我们选定的输出路径中的AB包,因为输出路径与Asset
文件夹同级,不会被打包。而 StreamingAssets
文件夹会随着Unity3D打包而被打包出去,在PC平台是可读可写的,在安卓和IOS都只是可读的。
using System.Collections; using System.Collections.Generic; using UnityEngine; public class ABTest : MonoBehaviour { // Start is called before the first frame update void Start() { // 1. 加载 AB包 AssetBundle ab = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "module"); // 从文件中同步加载 :路径 + 包名 // 2. 加载 AB包中的资源 建议用泛型写法 或者 Type指定类型加载 // 只使用名字加载 会出现 同名不同类型资源 分不清 //GameObject obj = ab.LoadAsset<GameObject>("Cube"); // 泛型加载 GameObject obj = ab.LoadAsset("Cube", typeof(GameObject)) as GameObject; // Type指定类型加载 Instantiate(obj); } // Update is called once per frame void Update() { } }
using System.Collections; using System.Collections.Generic; using UnityEngine; public class ABTest : MonoBehaviour { // Start is called before the first frame update void Start() { // 1. 加载 AB包 AssetBundle ab = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "module"); // 从文件中同步加载 :路径 + 包名 // 2. 加载 AB包中的资源 建议用泛型写法 或者 Type指定类型加载 // 只使用名字加载 会出现 同名不同类型资源 分不清 //GameObject obj = ab.LoadAsset<GameObject>("Cube"); // 泛型加载 GameObject cube = ab.LoadAsset("Cube", typeof(GameObject)) as GameObject; // Type指定类型加载 Instantiate(cube); // 加载一个桶 GameObject barrel_Sealed_01 = ab.LoadAsset("Barrel_Sealed_01", typeof(GameObject)) as GameObject; // Type指定类型加载 Instantiate(barrel_Sealed_01); } // Update is called once per frame void Update() { } }
注意:这里的预制体显示紫色,说明该预制体已经加载出来但是预制体上的资源丢失,这与下面要讲到的依赖有关。
AB包不能重复加载,一个名字的AB包只有一个,重复加载会报错
Sprite
资源自己单独找图集做头像,不熟悉Texture 2D的话可以提前了解Unity Texture 2D 图集切割 输出单个PNG,输出单个PNG后还要把每一个转成Sprite
格式头像图片生成新的AB包
build 过程报错:
解决方法:出现bug的原因是是使用了UnityEditor相关的API,所以把相关的脚本(我这里是ImageSlicer.cs
)放到 Assets/Editor/ 目录下即可解决,相关报错都可以用这个操作解决
使用协程实现异步的代码
IEnumerator LoadABRes(string ABName, string resName)
{
// 第一步 加载AB包
AssetBundleCreateRequest abcr = AssetBundle.LoadFromFileAsync(Application.streamingAssetsPath + "/" + ABName);
yield return abcr;
// 第二步 加载资源
AssetBundleRequest abr = abcr.assetBundle.LoadAssetAsync(resName, typeof(Sprite));
yield return abr;
// 显示sprite
img.sprite = abr.asset as Sprite;
}
启动协程方式:
// 异步加载 -> 协程
StartCoroutine(LoadABRes("sprite", "Avatar7")); // 输入包名,资源名
Sprite需要显示在Image上,所以需要建一个UI画布给图片显示,在场景里直接添加画布显示
脚本里可以添加一个Public的Image:public Image img;
直接把场景里面的image拖到img中
完整的代码
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class ABTest : MonoBehaviour { public Image img; // Start is called before the first frame update void Start() { // 1. 加载 AB包 AssetBundle ab = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "module"); // 从文件中同步加载 :路径 + 包名 // 2. 加载 AB包中的资源 建议用泛型写法 或者 Type指定类型加载 // 只使用名字加载 会出现 同名不同类型资源 分不清 //GameObject obj = ab.LoadAsset<GameObject>("Cube"); // 泛型加载 GameObject cube = ab.LoadAsset("Cube", typeof(GameObject)) as GameObject; // Type指定类型加载 Instantiate(cube); // 加载一个球 //GameObject sphere = ab.LoadAsset("sphere", typeof(GameObject)) as GameObject; // Type指定类型加载 //Instantiate(sphere); // 异步加载 -> 协程 StartCoroutine(LoadABRes("sprite", "Avatar7")); } IEnumerator LoadABRes(string ABName, string resName) { // 第一步 加载AB包 AssetBundleCreateRequest abcr = AssetBundle.LoadFromFileAsync(Application.streamingAssetsPath + "/" + ABName); yield return abcr; // 第二步 加载资源 AssetBundleRequest abr = abcr.assetBundle.LoadAssetAsync(resName, typeof(Sprite)); yield return abr; // 显示sprite img.sprite = abr.asset as Sprite; } // Update is called once per frame void Update() { } }
运行结果
卸载所有加载的AB包接口
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
//卸载所有加载的AB包 参数为true 会把通过AB包加载的资源也卸载了
AssetBundle.UnloadAllAssetBundles(true);
}
}
运行后卸载:
图消失了,资源丢失,变成紫色也说明立方体的资源丢失材质球丢失
若参数改成false:
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
//卸载所有加载的AB包 参数为true 会把通过AB包加载的资源也卸载了
AssetBundle.UnloadAllAssetBundles(false);
}
}
再次运行卸载后,资源不会消失
卸载单个AB包
// 卸载单个AB包, 参数为 true 会卸载该AB包所有加载的资源
ab.Unload(true);
若Cube使用Unload函数,则资源也被卸载
若参数为false,则资源还在,只卸载AB包
// 卸载单个AB包
ab.Unload(false);
运行结果:
加载有材质球的Cube
这时打开 AssetBundles 会发现这个材质球默认跟Cube在同一个AB包内:
若把材质球放到另外的AB包内,比如说materials包,再次bulid:
然后把上一次的卸载代码注释掉,直接运行:
依赖知识点:一个资源身上用到了别的AB包中的资源,若加载资源的时候只加载了自己的AB包,通过这个资源创建的对象,会出现资源丢失的情况,这种时候需要把依赖包一起加载才能正常
所以我们加载Cube的时候,也应该加载materials
包
// 加载依赖包
AssetBundle abRely = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "materials");
添加这个加载materials
的代码后,再次运行就可以看到Cube正常显示。
上面的这种方法可以解决当前Cube不能正常显示材质球问题,但是如果这个Cube不止依赖材质球,也许还有其他资源,那么这种时候,就像是一个资源用到了数个其他AB包中的资源:
一个资源身上用到了别的AB包的资源,如果只加载自己的AB包,通过自己的AB创建对象就回出现资源丢失的情况,这时只需要把依赖包一起加载了就正常显示。
加载AB包:
AssetBundle ab = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "model");
加载依赖包:
AssetBundle abRely = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "materials");
如何获取当前这个AB包内的资源依赖了什么AB包?通过主包来获取这一依赖信息!可以打印出来看看依赖包名是啥
// 加载主包
AssetBundle abMain = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "StandaloneWindows");
// 加载主包中的固定文件
AssetBundleManifest abManifest = abMain.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
// 从固定文件中 得到依赖信息 直接填包名
string[] strs = abManifest.GetAllDependencies("module");
// 得到依赖包的名字
for (int i = 0; i < strs.Length; i++)
{
Debug.Log(strs[i]);
}
运行打印:
所以需要加载当前AB包依赖的AB包时,把打印换成AB包加载即可:
// 加载主包
AssetBundle abMain = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "StandaloneWindows");
// 加载主包中的固定文件
AssetBundleManifest abManifest = abMain.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
// 从固定文件中 得到依赖信息 直接填包名
string[] strs = abManifest.GetAllDependencies("module");
// 得到依赖包的名字
for (int i = 0; i < strs.Length; i++)
{
// 可以得到所有AB包 应该需要存起来释放掉~ 这里主要是为了测试
AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + strs[i]);
}
运行结果:
以上加载依赖的弊端:
Manifest
文件查看依赖内容:赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。