16 KiB
16 KiB
Resource
模块概述
Resource 模块基于 YooAsset 封装了资源包初始化、版本管理、资源查询、同步/异步加载、实例化、缓存回收与低内存处理能力。
它是多个模块的底层依赖:
UI:加载窗口和 Widget 预制体Audio:加载音频资源Localization:加载语言表GameObjectPool:加载池对象原始 prefabScene:主场景与附加场景的资源加载链路
从当前实现来看,ResourceService 还额外提供了:
- 多包管理
- 同一路径并发加载合并
- 资源对象池缓存
- 低内存时统一回收入口
- 下载、版本、清单更新能力
快速开始
最少步骤
- 在场景中挂载
ResourceComponent - 配置
PlayMode、默认包名和解密服务名 - 游戏启动时调用
InitPackageAsync - 通过
GameApp.Resource或AppServices.Require<IResourceService>()加载资源
最小示例
using AlicizaX;
using UnityEngine;
public sealed class ResourceQuickStart : MonoBehaviour
{
private async void Start()
{
await GameApp.Resource.InitPackageAsync();
Texture2D icon = await GameApp.Resource.LoadAssetAsync<Texture2D>("UI/Common/Icon");
Debug.Log(icon != null);
}
}
架构说明
ResourceComponent
└─ ResourceService
├─ YooAsset PackageMap
├─ AssetInfo Cache
├─ AssetObject Pool
├─ Loading Operation Map
├─ Download / Version / Manifest
└─ ResourceExtComponent / AssetsReference
关键协作关系
ResourceComponent:负责注册服务、配置运行模式并接管低内存回收节奏ResourceService:负责包初始化、加载、缓存和释放AssetsReference:负责把实例对象与源资源句柄关联起来,在对象销毁时自动释放源资源引用ResourceExtComponent:负责追踪资源绑定目标并分帧回收无引用资源
运行模式
当前实现支持以下 PlayMode:
EditorSimulateModeOfflinePlayModeHostPlayModeWebPlayMode
不同模式决定初始化参数:
- EditorSimulateMode:编辑器模拟构建目录
- OfflinePlayMode:本地内置资源
- HostPlayMode:内置资源 + 缓存 + 远端下载
- WebPlayMode:Web 环境资源拉取方式
并发加载合并机制
ResourceService 内部使用 _assetLoadingOperations 合并同一路径的并发加载请求。
这意味着:
- 多个系统同时加载同一个资源时,不会重复发起多次底层加载
- 后来的请求会等待第一条加载链路完成,再复用结果
这对 UI 和公共图集资源特别有价值。
核心类与接口
IResourceService
该接口同时覆盖:
- 包初始化
- 包版本与清单管理
- 资源存在性检查
- 同步/异步加载
- 场景对象实例化
- 缓存清理
- 低内存回收
ResourceComponent
它是 Resource 模块在场景中的入口组件,主要职责:
- 注册
ResourceService - 配置默认包名
PackageName - 设置运行模式
PlayMode - 设置解密服务名
decryptionServices - 设置资源对象池参数
- 接管
Application.lowMemory事件
ResourceService
从当前实现看,ResourceService 维护以下关键状态:
DefaultPackageNamePlayModePackageMap_assetInfoMap_assetLoadingOperations_assetPool
AssetsReference
这是实例对象与源资源句柄的桥接组件。
作用:
- 当你通过
LoadGameObject/LoadGameObjectAsync实例化资源时 - 框架会在实例对象上自动挂一个
AssetsReference - 当该实例对象销毁时,对应源资源引用会自动
UnloadAsset
这就是为什么:
LoadGameObject返回的实例对象通常不需要你手动UnloadAsset
ResourceExtComponent
主要用于:
- 追踪已绑定给 UI、Sprite 或其他目标对象的资源
- 自动清理已经失去引用的资源包装对象
- 以分帧方式做回收,降低一次性遍历成本
API 参考
以下内容按使用频率分组说明。
一、初始化与运行配置
void Initialize()
- 参数:无
- 返回值:无
- 说明:初始化 YooAsset、默认包与资产池
- 备注:通常由
ResourceComponent.Start()自动调用
UniTask<bool> InitPackageAsync(string packageName = "", string hostServerURL = "", string fallbackHostServerURL = "")
- 可选参数:
packageName - 可选参数:
hostServerURL - 可选参数:
fallbackHostServerURL - 返回值:
UniTask<bool>
说明:
- 初始化指定资源包
- 如果
packageName为空,则使用DefaultPackageName - 在 Host/Web 模式下通常需要传入远端地址
异常与边界:
- 如果同一个包正在初始化或已成功初始化,会记录错误并直接返回失败结果
- 初始化失败时任务会抛出异常
string DefaultPackageName { get; set; }
- 说明:默认资源包名
- 推荐:整个项目统一默认包名约定
EPlayMode PlayMode { get; set; }
- 说明:资源系统运行模式
- 注意:Editor 与真机配置往往不同
string DecryptionServices { get; set; }
- 说明:解密服务类型名
- 注意:该值应是可被反射创建的类型全名
bool AutoUnloadBundleWhenUnused { get; set; }
- 说明:初始化参数中的自动卸载选项
二、版本与下载管理
string GetPackageVersion(string customPackageName = "")
- 可选参数:
customPackageName - 返回值:包版本字符串
RequestPackageVersionOperation RequestPackageVersionAsync(bool appendTimeTicks = false, int timeout = 60, string customPackageName = "")
- 可选参数:
appendTimeTicks - 可选参数:
timeout - 可选参数:
customPackageName - 返回值:
RequestPackageVersionOperation
UpdatePackageManifestOperation UpdatePackageManifestAsync(string packageVersion, int timeout = 60, string customPackageName = "")
- 必填参数:
packageVersion - 可选参数:
timeout - 可选参数:
customPackageName - 返回值:
UpdatePackageManifestOperation
ResourceDownloaderOperation CreateResourceDownloader(string customPackageName = "")
- 可选参数:
customPackageName - 返回值:
ResourceDownloaderOperation
说明:
- 会使用当前
DownloadingMaxNum和FailedTryAgain
三、资源查询
HasAssetResult HasAsset(string location, string packageName = "")
- 必填参数:
location - 可选参数:
packageName - 返回值:
HasAssetResult
说明:
- 用于判断资源是否存在、是否在本地、是否需要远端下载
注意:
- 如果
location为空会抛异常
bool CheckLocationValid(string location, string packageName = "")
- 必填参数:
location - 可选参数:
packageName - 返回值:
bool
AssetInfo GetAssetInfo(string location, string packageName = "")
- 必填参数:
location - 可选参数:
packageName - 返回值:
AssetInfo
说明:
- 内部带有
_assetInfoMap缓存 - 同一路径重复获取成本较低
四、同步加载
T LoadAsset<T>(string location, string packageName = "") where T : UnityEngine.Object
- 必填参数:
location - 可选参数:
packageName - 返回值:
T - 泛型约束:
T : UnityEngine.Object
说明:
- 同步加载资源
- 首次加载会进入资源池缓存
- 重复获取相同资源时会优先从
_assetPool中复用
GameObject LoadGameObject(string location, Transform parent = null, string packageName = "")
- 必填参数:
location - 可选参数:
parent - 可选参数:
packageName - 返回值:
GameObject
说明:
- 同步加载 prefab 并实例化
- 实例对象会自动挂
AssetsReference - 销毁实例时会自动释放源资源引用
五、异步加载
UniTask<T> LoadAssetAsync<T>(string location, CancellationToken cancellationToken = default, string packageName = "") where T : UnityEngine.Object
- 必填参数:
location - 可选参数:
cancellationToken - 可选参数:
packageName - 返回值:
UniTask<T>
UniTask<GameObject> LoadGameObjectAsync(string location, Transform parent = null, CancellationToken cancellationToken = default, string packageName = "")
- 必填参数:
location - 可选参数:
parent - 可选参数:
cancellationToken - 可选参数:
packageName - 返回值:
UniTask<GameObject>
六、回调式加载
UniTask LoadAssetAsync(string location, int priority, LoadAssetCallbacks loadAssetCallbacks, object userData, string packageName = "")
UniTask LoadAssetAsync(string location, Type assetType, int priority, LoadAssetCallbacks loadAssetCallbacks, object userData, string packageName = "")
LoadAssetCallbacks 中的回调签名:
- 成功:
(string assetName, object asset, float duration, object userData) - 失败:
(string assetName, LoadResourceStatus status, string errorMessage, object userData) - 进度:
(string assetName, float progress, object userData)
适合:
- 旧式回调链
- 想同时观察进度与错误
- 不方便直接
await的场景
七、句柄接口
AssetHandle LoadAssetSyncHandle<T>(string location, string packageName = "") where T : UnityEngine.Object
AssetHandle LoadAssetAsyncHandle<T>(string location, string packageName = "") where T : UnityEngine.Object
适合:
- 需要直接管理底层句柄
- 需要和 YooAsset 句柄 API 配合
八、释放与回收
void UnloadAsset(object asset)
- 必填参数:
asset - 返回值:无
说明:
- 本质上是把资源对象从
_assetPool中Unspawn
void UnloadUnusedAssets()
- 返回值:无
说明:
- 释放资源对象池中未使用的对象
- 并对所有已成功初始化的包执行
UnloadUnusedAssetsAsync
void ForceUnloadAllAssets()
- 返回值:无
说明:
- 强制卸载所有资源
- WebGL 下会直接警告并退出
void ForceUnloadUnusedAssets(bool performGCCollect)
- 必填参数:
performGCCollect - 返回值:无
说明:
- 委托给
ResourceComponent控制实际的系统资源回收时机
常见用法
1. 初始化默认包
using AlicizaX;
using AlicizaX.Resource.Runtime;
using UnityEngine;
public sealed class ResourceInitExample : MonoBehaviour
{
private async void Start()
{
bool ok = await GameApp.Resource.InitPackageAsync();
Debug.Log($"Init default package success: {ok}");
}
}
2. 初始化远端包
using AlicizaX;
using UnityEngine;
public sealed class HostModeInitExample : MonoBehaviour
{
private async void Start()
{
await GameApp.Resource.InitPackageAsync(
packageName: "DefaultPackage",
hostServerURL: "https://cdn.example.com/game",
fallbackHostServerURL: "https://backup.example.com/game");
}
}
3. 同步加载配置资源
using AlicizaX;
using UnityEngine;
public sealed class SyncLoadExample : MonoBehaviour
{
private void Start()
{
TextAsset config = GameApp.Resource.LoadAsset<TextAsset>("Config/GameBalance");
Debug.Log(config != null ? config.text : "Config missing");
}
}
4. 异步加载图片资源
using AlicizaX;
using UnityEngine;
public sealed class AsyncLoadExample : MonoBehaviour
{
private async void Start()
{
Sprite sprite = await GameApp.Resource.LoadAssetAsync<Sprite>("UI/Common/Atlas/Icon_Star");
Debug.Log(sprite != null);
}
}
5. 加载并实例化 GameObject
using AlicizaX;
using UnityEngine;
public sealed class InstantiateExample : MonoBehaviour
{
private GameObject _hero;
private async void Start()
{
_hero = await GameApp.Resource.LoadGameObjectAsync("Character/Hero.prefab", transform);
}
private void OnDestroy()
{
if (_hero != null)
{
Destroy(_hero);
}
}
}
说明:
- 这里不需要对
_hero再调用UnloadAsset - 销毁实例时
AssetsReference会自动释放源资源引用
6. 使用回调式加载并监听进度
using AlicizaX;
using AlicizaX.Resource.Runtime;
using UnityEngine;
public sealed class CallbackLoadExample : MonoBehaviour
{
private async void Start()
{
var callbacks = new LoadAssetCallbacks(
(assetName, asset, duration, userData) =>
{
Debug.Log($"Load success: {assetName}, duration: {duration:F3}s");
},
(assetName, status, errorMessage, userData) =>
{
Debug.LogError($"Load failed: {assetName}, {status}, {errorMessage}");
},
(assetName, progress, userData) =>
{
Debug.Log($"Progress {assetName}: {progress:P0}");
});
await GameApp.Resource.LoadAssetAsync(
"UI/Common/Icon",
priority: 0,
loadAssetCallbacks: callbacks,
userData: null);
}
}
7. 下载资源版本并更新清单
using AlicizaX;
using UnityEngine;
public sealed class UpdateManifestExample : MonoBehaviour
{
private async void Start()
{
var versionOp = GameApp.Resource.RequestPackageVersionAsync();
await versionOp.ToUniTask();
if (versionOp.Status == YooAsset.EOperationStatus.Succeed)
{
string version = versionOp.PackageVersion;
var manifestOp = GameApp.Resource.UpdatePackageManifestAsync(version);
await manifestOp.ToUniTask();
Debug.Log($"Manifest updated to: {version}");
}
}
}
8. 创建下载器
using AlicizaX;
using UnityEngine;
public sealed class DownloaderExample : MonoBehaviour
{
private async void Start()
{
var downloader = GameApp.Resource.CreateResourceDownloader();
downloader.BeginDownload();
await downloader.ToUniTask();
Debug.Log($"Download status: {downloader.Status}");
}
}
运行行为细节
1. 重复加载同一路径会优先复用缓存
当前实现通过 _assetPool 保存 AssetObject,同一路径的资源会复用池内对象。
好处:
- 减少重复加载
- 降低频繁生成/销毁底层句柄的开销
2. 并发同路径加载会合并请求
如果同一路径资源正在加载中:
- 后续请求不会重复发起底层加载
- 会等待首个请求完成后再复用结果
3. LoadGameObject 与 LoadAsset<T> 的释放语义不同
LoadAsset<T>
- 返回的是资源对象本身
- 如有需要可以显式
UnloadAsset(asset)
LoadGameObject
- 返回的是实例化后的场景对象
- 源 prefab 资源句柄通过
AssetsReference绑定到实例对象 - 通常直接
Destroy(instance)即可
4. 低内存行为
触发 OnLowMemory() 时:
- 会调用
_forceUnloadUnusedAssetsAction - 实际清理由
ResourceComponent驱动
5. AssetInfo 有内部缓存
多次 GetAssetInfo 会命中 _assetInfoMap
适合:
- 频繁查询资源合法性
- UI 打开前做路径校验
6. 失败行为
以下情况通常会导致异常或错误日志:
- 资源路径为空
- 包不存在
- 资源定位无效
- 初始化失败
最佳实践
- 统一资源路径规范,避免大小写和目录命名混乱
- 默认优先异步加载
- 把“包初始化 / 版本更新 / 资源下载 / 资源使用”分成不同阶段处理
- UI、音频、图集等高复用资源尽量走缓存复用
- 对需要长期驻留的资源,业务层要明确生命周期,不要频繁反复加载
常见错误
1. 包未初始化就加载资源
现象:
- 资源查找失败
- 返回空对象或直接异常
规避:
- 确保启动流程中先执行
InitPackageAsync
2. 默认包名与实际包名不一致
现象:
- 路径正确但查不到资源
规避:
- 统一
DefaultPackageName - 多包场景显式传入
packageName
3. 手动 UnloadAsset 已实例化场景对象
现象:
- 语义不清晰
- 可能导致资源生命周期混乱
规避:
LoadGameObject返回的实例优先Destroy
4. 解密服务类型名错误
现象:
- 初始化时反射失败
规避:
DecryptionServices必须是可反射创建的完整类型名
性能注意事项
- 避免在主线程大量同步加载
- 常用资源优先缓存复用
- 合理设置:
AssetAutoReleaseIntervalAssetCapacityAssetExpireTimeAssetPriority
- 高并发异步加载时,优先复用路径和包配置,发挥内部合并机制优势