【Unity3D】基于AssetBundle实现资源热更新
1 前言
Unity3D 本地资源一般放在 Resources 目录下,但是 Resouces 文件夹的大小不能超过 2G,使用 AssetBundle 管理资源可以解决 Resources 文件夹受限问题。
本文代码资源见→基于AssetBundle实现资源热更新。
AssetBundle 主要用于管理资源,配合 AssetDatabase 和 AssetImporter 可以实现资源重命名,配合 BuildPipeline 可以实现资源压缩,配合 WWW 或 UnityWebRequest 可以实现加载服务器资源。下面简单介绍下相关接口:
1)AssetBundle 获取资源名,加载、卸载资源
静态方法:
// 从文件中加载AssetBundle
public static AssetBundle LoadFromFile(string path)
// 从二进制数组中加载AssetBundle
public static AssetBundle LoadFromMemory(byte[] binary)
// 从流中加载AssetBundle
public static AssetBundle LoadFromStream(Stream stream)
// 卸载所有AssetBundle
public static void UnloadAllAssetBundles(bool unloadAllObjects)
// 销毁对象
public static void Destroy(Object obj)
实例方法:
// 获取所有资源名
public string[] GetAllAssetNames()
// 判断是否包含资源
public bool Contains(string name)
// 加载资源
public Object[] LoadAllAssets()
public T[] LoadAllAssets<T>() where T : Object
public Object[] LoadAllAssets(Type type)
public Object LoadAsset(string name)
public T LoadAsset<T>(string name) where T : Object
public Object LoadAsset(string name, Type type)
// 卸载资源, unloadAllLoadedObjects为false时不卸载已从Bundle中加载出的资源
public void Unload(bool unloadAllLoadedObjects)
说明:入参 name 不区分大小写,建议使用小写,如果使用大写会自动转换为小写。
2)AssetBundleManifest 获取资源依赖
// 获取所有AssetBundles
public string[] GetAllAssetBundles()
// 获取指定assetBundleName的直接依赖
public string[] GetDirectDependencies(string assetBundleName)
// 获取指定assetBundleName的所有依赖
public string[] GetAllDependencies(string assetBundleName)
说明:入参 assetBundleName 不区分大小写,建议使用小写,如果使用大写会自动转换为小写。
3)AssetDatabase 获取所有资源名、删除资源
// 获取所有AssetBundle资源名
public static string[] GetAllAssetBundleNames()
// 根据assetBundleName删除AssetBundle
public static bool RemoveAssetBundleName(string assetBundleName, bool forceRemove)
// 刷新Project视图目录, 相当于右键手动刷新
public static void Refresh()
4)AssetImporter 设置资源名
// 获取AssetImporter, 资源文件路径
public static AssetImporter GetAtPath(string path)
// 获取/设置资源文件名
public string assetBundleName { get; set; }
5)BuildPipeline 压缩资源
// 压缩所有标记为AssetBundle的资源
public static AssetBundleManifest BuildAssetBundles(string outputPath, // 压缩文件输出路径BuildAssetBundleOptions assetBundleOptions, // 压缩算法BuildTarget targetPlatform // 平台
)
// BuildAssetBundleOptions.None: LZMA压缩算法, 压缩比大, 加载慢, 使用前需要整体解压
// BuildAssetBundleOptions.ChunkBasedCompression: LZ4压缩算法, 压缩比中等, 加载快可以加载指定资源而不用解压全部
// BuildAssetBundleOptions.UncompressedAssetBundle: 不压缩, 加载快
6)WWW 获取网络资源
// 获取WWW
public static WWW LoadFromCacheOrDownload(string url, int version)
// 获取AssetBundle
public AssetBundle assetBundle { get; }
说明:WWW 被 Unity3D 官方标记为过时了,建议使用 UnityWebRequest。
7)UnityWebRequest 获取网络资源
UnityWebRequest webRequest = UnityWebRequestAssetBundle.GetAssetBundle(uri)
yield return webRequest.SendWebRequest()
AssetBundle assetBundle = DownloadHandlerAssetBundle.GetContent(webRequest)
2 资源命名
Asset 资源主要有脚本、图片、网格、模型、预设体等,在 Assets 窗口选中资源,在 Inspector 窗口选择 AssetBundle 下拉列表,选择 New 给资源添加 AssetBundle 名,如下:
说明:AssetBundle 名不区分大小写,如果输入大写会自动转换为小写。只有添加了 AssetBundle 名的资源才能通过 BuildPipeline.BuildAssetBundles() 打包压缩。
3 资源压缩
1)创建目录及原资源
在 Assets 目录下创建 AssetBundles 目录(存放资源)和 Editor 目录(存放资源压缩脚本),在 AssetBundles 目录下创建 Compress 目录(存放压缩文件)和 Raw 目录(存放原资源文件),再在 Raw 目录下创建 Textures 目录(存放了一张图片 Picture)、Materials 目录(存放了一个材质 Material,并且依赖 Picture)、Prefabs 目录(存放了一个预设体 Quad,并且依赖 Material),目录结构如下:
2)自动压缩脚本
AssetCompressor.cs
using System.IO;
using UnityEditor;
using UnityEngine;public class AssetCompressor : Editor {// G:/Unity3d/AssetBundleDemo/Assets/AssetBundles/Rawprivate static string rawPath = Application.dataPath + "/AssetBundles/Raw";// G:/Unity3d/AssetBundleDemo/Assets/AssetBundles/Compressprivate static string compressPath = Application.dataPath + "/AssetBundles/Compress";[MenuItem("AssetBundle/CompressAssets")]public static void CompressAssets() { // 打包rawPath目录的资源, 生成压缩资源到compressPath目录ClearAllFilesBundleName();SetAssetBundlesName(rawPath);BuildAssetBundles();ClearAllFilesBundleName();AssetDatabase.Refresh(); // 刷新Project视图目录, 相当于右键手动刷新}private static void BuildAssetBundles() { // 压缩资源BuildPipeline.BuildAssetBundles(compressPath, // 压缩包输出包路径BuildAssetBundleOptions.ChunkBasedCompression, // 压缩算法BuildTarget.StandaloneWindows64 // Windows平台);}private static void ClearAllFilesBundleName() { // 删除所有AssetBundle名string[] names = AssetDatabase.GetAllAssetBundleNames();foreach (string name in names) {AssetDatabase.RemoveAssetBundleName(name, true);}}private static void SetAssetBundlesName(string rootPath) { // 设置资源的Bundle名DirectoryInfo rootInfo = new DirectoryInfo(rootPath);FileSystemInfo[] fileInfos = rootInfo.GetFileSystemInfos();foreach (FileSystemInfo fileInfo in fileInfos) {if (fileInfo is DirectoryInfo) {SetAssetBundlesName(fileInfo.FullName); // 递归遍历子文件夹下所有文件} else if (!fileInfo.Name.EndsWith(".meta")) {SetAssetBundleName(fileInfo.FullName);}}}private static void SetAssetBundleName(string filePath) { // 设置资源的Bundle名// 导入的相对路径(G:/Unity3d/AssetBundleDemo/Assets/AssetBundles/Compress/prefabs/quad.prefab)string impoterPath = "Assets/" + filePath.Substring(Application.dataPath.Length + 1);AssetImporter assetImporter = AssetImporter.GetAtPath(impoterPath);if (assetImporter != null) {filePath = filePath.Substring(rawPath.Length + 1); // 去源文件前缀(可选, 建议使用)// filePath = filePath.Substring(filePath.LastIndexOf("\\") + 1); // 去所有前缀(可选, 不建议使用)// 去后缀(可选, 不去后缀刷新目录后会报错, 但不影响资源压缩和后续资源加载)// filePath = filePath.Remove(filePath.LastIndexOf("."));assetImporter.assetBundleName = filePath;}}
}
说明:AssetCompressor.cs 文件需要放在 Editor 目录下,编译成功后,在菜单栏可以看到 AssetBundle 菜单,如下:
点击 CompressAssets 选项,会将 Assets/AssetBundles/Raw 目录下的资源打包压缩至 Assets/AssetBundles/Compress 目录,如下:
注意:运行上述代码后,会报以下错误,这是因为文件已经压缩了,但还是以 “.prefab”、“.jpg”、“.mat” 为后缀,被 Unity3D 识别为损坏文件。该错误不影响压缩文件生成,也不影响后续资源加载,可以忽略。如果不想出现以下报错,可以将去后缀的注释代码打开。
3)压缩文件
打开 Compress.manifest 文件如下:
ManifestFileVersion: 0
CRC: 3680739267
AssetBundleManifest:AssetBundleInfos:Info_0:Name: materials/material.matDependencies:Dependency_0: textures/picture.jpgInfo_1:Name: prefabs/quad.prefabDependencies:Dependency_0: materials/material.matInfo_2:Name: textures/picture.jpgDependencies: {}
说明:后续要加载资源时,如果不清楚 AssetBundle 名,可以在 Compress.manifest 文件中查看相应 Name 值。可以看到,这里的 Name 值也全都自动转换为小写了,在加载资源时,如果传入大写的也能正常获取到相应资源。
4 加载本地资源
1)加载简单资源
// targetPath="ptextures/picture.jpg"
public static T LoadAsset<T>(string targetPath) { // 加载资源// G:/Unity3d/AssetBundleDemo/Assets/AssetBundles/Compress/ptextures/picture.jpgAssetBundle targetBundle = AssetBundle.LoadFromFile(compressPath + "/" + targetPath);string fileName = targetPath.Substring(targetPath.LastIndexOf("/") + 1); // picture.jpgobject obj = targetBundle.LoadAsset(fileName);if (obj != null) {return (T) obj;}return default(T);
}
说明:如果没有依赖资源,可以使用该方法;如果有依赖资源,就会出现异常。当 targetPath = "prefabs/quad.prefab" 时,创建的 Quad 显示如下,Quad 显示品红,表示它依赖的材质和图片缺失。
2)加载有依赖的资源
LocalAssetLoader.cs
using UnityEngine;public class LocalAssetLoader {// G:/Unity3d/AssetBundleDemo/Assets/AssetBundles/Compressprivate static string compressPath = Application.dataPath + "/AssetBundles/Compress"; // 压缩文件根路径// G:/Unity3d/AssetBundleDemo/Assets/AssetBundles/Compress/Compressprivate static string rootManifestPath = compressPath + "/Compress"; // 根manifest文件路径(Compress.manifest文件绝对路径)public static T LoadAsset<T>(string targetPath) { // 加载资源LoadDependencies(targetPath);return LoadTarget<T>(targetPath);}private static void LoadDependencies(string targetPath) { // 加载目标资源的依赖AssetBundle manifestBundle = AssetBundle.LoadFromFile(rootManifestPath);// 解压Manifest文件, 传入的参数不区分大小写(如果是大写, 会自动转换为小写)AssetBundleManifest manifest = manifestBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");string[] dependencies = manifest.GetAllDependencies(targetPath); // 获取目标文件的所有依赖for (int i = 0; i < dependencies.Length; i++) {string filePath = compressPath + "/" + dependencies[i];AssetBundle.LoadFromFile(filePath);}}private static T LoadTarget<T>(string targetPath) { // 加载目标资源AssetBundle targetBundle = AssetBundle.LoadFromFile(compressPath + "/" + targetPath);string fileName = targetPath.Substring(targetPath.LastIndexOf("/") + 1);object obj = targetBundle.LoadAsset(fileName);if (obj != null) {return (T) obj;}return default(T);}
}
说明:manifest.GetAllDependencies()、AssetBundle.LoadFromFile()、targetBundle.LoadAsset() 的入参不区分大小写,因此传入的 targetPath 也可以不区分大小写。
3)调用 LocalAssetLoader 加载资源
SimpleLoad.cs
using UnityEngine;public class SimpleLoad : MonoBehaviour {private void Start() {GameObject obj = LocalAssetLoader.LoadAsset<GameObject>("prefabs/quad.prefab"); // 加载预设体// GameObject obj = LocalAssetLoader.LoadAsset<GameObject>("Prefabs/Quad.prefab"); // 加载预设体Instantiate(obj);}
}
说明: 由于 LocalAssetLoader.LoadAsset 的入参不区分大小写,因此传入 "prefabs/quad.prefab" 和 "Prefabs/Quad.prefab" 都能正确加载资源。
运行效果如下:
5 使用 WWW 加载服务器资源
W3AssetLoader.cs
using System;
using System.Collections;
using UnityEngine;public class W3AssetLoader : MonoBehaviour {private string compressPath; // 压缩文件根路径private string rootManifestPath; // 根manifest文件路径private static W3AssetLoader instance; // 单例private void Awake() {instance = this;// compressPath = GetW3Path(Application.dataPath + "/AssetBundle/Compress");compressPath = "https://gitcode.net/m0_37602827/AssetBundleDemo/-/raw/master/Assets/AssetBundle/Compress";rootManifestPath = compressPath + "/Compress";}public static void LoadAsset<T>(string targetPath, Action<T> action) { // 加载资源instance.StartCoroutine(instance.LoadAssetCorutine<T>(targetPath, action));}private IEnumerator LoadAssetCorutine<T>(string targetPath, Action<T> action) { // 加载资源的协程yield return LoadDependencies(targetPath);yield return LoadTarget(targetPath, action);}private IEnumerator LoadDependencies(string targetPath) { // 加载目标资源的依赖WWW w3 = WWW.LoadFromCacheOrDownload(rootManifestPath, 1);yield return w3;AssetBundle assetBundle = w3.assetBundle;if (assetBundle != null) {AssetBundleManifest manifest = assetBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");if (manifest != null) {string[] dependencies = manifest.GetAllDependencies(targetPath); // 获取目标文件的所有依赖for (int i = 0; i < dependencies.Length; i++) {string filePath = compressPath + "/" + dependencies[i];w3 = WWW.LoadFromCacheOrDownload(filePath, 1);yield return w3;assetBundle = w3.assetBundle;}}}}private IEnumerator LoadTarget<T>(string targetPath, Action<T> action) { // 加载目标文件string fullPath = compressPath + "/" + targetPath;WWW w3 = WWW.LoadFromCacheOrDownload(fullPath, 1);yield return w3;AssetBundle assetBundle = w3.assetBundle;if (assetBundle != null) {string fileName = targetPath.Substring(targetPath.LastIndexOf("/") + 1);object obj = assetBundle.LoadAsset(fileName); // 解压文件if (obj != null && action != null) {action.Invoke((T) obj);}}}private string GetW3Path(string path) {#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WINpath = "file:///" + path;#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSXpath = "file://" + path;#endifreturn path;}
}
SimpleLoad.cs
using UnityEngine;public class SimpleLoad : MonoBehaviour {private void Start() {W3AssetLoader.LoadAsset<GameObject>("prefabs/quad", Callback);// W3AssetLoader.LoadAsset<GameObject>("Prefabs/Quad", Callback);}private void Callback(GameObject obj) {Instantiate(obj);}
}
注意:W3AssetLoader.LoadAsset() 方法的入参不要加 ".prefab" 后缀,并且入参也不区分大小写。
6 使用 UnityWebRequest 加载服务器资源
WebAssetLoader.cs
using System;
using System.Collections;
using UnityEngine;
using UnityEngine.Networking;public class WebAssetLoader : MonoBehaviour {private string compressPath; // 压缩文件根路径private string rootManifestPath; // 根manifest文件路径private static WebAssetLoader instance; // 单例private void Awake() {instance = this;// compressPath = GetW3Path(Application.dataPath + "/AssetBundle/Compress");compressPath = "https://gitcode.net/m0_37602827/AssetBundleDemo/-/raw/master/Assets/AssetBundle/Compress";rootManifestPath = compressPath + "/Compress";}public static void LoadAsset<T>(string targetPath, Action<T> action) { // 加载资源instance.StartCoroutine(instance.LoadAssetCorutine<T>(targetPath, action));}private IEnumerator LoadAssetCorutine<T>(string targetPath, Action<T> action) { // 加载资源的协程yield return LoadDependencies(targetPath);yield return LoadTarget(targetPath, action);}private IEnumerator LoadDependencies(string targetPath) { // 加载目标资源的依赖UnityWebRequest webRequest = UnityWebRequestAssetBundle.GetAssetBundle(rootManifestPath);yield return webRequest.SendWebRequest();AssetBundle assetBundle = DownloadHandlerAssetBundle.GetContent(webRequest);if (assetBundle != null) {AssetBundleManifest manifest = assetBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");if (manifest != null) {string[] dependencies = manifest.GetAllDependencies(targetPath); // 获取目标文件的所有依赖for (int i = 0; i < dependencies.Length; i++) {string filePath = compressPath + "/" + dependencies[i];webRequest = UnityWebRequestAssetBundle.GetAssetBundle(filePath);yield return webRequest.SendWebRequest();assetBundle = DownloadHandlerAssetBundle.GetContent(webRequest);}}}}private IEnumerator LoadTarget<T>(string targetPath, Action<T> action) { // 加载目标文件string fullPath = compressPath + "/" + targetPath;UnityWebRequest webRequest = UnityWebRequestAssetBundle.GetAssetBundle(fullPath);yield return webRequest.SendWebRequest();AssetBundle assetBundle = DownloadHandlerAssetBundle.GetContent(webRequest);if (assetBundle != null) {string fileName = targetPath.Substring(targetPath.LastIndexOf("/") + 1);object obj = assetBundle.LoadAsset(fileName); // 解压文件if (obj != null && action != null) {action.Invoke((T) obj);}}}private string GetW3Path(string path) {#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WINpath = "file:///" + path; // Windows平台#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSXpath = "file://" + path; // Mac平台#endifreturn path;}
}
SimpleLoad.cs
using UnityEngine;public class SimpleLoad : MonoBehaviour {private void Start() {WebAssetLoader.LoadAsset<GameObject>("prefabs/quad.prefab", Callback);}private void Callback(GameObject obj) {Instantiate(obj);}
}
注意:WebAssetLoader.LoadAsset() 的入参需要添加 ".prefab" 后缀,并且入参区分大小写。
【Unity3D】基于AssetBundle实现资源热更新相关推荐
- Addressable资源热更新疑问
1)Addressable资源热更新疑问 2)如何解决远处网格线会花的问题 3)关于着色器中某些特殊图片的用途 4)Lightmap在内存中有重复加载 5)Unity Job System问题 这是 ...
- 关于uni-app的资源热更新!!!
先看官方文档,对uni-app 的资源热更新有一个大致的了解: 整包升级 在线资源热更新 app方法 本文主要讲述,实现资源热更新时客户端需要进行的操作: 可以在***app.vue***的***on ...
- [unity3d]手游资源热更新策略探讨
原地址:http://blog.csdn.net/dingxiaowei2013/article/details/20079683 我们学习了如何将资源进行打包.这次就可以用上场了,我们来探讨一下手游 ...
- 《天涯明月刀》游戏资源热更新解决方案
天刀在韩国化的过程中,韩方对天刀的游戏内商城功能提出了一系列的适应他们本土运营习惯的商业化改造需求,其中最重要的基础功能修改就是需要商城能够支持不停服修改商品内容或上下架商品,包含新增未事先打进版本配 ...
- GameFramework篇:StarForce资源热更新讲解(一:基本流程)
准备工作: StarForce dev/Update分支 https://github.com/EllanJiang/StarForce/tree/dev/Update 注意下载子库 ...
- Unity资源热更新
通过把StreamingAssets文件夹下的本地的资源MD5列表文件复制到persistentDataPath文件夹下和服务器上的进行对比,把有变化的资源更新替换到本地persistentDataP ...
- Unity资源热更新--资源管理、Addressable
实习前理解错了mentor的意思,任务没完成TAT,火速学习一下addressable,另外狠狠吐槽北京健康宝弹窗. 资源打包方式 可热更资源类型:预制件.材质.贴图.模型.场景.shader.动画与 ...
- GameFramework篇:StarForce资源热更新讲解(二:具体操作步骤)
上篇我们讲解了基本的实现流程,那么本篇我们来谈谈实际操作,从零开始一步步配置 先来捋一遍思路 首先,Full目录下的资源是Unity打出来的AssetBundle经过加密(如果选择了Load from ...
- Unity 热更新技术 |(七)完整热更新实战案例学习使用,包括资源热更新及代码热更新【文末书】
最新文章
- win10鼠标灵敏度怎么调_和平精英最稳压枪灵敏度怎么调教程,适合所有段位以及适合国际版PUBG手游压枪...
- Python_note7 文件和数据格式化+wordcloud库
- VTK修炼之道46:图形基本操作进阶_三角网格体积、表面积、测地距离、包围盒
- StyleAI:白度-物理上,怎样才算白?
- php 常用的系统函数
- 遗传算法python实现_Python遗传算法代码实例讲解
- 苹果5G手机因疫情影响 或将推迟至10月发布
- es 创建索引_es的基本原理和操作文档
- 分布式监控系统开发【day38】:报警阈值程序逻辑解析(三)
- 【时间序列预测算法】——Holt-Winters算法介绍及代码实现
- 内网环境部署zabbix5.0版本监控(一)
- 安卓非微信内置浏览器中的网页调起微信支付的方案研究
- vant-ui的官方入口
- kail之MSF渗透测试
- LeetCode 12 数字转化为罗马符号(难度: Medium)
- SpringBoot项目的云服务器部署
- 深度|从一个故事说起,谈谈企业应用架构的演变史
- opencv 边缘羽化,边缘过渡
- C/C++实现矩阵/图形90°旋转
- 百度富文本编辑器 UM