跳转至

ASSET PIPELINE · HOT UPDATE · OBJECT POOL

ZMAsset Framework

面向商业级 Unity 项目的模块化资源管理与热更新框架

将资源打包、加载、缓存、实例化、回收、热更新与远端寻址收拢进一套稳定管线。业务层只面对统一 API,开发环境和 AssetBundle 运行环境可以使用一致的资源路径与调用方式。

5 分钟接入 核心 API 教学课程

多模块打包 · 多线程热更 · 引用计数 · 大型对象池 · AES 加密

框架介绍

ZMAsset 是一套以模块为边界的 Unity 资源基础设施。它同时覆盖编辑器资源加载、AssetBundle 构建与加载、资源热更新、缓存引用管理及对象池复用,适合大厅与多个子游戏并存的大型项目。

项目 说明
核心命名空间 ZM.ZMAsset
推荐入口 ZMAssetZMAddressableAsset
异步模型 Cysharp.Threading.Tasks.UniTask
示例 Unity 版本 2022.3.62f3c1
加载模式 Editor / AssetBundle
热更模式 NoHot / Hot
平台支持 Android、iOS、Windows、macOS、WebGL 等

核心价值

同一份业务代码可以在 Editor 模式快速迭代,在发布环境切换到 AssetBundle;资源模块、热更清单、下载目录和缓存生命周期由框架统一管理。

核心能力

  • 多模块资源管线


    Hall、GameItem 或子游戏可以拥有独立的打包配置、Bundle 配置表、热更清单和运行时模块。

  • 热更新系统

    支持版本检测、差异文件计算、多线程下载、失败处理、更新进度和模块级热更。

  • 缓存与对象池

    内置对象池、资源缓存与引用计数,实例回收时可恢复 Transform、UI 与特效初始状态。

  • 资源加密

    构建阶段可启用 AES 加密,运行时从本地或热更目录读取后自动解密加载。

  • 远端可寻址加载

    通过 ZMAddressableAsset 使用资源路径与模块名加载本地或网络资源。

  • 可视化构建工具

    Unity 菜单提供模块配置、正式包、热更包和输出目录等完整编辑器工作流。

架构概览

业务代码
   ├── ZMAsset                 常规资源、实例化、场景、热更新
   └── ZMAddressableAsset      模块化远端可寻址资源
       ResourceManager        缓存、对象池、异步任务、引用计数
        ├── AssetBundleManager
        └── AddressableAssetSystem
       HotAssetsManager       版本检查、清单对比、多线程下载
 Editor Assets / StreamingAssets / PersistentData / Remote Server

门面优先

日常业务代码建议只调用 ZMAssetZMAddressableAssetResourceManagerAssetBundleManagerHotAssetsManager 属于框架实现层,除扩展框架外不建议直接依赖。

快速接入

1. 创建全局配置

在 Project 窗口创建 AssetsBundleSettings

Create → AssetsBundleSettings

配置文件必须放入某个 Resources 目录,并保持资源名为:

AssetsBundleSettings.asset

框架通过 Resources.Load<BundleSettings>("AssetsBundleSettings") 获取它。

2. 配置运行模式

字段 作用 开发期建议
loadAssetType EditorAssetBundle 加载模式 Editor
bundleHotType 是否启用资源热更 NoHot
AssetBundleDownLoadUrl 热更服务器根地址 按部署环境填写
MAX_THREAD_COUNT 最大下载线程数 按目标平台测试后设置
ABSUFFIX Bundle 文件后缀 默认空字符串
bundleEncrypt.isEncrypt 是否开启 Bundle 加密 发布策略决定
bundleEncrypt.encryptKey AES 密钥 不要提交真实生产密钥
ZMAssetRootPath 框架相对 Assets 的路径 与实际安装目录一致

3. 初始化框架与模块

初始化应在首个资源 API 调用之前完成,并且在应用生命周期中只执行一次。

using Cysharp.Threading.Tasks;
using UnityEngine;
using ZM.ZMAsset;

public sealed class GameBootstrap : MonoBehaviour
{
    private async void Awake()
    {
        ZMAsset.InitFrameWork();

        bool ready = await ZMAsset.InitAssetsModule(BundleModuleName.Hall);
        if (!ready)
        {
            Debug.LogError("Hall 资源模块初始化失败");
            return;
        }

        Debug.Log("ZMAsset 已就绪");
    }
}

初始化顺序

热更项目应先完成目标模块的热更新,再调用 InitAssetsModule 建立 Bundle 配置映射,最后开始加载该模块资源。

4. 定义统一路径

建议集中维护以 Assets/ 开头的完整资源路径:

public static class AssetsPathConfig
{
    public const string GameData = "Assets/GameData/";
    public const string HallPrefabs = GameData + "Hall/Prefabs/";
    public const string HallTextures = GameData + "Hall/Textures/";
}

常用示例

同步实例化与释放

GameObject window = ZMAsset.InstantiateObject(
    AssetsPathConfig.HallPrefabs + "MainWindow.prefab",
    uiRoot
);

// 默认进入对象池;destroy: true 会销毁对应缓存。
ZMAsset.Release(window);

UniTask 异步实例化

AssetsRequest request = await ZMAsset.InstantiateObjectAsync(
    AssetsPathConfig.HallPrefabs + "RankItem.prefab",
    content,
    param1: rankId
);

GameObject item = request.obj;
int id = (int)request.param1;

// 使用完成后同时归还实例和请求对象。
request.Release();

预热对象池

await ZMAsset.PreLoadObjectAsync<GameObject>(
    AssetsPathConfig.HallPrefabs + "NoticeItem.prefab",
    count: 20
);

加载常见资源

Sprite icon = await ZMAsset.LoadSpriteAsync(
    AssetsPathConfig.HallTextures + "icon_coin.png"
);

Texture texture = await ZMAsset.LoadTextureAsync(
    AssetsPathConfig.HallTextures + "background.jpg"
);

AudioClip audio = ZMAsset.LoadAudio("Assets/GameData/Audio/click.wav");
TextAsset config = await ZMAsset.LoadTextAssetAsync("Assets/GameData/Cfg/hero.json");

加载场景

AsyncOperation operation = ZMAsset.LoadSceneAsync(
    "Assets/GameData/Battle/Scenes/Battle.unity",
    UnityEngine.SceneManagement.LoadSceneMode.Additive
);

Editor 模式下,目标场景需要加入 File → Build Settings → Scenes In Build;AssetBundle 模式不受此限制。

可寻址资源

AssetsRequest request = await ZMAddressableAsset.InstantiateAsyncFormPool(
    "Assets/GameData/GameItem/6013/6013.prefab",
    parent,
    BundleModuleName.AdressAsset
);

Texture texture = await ZMAddressableAsset.LoadTextureAsync(
    "Assets/GameData/GameItem/6001/huafei.png",
    BundleModuleName.AdressAsset
);

request.Release();

资源热更新

推荐流程

初始化 ZMAsset
  → 检查模块版本
  → 提示用户更新大小
  → 启动模块热更
  → 下载并校验文件
  → 初始化资源模块
  → 进入业务 World

检查更新大小

ZMAsset.CheckAssetsVersion(BundleModuleName.Hall, (needUpdate, sizeMb) =>
{
    if (!needUpdate)
    {
        EnterHall().Forget();
        return;
    }

    updateView.Show($"需要下载 {sizeMb:F2} MB 资源");
});

启动热更新

ZMAsset.HotAssets(
    BundleModuleName.Hall,
    startHotCallBack: module => Debug.Log($"开始更新:{module}"),
    hotFinish: module => OnHotFinished(module).Forget(),
    waiteDownLoad: module => Debug.Log($"等待用户确认下载:{module}"),
    isCheckAssetsVersion: true
);

private async UniTaskVoid OnHotFinished(string module)
{
    if (await ZMAsset.InitAssetsModule(module))
        EnterHall();
}

服务器目录约定

运行时代码按照以下结构请求模块清单:

{AssetBundleDownLoadUrl}/HotAssets/{ModuleName}/{ModuleName}AssetsHotManifest_{Platform}.json

例如:

https://cdn.example.com/HotAssets/Hall/HallAssetsHotManifest_Android.json

编辑器构建流程

打开可视化构建窗口:

ZM → Build BundleWindow
工具/入口 用途
Bundle Setting 配置加载、热更、平台、压缩、加密和下载参数
模块配置 定义模块的 Prefab、根目录、指定目录与源目录规则
正式包构建 生成 Bundle、配置表和内嵌资源
热更包构建 生成指定版本差异资源及热更清单
ZM/BundleFolder 打开 AssetBundle 输出目录
ZM/HotBundleFolder 打开热更包目录
ZM/PersistentFolder 打开运行时持久化目录

模块打包规则

BundleModuleData 支持多种资源归集方式:

  • prefabPathArr:以 Prefab 为构建单元。
  • rootFolderPathArr:按根目录子文件夹组织 Bundle。
  • signFolderPathArr:为指定目录配置明确的 Bundle 名称。
  • sourceFolderPathArr:纳入模块的源资源目录。

模块边界

建议按可独立进入、更新或卸载的业务域拆分模块,例如 HallPokerBattle。不要按文件类型把整个项目拆成 Texture、Prefab、Audio 三个超大公共模块。

ZMAsset API

初始化与模块

API 返回值 说明
InitFrameWork() void 创建框架实例、回收节点、热更与资源管理器
InitAssetsModule(string) UniTask<bool> 初始化指定 AssetBundle 模块
CheckAssetsVersion(string, Action<bool,float>) void 返回是否更新及更新体积
HotAssets(...) void 启动指定模块热更新流程
GetHotAssetsModule(string) HotAssetsModule 获取模块热更状态与高级控制对象

实例化与预加载

API 返回值 使用场景
InstantiateObject(path, parent) GameObject 同步实例化
InstantiateObject(path, parent, position, scale, rotation) GameObject 指定本地变换实例化
InstantiateObjectAsync(..., callback, ...) void 回调式异步实例化
InstantiateObjectAsync(path, parent, ...) UniTask<AssetsRequest> 推荐的可等待异步实例化
InstantiateObjectAndLoad(...) long 边下载边等待实例化,返回任务 ID
PreLoadObjct(path, count) void 同步预热对象池;API 名按源码拼写
PreLoadObjectAsync<T>(path, count) UniTask 异步预热对象池
RemoveObjectLoadCallBack(long) void 取消尚未回调的对象加载任务

资源加载

API 返回值 说明
PreLoadResource<T>(path) void 同步预加载非实例资源
PreLoadResourceAsync<T>(path) UniTask<T> 异步预加载资源
LoadSprite(path) Sprite 同步加载 Sprite
LoadSpriteAsync(path) UniTask<Sprite> 异步加载 Sprite;自动补 .png
LoadSpriteAsync(path, Image, ...) long 加载后直接赋给 Image,返回任务 ID
LoadTexture(path) Texture 同步加载 Texture;自动补 .jpg
LoadTextureAsync(path) UniTask<Texture> 异步加载 Texture;自动补 .jpg
LoadAudio(path) AudioClip 同步加载音频
LoadTextAsset(path) TextAsset 同步加载文本资源
LoadTextAssetAsync(path) UniTask<TextAsset> 异步加载文本资源
LoadScriptableObject<T>(path) T 加载 ScriptableObject
LoadAtlasSprite(atlasPath, spriteName) Sprite 从 Unity SpriteAtlas 加载 Sprite
LoadPNGAtlasSprite(atlasPath, spriteName) Sprite 从 TexturePacker 图集加载 Sprite
LoadSceneAsync(path, mode) AsyncOperation 异步加载场景

释放与清理

API 说明
Release(GameObject, bool destroy = false) 回收实例;destroytrue 时销毁缓存
Release(Texture) 释放 Texture,必须确认没有其他使用方
Release(AssetsRequest) 归还请求对象;通常直接调用 request.Release()
ClearAllAsyncLoadTask() 清理所有异步加载任务
ClearResourcesAssets(false) 清理对象池,并按引用计数选择性释放资源
ClearResourcesAssets(true) 深度清理全部框架实例与资源

ZMAddressableAsset API

API 返回值 说明
InstantiateAsyncFormPool(...) UniTask<AssetsRequest> 从指定可寻址模块实例化对象
LoadResourceAsync<T>(path, module) UniTask<T> 加载任意 Unity Object
PreLoadResourceAsync<T>(path, module) UniTask<T> 预加载模块资源
LoadSpriteAsync(path, module) UniTask<Sprite> 加载 Sprite
LoadTextureAsync(path, module) UniTask<Texture> 加载 Texture
LoadAudioAsync(path, module) UniTask<AudioClip> 加载 AudioClip
LoadTextAssetAsync(path, module) UniTask<TextAsset> 加载 TextAsset
LoadScriptableObject<T>(path, module) UniTask<T> 加载 ScriptableObject
Release(...) void 复用 ZMAsset 的统一释放流程

AssetsRequest

异步实例化返回的请求对象同时承载实例与透传参数:

成员 类型 说明
obj GameObject 加载并实例化得到的对象
param1 object 透传参数 1
param2 object 透传参数 2
param3 object 透传参数 3
Release() void 回收实例、清空参数并归还请求对象

建议使用 try/finally 保证释放:

AssetsRequest request = await ZMAsset.InstantiateObjectAsync(path, parent);
try
{
    Use(request.obj);
}
finally
{
    request?.Release();
}

对象池原始数据

框架通过原始数据组件使对象回收后恢复初始状态。

类型 恢复内容 编辑器入口
OriginData Transform、激活状态、Collider、Rigidbody Assets/生成原始数据
UIOriginData RectTransform、锚点、轴心、尺寸、粒子 Assets/生成UI原始数据
EffectOriginData Transform、粒子系统、TrailRenderer Assets/生成特效原始数据

选择目标 Prefab 后执行对应菜单,让框架记录初始状态。否则池中对象再次取出时,运行期间修改过的位置、显隐或特效状态可能被保留。

生命周期与内存策略

谁加载,谁释放

将资源所有权绑定到明确的 Window、业务模块或 World 生命周期。不要依赖 ClearResourcesAssets(true) 代替正常释放;深度清理更适合退出子游戏或重置大型业务域。

推荐策略:

  • 列表项、飘字和特效等高频对象:预热对象池,使用后 Release(GameObject)
  • 单次异步实例化:持有 AssetsRequest,结束时调用 request.Release()
  • 常驻 UI 图集或公共配置:在所属 World 进入时加载,World 退出时统一释放。
  • 子游戏切换:先停止异步请求,再执行非深度或深度清理。
  • Texture:只有明确不存在共享引用时才单独释放。

常见问题

为什么 BundleSettings.Instance 是 null?

确认 AssetsBundleSettings.asset 位于 Resources 目录内,并且文件名没有改变。框架使用固定名称 AssetsBundleSettings 加载配置。

Editor 能加载,打包后找不到资源?

检查资源是否加入正确模块、完整路径大小写是否一致、模块配置表是否随包发布,以及启动阶段是否调用了 InitAssetsModule

为什么热更完成后仍然读取旧资源?

确认热更完成回调后重新初始化了对应资源模块;同时检查服务器清单平台名、模块名和 Bundle 后缀是否与客户端配置一致。

对象池里的 UI 或特效状态不正确?

为 Prefab 生成对应的 UIOriginDataEffectOriginData,并确认新增子节点或组件后重新生成原始数据。

什么时候使用 ZMAddressableAsset?

当资源由独立模块管理、可能位于远端且需要通过模块名寻址时使用。普通内嵌或已初始化 AssetBundle 资源优先使用 ZMAsset

异步回调已经不需要了怎么办?

保存回调式加载返回的任务 ID,并调用 RemoveObjectLoadCallBack(loadId);切换 World 前也可以调用 ClearAllAsyncLoadTask() 做统一收尾。

发布前检查清单

  • [ ] AssetsBundleSettings.asset 位于 Resources 目录。
  • [ ] ZMAssetRootPath 与实际安装路径一致。
  • [ ] 所有资源模块名稳定且区分大小写。
  • [ ] 目标平台、压缩方式和 Bundle 后缀配置正确。
  • [ ] 热更 URL 使用生产 CDN,并验证清单可访问。
  • [ ] 加密开启时,构建端和运行端密钥一致。
  • [ ] 首包包含必要的模块配置和内嵌 Bundle。
  • [ ] 高频 Prefab 已生成正确的 OriginData。
  • [ ] 每个加载点都有明确的释放责任方。
  • [ ] 真机验证断网、弱网、下载失败和版本回退流程。

从资源加载走向完整资产管线

先在 Editor 模式完成 API 接入,再配置一个独立模块完成 AssetBundle 构建,最后接入版本检查与热更新。按阶段验证,能更快定位路径、构建或服务器配置问题。

查看实战课程 返回文档首页