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/Raw
private static string rawPath = Application.dataPath + "/AssetBundles/Raw";
// G:/Unity3d/AssetBundleDemo/Assets/AssetBundles/Compress
private 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: 2688481811
AssetBundleManifest:
AssetBundleInfos:
Info_0:
Name: materials/material
Dependencies:
Dependency_0: textures/picture
Info_1:
Name: prefabs/quad
Dependencies:
Dependency_0: materials/material
Info_2:
Name: textures/picture
Dependencies: {}

​ 说明:后续要加载资源时,如果不清楚 AssetBundle 名,可以在 Compress.manifest 文件中查看相应 Name 值。可以看到,这里的 Name 值也全都自动转换为小写了,在加载资源时,如果传入大写的也能正常获取到相应资源。

4 加载本地资源

1)加载简单资源

// targetPath="ptextures/picture"
public static T LoadAsset<T>(string targetPath) { // 加载资源
// G:/Unity3d/AssetBundleDemo/Assets/AssetBundles/Compress/ptextures/picture
AssetBundle targetBundle = AssetBundle.LoadFromFile(compressPath + "/" + targetPath);
string fileName = targetPath.Substring(targetPath.LastIndexOf("/") + 1); // picture.jpg
object obj = targetBundle.LoadAsset(fileName);
if (obj != null) {
return (T) obj;
}
return default(T);
}

​ 说明:如果没有依赖资源,可以使用该方法;如果有依赖资源,就会出现异常。当 targetPath = "prefabs/quad" 时,创建的 Quad 显示如下,Quad 显示品红,表示它依赖的材质和图片缺失。

2)加载有依赖的资源

​ LocalAssetLoader.cs

using UnityEngine;

public class LocalAssetLoader {
// G:/Unity3d/AssetBundleDemo/Assets/AssetBundles/Compress
private static string compressPath = Application.dataPath + "/AssetBundles/Compress"; // 压缩文件根路径
// G:/Unity3d/AssetBundleDemo/Assets/AssetBundles/Compress/Compress
private 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"); // 加载预设体
// GameObject obj = LocalAssetLoader.LoadAsset<GameObject>("Prefabs/Quad"); // 加载预设体
Instantiate(obj);
}
}

​ 说明: 由于 LocalAssetLoader.LoadAsset 的入参不区分大小写,因此传入 "prefabs/quad" 和 "Prefabs/Quad" 都能正确加载资源。

​ 运行效果如下:

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/AssetBundles/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_WIN
path = "file:///" + path;
#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
path = "file://" + path;
#endif
return path;
}
}

​ SimpleLoad.cs

using UnityEngine;

public class SimpleLoad : MonoBehaviour {

    private void Start() {
W3AssetLoader.LoadAsset<GameObject>("prefabs/quad.prefab", Callback);
// W3AssetLoader.LoadAsset<GameObject>("Prefabs/Quad.prefab", Callback);
} private void Callback(GameObject obj) {
Instantiate(obj);
}
}

​ 说明:W3AssetLoader.LoadAsset() 方法的入参也不区分大小写。

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/AssetBundles/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_WIN
path = "file:///" + path; // Windows平台
#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
path = "file://" + path; // Mac平台
#endif
return path;
}
}

​ SimpleLoad.cs

using UnityEngine;

public class SimpleLoad : MonoBehaviour {

    private void Start() {
WebAssetLoader.LoadAsset<GameObject>("prefabs/quad", Callback);
} private void Callback(GameObject obj) {
Instantiate(obj);
}
}

​ 注意:WebAssetLoader.LoadAsset() 的入参区分大小写。

​ 声明:本文转自【Unity3D】基于AssetBundle实现资源热更新

【Unity3D】基于AssetBundle实现资源热更新的更多相关文章

  1. Unity3D热更新之LuaFramework篇[09]--资源热更新与代码热更新的具体实现

    前言 在上一篇文章 Unity3D热更新之LuaFramework篇[08]--热更新原理及热更服务器搭建 中,我介绍了热更新的基本原理,并且着手搭建一台服务器. 本篇就做一个实战练习,真正的来实现热 ...

  2. [Cocos2d-x]Lua 资源热更新

    什么是热更新 所谓的热更新,指的是客户端的更新. 大致的流程是,客户端在启动后访问更新的URL接口,根据更新接口的反馈,下载更新资源,然后使用新的资源启动客户端,或者直接使用新资源不重启客户端. 热更 ...

  3. [unity3d]手游资源热更新策略探讨

    原地址:http://blog.csdn.net/dingxiaowei2013/article/details/20079683 我们学习了如何将资源进行打包.这次就可以用上场了,我们来探讨一下手游 ...

  4. Unity手游之路<十二>手游资源热更新策略探讨

    http://blog.csdn.net/janeky/article/details/17666409 上一次我们学习了如何将资源进行打包.这次就可以用上场了,我们来探讨一下手游资源的增量更新策略. ...

  5. python基于函数替换的热更新原理介绍

    热更新即在不重启进程或者不离开Python interpreter的情况下使得被编辑之后的python源码能够直接生效并按照预期被执行新代码.平常开发中,热更能极大提高程序开发和调试的效率,在修复线上 ...

  6. [转]Unity手游之路<十二>手游资源热更新策略探讨

    最近梳理了下游戏流程.恩,本来想写下,但是,还是看前辈的吧 版权声明: https://blog.csdn.net/janeky/article/details/17666409 上一次我们学习了如何 ...

  7. 【学习】Unity手游之路<十二>手游资源热更新策略探讨

    http://blog.csdn.net/janeky/article/details/17666409 =============================================== ...

  8. 【架构篇】ASP.NET Core 基于 Consul 动态配置热更新

    背景 通常,.Net 应用程序中的配置存储在配置文件中,例如 App.config.Web.config 或 appsettings.json.从 ASP.Net Core 开始,出现了一个新的可扩展 ...

  9. 实现iOS图片等资源文件的热更新化(五): 一个简单完整的资源热更新页面

    简介 一个简单的关于页面,有一个图片,版本号,App名称等,着重演示各个系列的文章完整集成示例. 动机与意义 这是系列文章的最后一篇.今天抽空写下,收下尾.文章本身会在第四篇的基础上,简单扩充下代码, ...

  10. 对C#热更新方案ILRuntime的探究

    转载请标明出处:http://www.cnblogs.com/zblade/ 对于游戏中的热更,目前主流的解决方案,分为Lua(ulua/slua/xlua/tolua)系和ILRuntime代表的c ...

随机推荐

  1. Cortex-M3内核介绍

    目录 Cortex Vendor - ARM介绍 ARM主要提供指令集,需要授权 ARM使用的RSIC结构,功耗比较低 Cortex M3整体架构 核心是Processor Core - 包含寄存器和 ...

  2. 他凌晨1:30给我开源的游戏加了UI|模拟龙生,挂机冒险

    一.前言 新年就要到了,祝大家新的一年: 龙行龘龘, 前程朤朤! 白泽花了点时间,用 800 行 Go 代码写了一个控制台的小游戏:<模拟龙生>,在游戏中你将模拟一条新生的巨龙,开始无尽的 ...

  3. java - 递归排序 - 求数组最小值

    // 递归求数组最小值 public class Bubble { // 定义存储最小值的变量 static int min; public static void main(String[] arg ...

  4. Qt5.9 UI设计(六)——TitleBar功能实现

    前言 上一章介绍了ControlTreeWidget 与ControlTabWidget联动的功能,这一章我们将实现自定义 TitleBar 的功能 操作步骤 修改按键图标最大和最小值 右键按键图标, ...

  5. [转帖]oracle ZHS16GBK的数据库导入到字符集为AL32UTF8的数据库(转载+自己经验总结)

    字符集子集向其超集转换是可行的,如此例 ZHS16GBK转换为AL32UTF8. 导出使用的字符集将会记录在导出文件中,当文件导入时,将会检查导出时使用的字符集设置,如果这个字符集不同于导入客户端的N ...

  6. oceanbase部署维护命令学习

    oceanbase部署维护命令学习 背景 之前学习过TIDB数据库, 最近又准备学习一下Oceanbase数据库 发现其实两者还是比较相似的. 比较大的区别在于. TiDB是完全开源的, 并且比较明确 ...

  7. [转帖]MySQL的版本情况

    Introducing MySQL Innovation and Long-Term Support (LTS) versions (oracle.com) Introducing MySQL Inn ...

  8. [转帖]CPU计算性能speccpu2006的测试方法及工具下载

    https://www.yii666.com/blog/335517.html CPU计算性能speccpu2006的测试方法及工具下载 简介 测试原理 目录结构 测试方法 基准测试项解析 测试结果 ...

  9. MYsql备份恢复简单过程

    1. 备份数据库 建完数据库更新完补丁之后进行数据库的备份操作. mysqldump -uroot --databases yourdatabase -p > /home/yourdatabas ...

  10. Whisper对于中文语音识别与转写中文文本优化的实践(Python3.10)

    阿里的FunAsr对Whisper中文领域的转写能力造成了一定的挑战,但实际上,Whisper的使用者完全可以针对中文的语音做一些优化的措施,换句话说,Whisper的"默认"形态 ...