一、YooAsset 是什么?
YooAsset 是由途游游戏开源的 Unity 资源管理系统,它可以满足以下典型需求:
- 发布零资源安装包,玩家边玩边下载
- 发布保证前期体验的安装包,后续内容按需下载
- 发布 300MB 以内的安装包,进游戏前下载剩余内容
- 偏单机游戏,联网时更新,断网时玩老版本
- MOD 游戏,玩家上传和下载自制内容
- 超大体量项目,分工程构建上百 GB 资源
核心特性
| 特性 | 说明 |
|---|---|
| 构建管线无缝切换 | 支持内置构建管线(BBP)和可编程构建管线(SBP) |
| 分布式构建 | 支持分工程、分内容构建,方便 MOD 支持 |
| 可寻址资源定位 | 支持完整路径和可寻址地址两种定位方式 |
| 基于标签的分包 | 自动对依赖资源包分类,避免人工维护 |
| 引用计数管理 | 安全的资源卸载策略,附带泄漏分析器 |
| 多种运行模式 | 编辑器模拟、单机、联机、WebGL、自定义模式自由切换 |
| 边玩边下载 | 加载时自动下载缺失资源包,支持断点续传 |
| 原生文件管理 | 无缝衔接打包系统,支持原生文件版本管理和下载 |
二、安装 YooAsset
YooAsset 提供三种安装方式,推荐使用 OpenUPM 安装,方便后续版本升级。
方式一:通过 Package Manager 安装(推荐)
- 打开
Edit → Project Settings → Package Manager - 添加 Scoped Registry:
| 字段 | 值 |
|---|---|
| Name | package.openupm.com |
| URL | package.openupm.com |
| Scope(s) | com.tuyoogame.yooasset |
- 打开
Edit → Windows → Package Manager,选择My Registries,找到 YooAsset 点击 Install
注意:按照自己需求安装特定版本,不要安装最新版,避免 API 变更导致代码报错。
方式二:通过 manifest.json 安装
直接修改 Packages/manifest.json 文件:
json
{
"dependencies": {
"com.tuyoogame.yooasset": "2.3.18",
...
},
"scopedRegistries": [
{
"name": "package.openupm.com",
"url": "https://package.openupm.com",
"scopes": [
"com.tuyoogame.yooasset"
]
}
]
}
方式三:通过 GitHub 下载
在 YooAsset Releases 页面下载最新版本的 Source Code 压缩包,解压到项目的 Assets/YooAsset 目录下。
系统要求
- Unity 版本:2019.4 / 2020.3 / 2021.3 / 2022.3 / 6.0
- 支持平台:Windows、macOS、Android、iOS、WebGL
- 开发环境:.NET 4.x
安装完成后的目录结构:
markdown
Assets/
└── YooAsset/
├── Editor/ 编辑器源码目录
├── Runtime/ 运行时源码目录
├── LICENSE 版权文档
└── README 说明文档
三、全局配置
安装完成后,第一步是创建全局配置文件。
- 在 Project 窗口中右键 →
Create → YooAsset → Create Setting - 重要 :将生成的配置文件放到
Resources文件夹下
配置项说明:
| 配置项 | 说明 |
|---|---|
| Default Yoo Folder Name | 沙盒目录和内置目录的根目录名称 |
这个名称决定了运行时资源在设备上的存储路径,一般保持默认即可。
四、资源配置
资源配置是 YooAsset 工作流的核心环节,它决定了哪些资源参与打包、如何分组、如何寻址。
打开资源配置窗口:Window → YooAsset → AssetBundle Collector
4.1 界面概览
- 左侧:分组列表
- 右侧:当前分组的详细配置
注:第一次进入的时候有可能左右结构,需要把
Global Setting下方的Show Packages选中,Package Settings里面的内容按需勾选,参考4.2
工具栏按钮:
- 导入:导入保存的 XML 配置文件
- 导出:将配置导出为 XML 文件(方便版本管理和团队协作)
- 修复:当配置中的文件夹被移动后,点击修正路径
4.2 包(Package)设置
每个 Package 是一个独立的资源包单元,支持以下配置:
| 配置项 | 说明 |
|---|---|
| Enable Addressable | 启用可寻址资源定位。开启后同时支持全路径加载 |
| Location To Lower | 资源定位地址大小写不敏感 |
| Include Asset GUID | 资源清单中包含资源 GUID 信息 |
| Auto Collect Shaders | 将所有着色器构建到独立的资源包内 |
| File Ignore Rule | 文件全局忽略规则,可自定义扩展 |
提示 :当项目有多个 Package 时,建议开启
Unique Bundle Name,为资源包名追加 PackageName 前缀,避免冲突。
4.3 资源分组(Group)
分组是对资源的逻辑划分,比如可以按功能模块分为 UI、Scene、Audio、Config 等。
分组配置:
| 配置项 | 说明 |
|---|---|
| Active Rule | 激活规则,支持自定义。内置:EnableGroup(启用)、DisableGroup(禁用) |
| Grouper Name | 分组名称 |
| Grouper Desc | 分组备注 |
| Asset Tags | 资源分类标签,用分号分隔,如 level1;level2;level3 |
4.4 收集器(Collector)
收集器定义了如何收集某个路径下的资源,是资源配置的最细粒度。
| 配置项 | 说明 |
|---|---|
| Collect Path | 收集路径,可以是文件夹或单个文件 |
| Collector Type | 收集器类型(见下文) |
| AddressRule | 可寻址规则 |
| PackRule | 打包规则 |
| FilterRule | 过滤规则 |
| User Data | 用户自定义数据 |
| Asset Tags | 该收集器下资源的标签 |
收集器类型:
| 类型 | 说明 |
|---|---|
| MainAssetCollector | 主资源,写入资源清单,可通过代码加载 |
| StaticAssetCollector | 参与打包但不写入清单,适合定制化打包策略 |
| DependAssetCollector | 依赖资源,不写入清单,未被引用时自动剔除 |
可寻址规则(AddressRule):
| 规则 | 说明 |
|---|---|
| AddressByFileName | 以文件名为地址 |
| AddressByFilePath | 以文件路径为地址 |
| AddressByGroupAndFileName | 分组名 + 文件名 |
| AddressByFolderAndFileName | 文件夹名 + 文件名 |
打包规则(PackRule):
| 规则 | 说明 |
|---|---|
| PackSeparately | 每个文件单独打包 |
| PackDirectory | 同一文件夹的文件打成一个包 |
| PackTopDirectory | 收集器下顶级文件夹打成一个包 |
| PackGroup | 整个分组打成一个包 |
| PackRawFile | 处理为原生资源包 |
过滤规则(FilterRule):
| 规则 | 说明 |
|---|---|
| CollectAll | 收集所有资源文件 |
| CollectScene | 只收集场景文件 |
| CollectPrefab | 只收集预制体文件 |
| CollectSprite | 只收集精灵类型文件 |
4.5 实战:配置一个示例项目
假设我们的项目结构如下:
swift
Assets/
└── GameRes/
├── Scenes/ 场景资源
├── Prefabs/ 预制体
├── UI/
│ ├── Prefabs/ UI预制体
│ └── Atlas/ UI图集
├── Audio/ 音频
└── Config/ 配置表
推荐配置方案:
| 分组名 | Collect Path | Collector Type | PackRule | FilterRule | AddressRule |
|---|---|---|---|---|---|
| Scene | Assets/GameRes/Scenes | MainAssetCollector | PackSeparately | CollectScene | AddressByFileName |
| Prefab | Assets/GameRes/Prefabs | MainAssetCollector | PackDirectory | CollectAll | AddressByFileName |
| UIPrefab | Assets/GameRes/UI/Prefabs | MainAssetCollector | PackDirectory | CollectPrefab | AddressByFileName |
| UIAtlas | Assets/GameRes/UI/Atlas | MainAssetCollector | PackDirectory | CollectSprite | AddressByFileName |
| Audio | Assets/GameRes/Audio | MainAssetCollector | PackDirectory | CollectAll | AddressByFileName |
| Config | Assets/GameRes/Config | MainAssetCollector | PackGroup | CollectAll | AddressByFileName |
场景资源建议使用 PackSeparately,因为场景通常较大,单独打包有利于增量更新。
五、资源构建
配置完成后,就可以进行资源构建了。
打开构建窗口:Window → YooAsset → AssetBundle Builder
5.1 构建参数详解
| 参数 | 说明 |
|---|---|
| Build Package | 选择要构建的资源包裹 |
| Build Pipeline | 构建管线(见下文) |
| Build Output | 构建输出目录,按平台自动划分 |
| Build Version | 资源包版本号 |
| Clear Build Cache | 清理构建缓存(不勾选则增量打包,速度更快) |
| Use Asset Depend DB | 使用资源依赖数据库,大幅提升构建速度 |
| Encryption Services | 资源包加密类 |
| Compression | 压缩方式 |
| File Name Style | 输出文件名样式 |
| Copy Buildin File Option | 首包资源拷贝方式 |
构建管线选择:
| 管线 | 说明 | 推荐场景 |
|---|---|---|
| EditorSimulateBuildPipeline | 编辑器模拟构建,不生成 Bundle | 编辑器开发调试 |
| BuiltinBuildPipeline | 内置构建管线 | Unity 2020 及以下 |
| ScriptableBuildPipeline | 可编程构建管线 | Unity 2021.3+ 推荐 |
| RawFileBuildPipeline | 原生文件构建管线 | 非Unity识别的资源(如 FMOD bank 文件) |
文件名样式:
| 样式 | 说明 |
|---|---|
| HashName | 哈希值命名(最安全,推荐) |
| BundleName | 资源包名 |
| BundleName_HashName | 资源包名 + 哈希值 |
首包资源拷贝方式:
| 方式 | 说明 |
|---|---|
| None | 不拷贝任何文件 |
| ClearAndCopyAll | 清空后拷贝全部 |
| ClearAndCopyByTags | 清空后按标签拷贝 |
| OnlyCopyAll | 不清空,直接拷贝全部 |
| OnlyCopyByTags | 不清空,按标签拷贝 |
5.2 执行构建
- 选择 Build Package(如 DefaultPackage)
- 选择 Build Pipeline(推荐 ScriptableBuildPipeline)
- 填写 Build Version(如 1.0.0)
- 勾选 Use Asset Depend DB
- 选择 Compression(推荐 LZ4)
- 选择 File Name Style(推荐 HashName)
- 选择 Copy Buildin File Option(如 ClearAndCopyByTags,配合标签实现首包分包)
- 点击 构建 按钮
构建成功后,输出目录结构:
css
BuildOutput/
└── [平台名]/
└── [版本号]/
├── PackageManifest_DefaultPackage.version 版本文件
├── PackageManifest_DefaultPackage_xxx.hash 清单哈希
├── PackageManifest_DefaultPackage_xxx.json 清单(JSON格式,可读)
├── PackageManifest_DefaultPackage_xxx.bytes 清单(二进制格式,运行时使用)
├── xxx.bundle 资源包文件
└── PackageManifest_DefaultPackage_xxx.report 构建报告
5.3 资源加密(可选)
如果需要对资源包进行加密。以下是一个简单的 XOR 加密示例:
csharp
/// <summary>
/// XOR 解密文件流,读取时自动对每个字节执行异或解密
/// </summary>
public class TestBundleStream : FileStream
{
public const byte KEY = 64;
public TestBundleStream(string path, FileMode mode, FileAccess access, FileShare share) : base(path, mode, access, share)
{
}
public TestBundleStream(string path, FileMode mode) : base(path, mode)
{
}
public override int Read(byte[] array, int offset, int count)
{
var index = base.Read(array, offset, count);
for (int i = 0; i < array.Length; i++)
{
array[i] ^= KEY;
}
return index;
}
}
/// <summary>
/// 流模式 XOR 加密器,对 TestRes3 目录的 Bundle 进行逐字节异或加密
/// </summary>
public class TestFileStreamEncryption : IBundleEncryptor
{
public BundleEncryptResult Encrypt(BundleEncryptArgs fileInfo)
{
// 说明:对TestRes3资源目录进行加密
if (fileInfo.BundleName.Contains("_testres3_"))
{
var fileData = File.ReadAllBytes(fileInfo.FilePath);
for (int i = 0; i < fileData.Length; i++)
{
fileData[i] ^= TestBundleStream.KEY;
}
return new BundleEncryptResult(true, fileData);
}
else
{
return new BundleEncryptResult(false, null);
}
}
}
加密支持三种加载方式:
- LoadFromFileOffset:通过文件偏移解密加载(性能最好)
- LoadFromMemory:通过内存解密加载
- LoadFromStream:通过文件流解密加载
六、资源部署
构建完成后,需要将补丁包部署到 CDN 服务器。
6.1 部署目录结构
python
CDN/
├── android/
│ ├── v1.0/ APP版本(不是资源版本)
│ │ ├── PackageManifest_DefaultPackage.version
│ │ ├── PackageManifest_DefaultPackage_xxx.hash
│ │ ├── PackageManifest_DefaultPackage_xxx.bytes
│ │ └── xxx.bundle
│ └── v2.0/
└── iphone/
├── v1.0/
└── v2.0/
关键理解 :
v1.0是 APP 版本而非资源版本。在不更换安装包的情况下,每次生成的补丁文件覆盖到同一 APP 版本目录下即可。第二次上传会覆盖第一次的版本记录文件。
6.2 本地测试
开发阶段可以在本地搭建 Web 服务器进行测试:
bash
# 使用 Python 快速搭建本地服务器
python -m http.server 8080
将构建产物拷贝到服务器目录下,即可通过 http://127.0.0.1:8080/CDN/Android/v1.0 访问资源。
七、运行时初始化
这是最关键的环节,YooAsset 提供了多种运行模式,适配不同的业务场景。
7.1 初始化资源系统
csharp
using YooAsset;
// 初始化资源系统(全局只需调用一次)
YooAssets.Initialize();
// 创建资源包对象
var package = YooAssets.CreatePackage("DefaultPackage");
// 设置为默认资源包(设置后可使用 YooAssets 的快捷加载接口)
YooAssets.SetDefaultPackage(package);
7.2 编辑器模拟模式(EditorSimulateMode)
适用场景:编辑器开发调试,不需要构建资源包即可模拟真实环境。
csharp
private IEnumerator InitPackage()
{
var buildResult = EditorSimulateModeHelper.SimulateBuild("DefaultPackage");
var packageRoot = buildResult.PackageRootDirectory;
// 创建资源包裹类
var package = YooAssets.TryGetPackage("DefaultPackage");
if (package == null)
{
package = YooAssets.CreatePackage("DefaultPackage");
}
InitializationOperation initializationOperation = null;
var createParameters = new EditorSimulateModeParameters();
initializationOperation = package.InitializeAsync(createParameters);
yield return initializationOperation;
}
注意:该模式只在编辑器下生效,每次启动都会执行一次模拟构建,资源量大时会有卡顿。
7.3 单机运行模式(OfflinePlayMode)
适用场景:不需要热更新的单机游戏。
csharp
private IEnumerator InitPackage()
{
// 创建资源包裹类
var package = YooAssets.TryGetPackage("DefaultPackage");
if (package == null)
{
package = YooAssets.CreatePackage("DefaultPackage");
}
var fileSystemParams = FileSystemParameters.CreateDefaultBuildinFileSystemParameters();
var createParameters = new OfflinePlayModeParameters();
createParameters.BuildinFileSystemParameters = fileSystemParams;
var initOperation = package.InitializeAsync(createParameters);
yield return initOperation;
if (initOperation.Status == EOperationStatus.Succeed)
Debug.Log("资源包初始化成功!");
else
Debug.LogError($"资源包初始化失败:{initOperation.Error}");
}
7.4 联机运行模式(HostPlayMode)
适用场景:需要热更新的网络游戏,这是最常用的模式。
csharp
private IEnumerator InitPackage()
{
string defaultHostServer = "http://127.0.0.1/CDN/Android/v1.0";
string fallbackHostServer = "http://127.0.0.1/CDN/Android/v1.0";
IRemoteServices remoteServices = new RemoteServices(defaultHostServer, fallbackHostServer);
var cacheFileSystemParams = FileSystemParameters.CreateDefaultCacheFileSystemParameters(remoteServices);
var buildinFileSystemParams = FileSystemParameters.CreateDefaultBuildinFileSystemParameters();
var createParameters = new HostPlayModeParameters();
createParameters.BuildinFileSystemParameters = buildinFileSystemParams;
createParameters.CacheFileSystemParameters = cacheFileSystemParams;
var initOperation = package.InitializeAsync(createParameters);
yield return initOperation;
if (initOperation.Status == EOperationStatus.Succeed)
Debug.Log("资源包初始化成功!");
else
Debug.LogError($"资源包初始化失败:{initOperation.Error}");
}
/// <summary>
/// 远端资源地址查询服务类
/// </summary>
private class RemoteServices : IRemoteServices
{
private readonly string _defaultHostServer;
private readonly string _fallbackHostServer;
public RemoteServices(string defaultHostServer, string fallbackHostServer)
{
_defaultHostServer = defaultHostServer;
_fallbackHostServer = fallbackHostServer;
}
string IRemoteServices.GetRemoteMainURL(string fileName)
{
return $"{_defaultHostServer}/{fileName}";
}
string IRemoteServices.GetRemoteFallbackURL(string fileName)
{
return $"{_fallbackHostServer}/{fileName}";
}
}
7.5 获取资源版本(必须步骤)
初始化成功后,必须获取资源版本号:
csharp
private IEnumerator RequestPackageVersion()
{
var package = YooAssets.GetPackage("DefaultPackage");
var operation = package.RequestPackageVersionAsync();
yield return operation;
if (operation.Status == EOperationStatus.Succeed)
{
string packageVersion = operation.PackageVersion;
Debug.Log($"资源版本:{packageVersion}");
}
else
{
Debug.LogError($"获取资源版本失败:{operation.Error}");
}
}
7.7 更新资源清单(必须步骤)
获取版本号后,更新资源清单:
csharp
private IEnumerator UpdatePackageManifest(string packageVersion)
{
var package = YooAssets.GetPackage("DefaultPackage");
var operation = package.UpdatePackageManifestAsync(packageVersion);
yield return operation;
if (operation.Status == EOperationStatus.Succeed)
{
Debug.Log("资源清单更新成功!");
}
else
{
Debug.LogError($"资源清单更新失败:{operation.Error}");
}
}
7.8 完整初始化流程封装
将上述步骤封装为一个完整的初始化管理器:
csharp
using System.Collections;
using UnityEngine;
using YooAsset;
public class YooAssetInitManager : MonoBehaviour
{
[Header("CDN 配置")]
[SerializeField] private string defaultHostServer = "http://127.0.0.1/CDN/Android/v1.0";
[SerializeField] private string fallbackHostServer = "http://127.0.0.1/CDN/Android/v1.0";
[Header("下载配置")]
[SerializeField] private int downloadingMaxNumber = 10;
[SerializeField] private int failedTryAgain = 3;
private ResourcePackage _package;
private void Start()
{
StartCoroutine(InitYooAsset());
}
private IEnumerator InitYooAsset()
{
// 1. 初始化资源系统
YooAssets.Initialize();
_package = YooAssets.CreatePackage("DefaultPackage");
YooAssets.SetDefaultPackage(_package);
// 2. 根据运行环境选择初始化模式
InitializationOperation initOperation;
#if UNITY_EDITOR
// 编辑器模拟模式
var buildResult = EditorSimulateModeHelper.SimulateBuild("DefaultPackage");
var editorParams = new EditorSimulateModeParameters();
editorParams.EditorFileSystemParameters =
FileSystemParameters.CreateDefaultEditorFileSystemParameters(buildResult.PackageRootDirectory);
initOperation = _package.InitializeAsync(editorParams);
#else
// 联机运行模式
var remoteServices = new RemoteServices(defaultHostServer, fallbackHostServer);
var hostParams = new HostPlayModeParameters();
hostParams.BuildinFileSystemParameters =
FileSystemParameters.CreateDefaultBuildinFileSystemParameters();
hostParams.CacheFileSystemParameters =
FileSystemParameters.CreateDefaultCacheFileSystemParameters(remoteServices);
initOperation = _package.InitializeAsync(hostParams);
#endif
yield return initOperation;
if (initOperation.Status != EOperationStatus.Succeed)
{
Debug.LogError($"初始化失败:{initOperation.Error}");
yield break;
}
// 3. 获取资源版本
var versionOp = _package.RequestPackageVersionAsync();
yield return versionOp;
if (versionOp.Status != EOperationStatus.Succeed)
{
Debug.LogError($"获取版本失败:{versionOp.Error}");
yield break;
}
// 4. 更新资源清单
var manifestOp = _package.UpdatePackageManifestAsync(versionOp.PackageVersion);
yield return manifestOp;
if (manifestOp.Status != EOperationStatus.Succeed)
{
Debug.LogError($"更新清单失败:{manifestOp.Error}");
yield break;
}
// 5. 下载更新资源
yield return DownloadResources();
Debug.Log("YooAsset 初始化完成!");
// TODO: 进入游戏主逻辑
}
private IEnumerator DownloadResources()
{
var downloader = _package.CreateResourceDownloader(downloadingMaxNumber, failedTryAgain);
if (downloader.TotalDownloadCount == 0)
{
Debug.Log("没有需要更新的资源");
yield break;
}
Debug.Log($"需要下载:{downloader.TotalDownloadCount} 个文件," +
$"总大小:{downloader.TotalDownloadBytes / 1024f / 1024f:F2} MB");
downloader.OnDownloadProgressCallback = OnDownloadProgress;
downloader.OnDownloadErrorCallback = OnDownloadError;
downloader.BeginDownload();
yield return downloader;
if (downloader.Status == EOperationStatus.Succeed)
Debug.Log("资源下载完成!");
else
Debug.LogError("资源下载失败!");
}
private void OnDownloadProgress(int totalCount, int currentCount, long totalBytes, long currentBytes)
{
float progress = (float)currentCount / totalCount;
Debug.Log($"下载进度:{progress:P0} ({currentCount}/{totalCount})");
}
private void OnDownloadError(string fileName, string error)
{
Debug.LogError($"下载失败:{fileName},错误:{error}");
}
}
八、资源加载
初始化完成后,就可以加载资源了。YooAsset 提供了丰富的加载接口。
8.1 加载方法一览
| 方法 | 说明 |
|---|---|
LoadAssetAsync<T>() |
异步加载资源对象 |
LoadAssetSync<T>() |
同步加载资源对象 |
LoadSubAssetsAsync<T>() |
异步加载子资源对象 |
LoadSubAssetsSync<T>() |
同步加载子资源对象 |
LoadAllAssetsAsync<T>() |
异步加载资源包内所有对象 |
LoadAllAssetsSync<T>() |
同步加载资源包内所有对象 |
LoadSceneAsync() |
异步加载场景 |
LoadRawFileAsync() |
异步获取原生文件 |
| `LoadRawFileSync()`` | 同步获取原生文件 |
8.2 资源定位约定
未开启可寻址模式:location 代表资源完整路径
csharp
package.LoadAssetAsync<AudioClip>("Assets/GameRes/Audio/bgMusic");
开启可寻址模式:location 代表可寻址地址(也支持完整路径)
csharp
// 可寻址地址(AddressRule 为 AddressByFileName 时)
package.LoadAssetAsync<AudioClip>("bgMusic");
8.3 异步加载的三种方式
委托方式:
csharp
void Start()
{
AssetHandle handle = package.LoadAssetAsync<AudioClip>("Assets/GameRes/Audio/bgMusic");
handle.Completed += OnLoadCompleted;
}
void OnLoadCompleted(AssetHandle handle)
{
AudioClip audioClip = handle.AssetObject as AudioClip;
}
协程方式:
csharp
IEnumerator Start()
{
AssetHandle handle = package.LoadAssetAsync<AudioClip>("Assets/GameRes/Audio/bgMusic");
yield return handle;
AudioClip audioClip = handle.AssetObject as AudioClip;
}
Task 方式:
csharp
async void Start()
{
AssetHandle handle = package.LoadAssetAsync<AudioClip>("Assets/GameRes/Audio/bgMusic");
await handle.Task;
AudioClip audioClip = handle.AssetObject as AudioClip;
}
8.4 预制体加载与实例化
csharp
IEnumerator LoadPrefab()
{
AssetHandle handle = package.LoadAssetAsync<GameObject>("Assets/GameRes/Panel/login");
yield return handle;
// 直接通过句柄实例化
GameObject instance = handle.InstantiateSync();
Debug.Log($"实例化对象:{instance.name}");
}
8.5 场景加载
csharp
IEnumerator LoadScene()
{
string location = "Assets/GameRes/Scene/Login";
var sceneMode = LoadSceneMode.Single;
var physicsMode = LocalPhysicsMode.None;
bool suspendLoad = false;
SceneHandle handle = package.LoadSceneAsync(location, sceneMode, physicsMode, suspendLoad);
yield return handle;
Debug.Log($"场景加载完成:{handle.Scene.name}");
}
注意:加载新的主场景时,会自动释放之前加载的主场景及附加场景。
8.6 子对象加载(图集精灵)
csharp
IEnumerator LoadSubAssets()
{
SubAssetsHandle handle = package.LoadSubAssetsAsync<Sprite>("Assets/GameRes/UI/Atlas/login");
yield return handle;
Sprite sprite = handle.GetSubAssetObject<Sprite>("spriteName");
Debug.Log($"精灵名称:{sprite.name}");
}
8.7 原生文件加载
原生文件必须使用 RawFileBuildPipeline 构建:
csharp
IEnumerator LoadRawFile()
{
string location = "Assets/GameRes/wwise/init.bnk";
RawFileHandle handle = package.LoadRawFileAsync(location);
yield return handle;
byte[] fileData = handle.GetRawFileData(); // 二进制数据
string fileText = handle.GetRawFileText(); // 文本数据
string filePath = handle.GetRawFilePath(); // 文件路径
}
8.8 热更脚本加载
适用于 Lua、ILRuntime、HybridCLR 等热更方案,将热更 DLL 的后缀改为 .bytes:
csharp
IEnumerator LoadHotfixDll()
{
AssetHandle handle = package.LoadAssetAsync<TextAsset>("Assets/GameRes/Hotfix");
yield return handle;
TextAsset textAsset = handle.AssetObject as TextAsset;
// textAsset.bytes 二进制数据
// textAsset.text 文本数据
}
8.9 通过标签获取资源信息
csharp
void GetAssetsByTag(string tag)
{
AssetInfo[] assetInfos = package.GetAssetInfos(tag);
foreach (var assetInfo in assetInfos)
{
Debug.Log($"资源路径:{assetInfo.AssetPath}");
}
}
九、资源卸载
正确的资源卸载是避免内存泄漏的关键。
9.1 释放资源句柄
csharp
IEnumerator LoadAndRelease()
{
AssetHandle handle = package.LoadAssetAsync<AudioClip>("Assets/GameRes/Audio/bgMusic");
yield return handle;
// 使用资源...
AudioClip clip = handle.AssetObject as AudioClip;
// 不再使用时释放句柄(减少引用计数)
handle.Release();
}
重要 :每个加载方法都会返回一个资源句柄,不再使用时必须调用
Release(),否则会造成资源泄漏。
9.2 卸载未使用的资源
csharp
// 卸载所有引用计数为零的资源包
// 建议在切换场景后调用,或使用定时器定期执行
private IEnumerator UnloadUnusedAssets()
{
var package = YooAssets.GetPackage("DefaultPackage");
var operation = package.UnloadUnusedAssetsAsync();
operation.WaitForAsyncComplete(); // 支持同步等待
yield return operation;
}
9.3 强制卸载所有资源
csharp
// 谨慎使用!仅在合适的时机调用
private IEnumerator ForceUnloadAllAssets()
{
var package = YooAssets.GetPackage("DefaultPackage");
var operation = package.UnloadAllAssetsAsync();
yield return operation;
}
9.4 场景卸载
csharp
IEnumerator UnloadScene(SceneHandle sceneHandle)
{
// 卸载成功后自动释放 handle 的引用计数
var operation = sceneHandle.UnloadAsync();
yield return operation;
}
十、资源热更新
联机运行模式下,资源热更新是核心能力。
10.1 创建下载器
YooAsset 提供三种下载器创建方式:
csharp
// 1. 下载当前版本所有需要更新的资源
var downloader = package.CreateResourceDownloader(downloadingMaxNumber, failedTryAgain);
// 2. 按标签下载资源
var downloader = package.CreateResourceDownloader(new string[] { "level1", "level2" }, downloadingMaxNumber, failedTryAgain);
// 3. 按指定资源列表下载
AssetInfo[] assetInfos = ...;
var downloader = package.CreateBundleDownloader(assetInfos, downloadingMaxNumber, failedTryAgain);
参数说明:
downloadingMaxNumber:同时下载的最大文件数failedTryAgain:下载失败重试次数
10.2 完整下载流程
csharp
IEnumerator DownloadResources()
{
var package = YooAssets.GetPackage("DefaultPackage");
var downloader = package.CreateResourceDownloader(10, 3);
// 没有需要下载的资源
if (downloader.TotalDownloadCount == 0)
{
Debug.Log("已是最新版本");
yield break;
}
// 显示下载信息
int totalCount = downloader.TotalDownloadCount;
long totalBytes = downloader.TotalDownloadBytes;
Debug.Log($"需要下载 {totalCount} 个文件,共 {totalBytes / 1024f / 1024f:F2} MB");
// 注册回调
downloader.OnDownloadProgressCallback = (totalCount, currentCount, totalBytes, currentBytes) =>
{
float progress = (float)currentCount / totalCount;
// 更新 UI 进度条
};
downloader.OnDownloadErrorCallback = (fileName, error) =>
{
Debug.LogError($"下载失败:{fileName},{error}");
};
downloader.OnDownloadFinishCallback = (succeed) =>
{
Debug.Log(succeed ? "下载完成" : "下载失败");
};
// 开始下载
downloader.BeginDownload();
yield return downloader;
if (downloader.Status == EOperationStatus.Succeed)
{
Debug.Log("资源更新完成!");
}
}
10.3 下载器合并
可以将同一 Package 下的多个下载器合并,避免重复下载:
csharp
var downloader1 = package.CreateResourceDownloader("level_tag", 10, 3);
var downloader2 = package.CreateBundleDownloader(assetInfos, 10, 3);
// 合并下载器
downloader1.Combine(downloader2);
// 只需开启一个下载器
downloader1.BeginDownload();
10.4 检测资源是否需要下载
csharp
bool isNeedDownload = package.IsNeedDownloadFromRemote("Assets/GameRes/Scene/Login");
十一、销毁资源包
在游戏退出或需要重新初始化时,需要销毁资源包:
csharp
private IEnumerator DestroyPackage()
{
var package = YooAssets.GetPackage("DefaultPackage");
// 先销毁资源包
DestroyOperation operation = package.DestroyAsync();
yield return operation;
// 然后移除资源包
if (YooAssets.RemovePackage(package))
{
Debug.Log("资源包移除成功!");
}
}