当前位置: 首页 > news >正文

Unity MyFramework:框架内资源管理和 YooAsset 有什么区别

Unity 项目里,资源管理是一个绕不开的问题。

小项目里可以直接Resources.Load,或者直接拖引用。

但项目一大,就会遇到很多问题:

  • 编辑器和真机加载方式不一致
  • AssetBundle 依赖关系需要管理
  • 异步加载需要合并回调
  • 资源卸载时机不好控制
  • UI、特效、音效、表格都需要统一资源入口
  • 热更新资源和本地资源要用同一套调用方式

MyFramework 里有自己的资源管理模块。

它和 YooAsset 这种成熟资源管理框架的定位并不一样。

项目地址:

https://github.com/ZHOURUIH/MyFramework

先说结论:

YooAsset 更像一个通用资源管理框架。

MyFramework 的资源管理更像框架内部的一层资源抽象。

YooAsset 重点解决的是资源包构建、资源定位、资源下载、资源缓存、资源版本、资源卸载这些通用问题。

MyFramework 的资源管理重点解决的是:

资源怎么融入自己的 UI、对象池、热更新、表格、特效、生命周期和工程结构。

所以这两个东西不是简单的谁替代谁。

它们的目标不同。


一、YooAsset 是通用资源系统,MyFramework 是框架内部模块

YooAsset 的目标很明确。

它是一套 Unity3D 资源管理系统,用于帮助团队快速部署和交付游戏。官方介绍里也提到,它支持编辑器模拟模式、单机运行模式、联机运行模式、Web 运行模式,支持引用计数、边玩边下、多功能下载器、版本管理等能力。

也就是说,YooAsset 重点解决的是:

  • 资源包怎么构建
  • 资源怎么定位
  • 资源怎么下载
  • 资源怎么缓存
  • 资源怎么版本管理
  • 资源怎么卸载
  • 多运行模式怎么切换

MyFramework 的资源管理不是独立产品。

它是 MyFramework 运行时的一部分。

它要解决的是:

  • 编辑器下用 AssetDatabase 加载
  • 打包后用 AssetBundle 加载
  • 业务层统一调用资源
  • 异步加载结果用ResourceRef<T>管理
  • 资源引用释放接入框架对象池
  • 资源卸载接入框架生命周期
  • 资源路径规则强制统一
  • AssetBundle 依赖关系由框架自己管理

这就是最核心的区别。

YooAsset 关注的是资源系统本身。

MyFramework 关注的是资源怎么服务整个框架。


二、YooAsset 的入口是 ResourcePackage

YooAsset 3.0.x 中,资源系统初始化后需要创建或获取ResourcePackage,资源加载、下载、清理等操作都应该通过ResourcePackage实例调用。官方文档也说明 3.0 版本已经移除了默认包裹静态快捷加载接口。

YooAsset 官方文档里的初始化方式大致是:

YooAssets.Initialize(); var package = YooAssets.CreatePackage("DefaultPackage"); var package = YooAssets.GetPackage("DefaultPackage");

资源加载也是通过 package 调用。

比如官方文档里的异步加载示例:

AssetHandle handle = package.LoadAssetAsync<AudioClip>("Assets/GameRes/Audio/bgMusic.mp3"); yield return handle; AudioClip audioClip = handle.AssetObject as AudioClip;

预制体加载示例:

AssetHandle handle = package.LoadAssetAsync<GameObject>("Assets/GameRes/Panel/login.prefab"); yield return handle; GameObject go = handle.InstantiateSync(); Debug.Log($"Prefab name is {go.name}");

这套方式很标准。

业务层拿到的是AssetHandle

资源对象在handle.AssetObject里。

如果要实例化预制体,可以用handle.InstantiateSync()

如果资源不用了,需要释放句柄。

官方文档里的卸载示例也明确写了:

AssetHandle handle = package.LoadAssetAsync<AudioClip>("Assets/GameRes/Audio/bgMusic.mp3"); yield return handle; handle.Release();

这就是 YooAsset 的资源生命周期模型:

加载返回 Handle,不用时 Release。


三、MyFramework 的入口是 ResourceManager

MyFramework 里资源管理的核心入口是ResourceManager

它是一个FrameSystem

资源系统初始化时,会根据当前环境决定加载源。

ResourceManager.init()是这样写的:

public override void init() { base.init(); mLoadSource = isEditor() ? GameEntryBase.getInstance().mFrameworkParam.mLoadSource : LOAD_SOURCE.ASSET_BUNDLE; if (isEditor()) { mObject.AddComponent<ResourcesManagerDebug>(); } }

这里能看出几个关键点。

第一,MyFramework 会判断是否在编辑器下。

第二,编辑器下的加载源可以通过mFrameworkParam.mLoadSource配置。

第三,非编辑器环境强制使用LOAD_SOURCE.ASSET_BUNDLE

也就是说,MyFramework 的资源模块不是让业务层自己选择加载模式。

加载模式由框架启动参数决定。

业务层只需要调用资源接口。


四、MyFramework 同步加载返回 ResourceRef

MyFramework 的同步加载接口不是直接返回 Unity 对象,而是返回ResourceRef<T>

loadGameResource是这样写的:

public ResourceRef<T> loadGameResource<T>(string name, bool errorIfNull = true) where T : UObject { using var a = new ProfilerScope(0); checkRelativePath(name); T res = null; if (mLoadSource == LOAD_SOURCE.ASSET_DATABASE) { res = mAssetDataBaseLoader.loadResource<T>(name); } else if (mLoadSource == LOAD_SOURCE.ASSET_BUNDLE) { res = mAssetBundleLoader.loadAsset<T>(name); } if (res == null && errorIfNull) { logError("can not find resource : " + name + ",请确认文件存在,且带后缀名,且不能使用反斜杠\\," + (name.Contains(' ') || name.Contains(' ') ? "注意此文件名中带有空格" : "")); } if (res == null) { return null; } CLASS(out ResourceRef<T> resRef).setResource(res); return resRef; }

这段代码很能说明 MyFramework 的特点。

它不是直接把UObject返回给业务层,而是创建一个ResourceRef<T>

也就是说,加载资源后,框架希望外部持有的是资源引用对象,而不是裸资源对象。

这个设计和 YooAsset 的AssetHandle有一点类似,都是希望资源生命周期有一个明确的持有者。

但实现方式不一样。

YooAsset 的持有者是AssetHandle

MyFramework 的持有者是ResourceRef<T>


五、ResourceRef 是 MyFramework 自己的引用凭证

它的作用不是简单包装一下资源对象,而是会向ResourceManager注册引用凭证。

代码如下:

public class ResourceRef<T> : ClassObject where T : UObject { protected T mResource; // 引用的资源 protected long mToken; // 引用凭证,一般不允许外部直接访问 public override void resetProperty() { base.resetProperty(); mResource = null; mToken = 0; } public void setResource(T res) { mResource = res; if (mResource == null) { logError("resource is null"); return; } mToken = mResourceManager.addReference(mResource); } public bool isValid() { return mResource != null; } public T getResource() { return mResource; } public long getToken() { return mToken; } // 在UN_CLASS时自动被调用 public override void destroy() { base.destroy(); if (mResource == null) { logError("resource is null"); return; } mResourceManager.removeReference(mResource, ref mToken); } // 对当前资源新创建一个引用对象出来,用于使多个地方对同一个资源拥有生命周期所有权 public ResourceRef<T> copyRef() { CLASS(out ResourceRef<T> newObjRef).setResource(mResource); return newObjRef; } }

这里最关键的是setResourcedestroy

加载资源时:

mToken = mResourceManager.addReference(mResource);

销毁引用时:

mResourceManager.removeReference(mResource, ref mToken);

所以 MyFramework 的资源引用不是简单依赖 Unity 对象本身。

它会给每一次引用生成一个 token。

多个地方使用同一个资源,可以通过copyRef()生成新的引用对象。

这和 YooAsset 的handle.Release()不一样。

YooAsset 是显式释放资源句柄。

MyFramework 是通过ResourceRef<T>接入自己的ClassObject / UN_CLASS生命周期。

这也是 MyFramework 更项目化的地方。


六、MyFramework 会定时检查引用是否为空

ResourceManager里有一段引用检查逻辑。

它会定时检查某个资源是否已经没有引用凭证。

代码如下:

if (tickTimerLoop(ref mCheckRefTimer, elapsedTime, CHECK_REF_INTERVAL)) { List<int> willRemoveList = null; foreach (var item in mReferenceTokenList) { if (item.Value.isEmpty()) { if (willRemoveList == null) { LIST(out willRemoveList); } willRemoveList.add(item.Key); } } if (willRemoveList != null) { foreach (int id in willRemoveList) { mInstanceIDToUObject.Remove(id, out UObject item); mReferenceTokenList.Remove(id); unloadInternal(item); } UN_LIST(ref willRemoveList); } }

这段逻辑说明 MyFramework 的卸载不是简单“谁调用 unload 谁卸载”。

它会记录资源的引用 token。

当某个资源没有任何引用 token 时,框架会自动进入卸载流程。

这个机制是和 MyFramework 自己的对象池、ClassObjectUN_CLASS配套的。

所以这里的区别是:

YooAsset 更强调资源句柄的显式释放。

MyFramework 更强调资源引用对象接入框架生命周期。


七、MyFramework 的异步安全加载和对象生命周期绑定

MyFramework 里有一个很有项目特点的接口:

loadGameResourceAsyncSafe

它的作用是:

在某个 IRecyclable 对象生命周期内加载资源,如果加载完成时对象已经被回收,就自动卸载资源并且不回调。

真实代码如下:

public CustomAsyncOperation loadGameResourceAsyncSafe<T>(IRecyclable relatedObj, string name, Action<ResourceRef<T>, string> callback, bool errorIfNull = true) where T : UObject { long assignID = relatedObj?.getAssignID() ?? 0; return loadGameResourceAsyncInternal<T>(name, (UObject asset, UObject[] _, byte[] _, string loadPath) => { if (callback == null || assignID != (relatedObj?.getAssignID() ?? 0)) { unloadInternal(asset); return; } ResourceRef<T> resRef = null; if (asset != null) { CLASS(out resRef).setResource(asset as T); } callback(resRef, loadPath); }, errorIfNull); }

这段代码是 MyFramework 和通用资源系统差异比较明显的地方。

它不是只关心资源有没有加载成功。

它还关心:

加载完成时,发起加载的对象是不是还活着。

如果relatedObj已经被回收,或者assignID已经变化,说明这个对象已经不是原来的对象了。

那资源加载完成也不能继续回调。

否则就可能出现:

  • 窗口已经关闭,异步图片又回调回来
  • 对象已经回收到对象池,异步加载又设置到旧对象上
  • 角色已经销毁,异步资源又被挂到无效对象上
  • UI 节点已经复用,旧请求覆盖新显示

所以 MyFramework 这里直接做了防护:

unloadInternal(asset); return;

这就是项目内资源模块的特点。

它不只是加载资源。

它要和框架对象生命周期配合。


八、MyFramework 强制资源路径规则

MyFramework 对资源路径有比较明确的要求。

checkRelativePath是这样写的:

protected static void checkRelativePath(string path) { // 需要带后缀 if (!path.Contains('.')) { logError("资源文件名需要带后缀:" + path); return; } // 不能是绝对路径 if (path.startWith(FrameBaseDefine.F_ASSETS_PATH)) { logError("不能传入绝对路径:" + path); return; } // 不能是以Assets或者Assets/GameResources开头的相对路径 if (path.startWith(FrameDefine.P_GAME_RESOURCES_PATH) || path.startWith(FrameBaseDefine.ASSETS)) { logError("不能是以Assets或者Assets/GameResources开头的相对路径:" + path); return; } }

这里可以看出,MyFramework 强制要求:

  • 资源路径必须带后缀
  • 不能传绝对路径
  • 不能以Assets开头
  • 不能以Assets/GameResources开头
  • 必须是相对于GameResources的路径

也就是说,MyFramework 不想让业务层随便传路径。

路径规则必须统一。

这和 YooAsset 的设计不一样。

YooAsset 的官方文档里,资源定位地址location可以是完整路径,也可以在开启可寻址模式后使用可寻址地址。官方文档里还说明,不带扩展名可以模糊匹配,带扩展名是精准匹配。

MyFramework 的选择更收敛。

它不追求所有定位方式都支持。

它更希望项目里所有资源路径都保持统一规则。

这也是项目内工具和通用工具的区别。


九、编辑器下走 AssetDatabase,打包后走 AssetBundle

MyFramework 里有两个加载器:

  • AssetDataBaseLoader
  • AssetBundleLoader

编辑器下可以用AssetDataBaseLoader加载。

非编辑器环境强制走AssetBundleLoader

AssetDataBaseLoader.loadResource真实代码如下:

public T loadResource<T>(string name) where T : UObject { string path = getFilePath(name); // 如果文件夹还未加载,则添加文件夹 var resList = mLoadedPath.getOrAddNew(path); // 资源未加载,则使用Resources.Load加载资源 if (!resList.TryGetValue(name, out AssetDataBaseLoadInfo info)) { if (!load<T>(path, name)) { return null; } // 加载后需要重新获取一次 info = resList.get(name); return info.getObject() as T; } if (info.getState() == LOAD_STATE.LOADED) { return info.getObject() as T; } else if (info.getState() == LOAD_STATE.DOWNLOADING) { logWarning("资源正在后台下载,不能同步加载!" + name); } else if (info.getState() == LOAD_STATE.LOADING) { logWarning("资源正在后台加载,不能同步加载!" + name); } else if (info.getState() == LOAD_STATE.NONE) { logWarning("资源已加入列表,但是未加载" + name); } return null; }

内部真正加载时,编辑器下走loadAssetAtPath

if (isEditor()) { string filePath = P_GAME_RESOURCES_PATH + name; if (isFileExist(filePath)) { info.setObject(loadAssetAtPath<T>(filePath)); info.setSubObjects(loadAllAssetsAtPath(filePath)); } } else { string filePath = removeSuffix(name); info.setObject(Resources.Load(filePath, typeof(T))); info.setSubObjects(Resources.LoadAll(filePath)); }

这里的重点是:

MyFramework 不希望业务层关心编辑器和运行时加载差异。

业务层只传GameResources下的相对路径。

底层是AssetDatabase,还是AssetBundle,由框架决定。


十、AssetBundle 依赖关系由框架自己管理

MyFramework 里AssetBundleInfo记录了当前资源包的依赖和被依赖关系。

真实字段如下:

protected Dictionary<string, AssetBundleInfo> mChildren = new(); // 依赖自己的AssetBundle列表,即引用了自己的AssetBundle protected Dictionary<string, AssetBundleInfo> mParents = new(); // 依赖的AssetBundle列表,即自己引用的AssetBundle,包含所有的直接和间接的依赖项 protected Dictionary<UObject, AssetInfo> mObjectToAsset = new(); // 通过Object查找AssetInfo的列表 protected Dictionary<string, AssetInfo> mAssetList = new(); // 资源包中的所有资源,初始化时就会填充此列表

加载 AssetBundle 时,会先加载依赖项。

同步加载资源包代码里有这一段:

foreach (var item in mParents) { item.Value.loadAssetBundle(); } mAssetBundle = AssetBundle.LoadFromFile(availableReadPath(mBundleFileName));

异步加载时,也会先请求依赖项:

public void loadParentAsync() { foreach (var item in mParents) { item.Value.loadAssetBundleAsync(null); } }

卸载时也会判断:

  • 当前包里的资源是否还在使用
  • 是否还有其他正在使用的 AssetBundle 依赖自己

canUnload是这样写的:

protected bool canUnload() { if (mLoadState != LOAD_STATE.LOADED) { return false; } // 如果资源包的资源已经没有在使用中,则卸载当前资源包 foreach (var item in mAssetList) { if (item.Value.getLoadState() != LOAD_STATE.NONE) { return false; } } // 如果已经没有资源被引用了,则卸载AssetBundle // 当前已经没有正在使用的AssetBundle引用了自己时才可以卸载 foreach (var item in mChildren) { if (item.Value.getLoadState() != LOAD_STATE.NONE) { return false; } } return true; }

这说明 MyFramework 自己维护了一套 AssetBundle 依赖关系和卸载策略。

YooAsset 也有完整的依赖管理,而且官方介绍里强调了它基于资源对象的资源包依赖管理方案,可以避免资源包之间循环依赖问题。

但两者的差异在于:

YooAsset 是通用资源包依赖管理。

MyFramework 是根据自己 AssetBundle 构建规则和运行时生命周期写的项目内依赖管理。


十一、MyFramework 支持边加载边下载,但不是完整通用更新框架

MyFramework 的AssetBundleLoader也支持远端下载。

比如资源包本地不存在时,会先下载,再加载:

if (fullPath == null) { byte[] assetBundleBytes = null; yield return downloadAssetBundleCoroutine(bundleInfo, (byte[] bytes) => { assetBundleBytes = bytes; }); bundleInfo.setLoadState(LOAD_STATE.LOADING); AssetBundleCreateRequest request = AssetBundle.LoadFromMemoryAsync(assetBundleBytes); if (request != null) { yield return request; assetBundle = request.assetBundle; } }

下载资源包的逻辑里,也会写入本地文件,并更新本地文件列表:

if (bytes != null && !isWebGL()) { // 写入到本地,并且更新资源列表 writeFile(F_PERSISTENT_ASSETS_PATH + bundleFileName, bytes); GameFileInfo fileInfo = new() { mFileName = bundleFileName, mFileSize = bytes.Length, mMD5 = generateFileMD5(bytes) }; mAssetVersionSystem.addPersistentFile(fileInfo); // 更新本地的文件列表 writeFileList(F_PERSISTENT_ASSETS_PATH, mAssetVersionSystem.generatePersistentAssetFileList()); }

所以 MyFramework 不是完全没有远端资源能力。

它也能在资源包缺失时下载资源包,并写入本地持久化目录。

但和 YooAsset 相比,MyFramework 的这部分更偏项目内实现。

YooAsset 的资源更新能力更完整。

官方文档里提到它支持版本管理、边玩边下载、多线程下载、断点续传、自动验证下载文件、自动修复损坏文件、多功能下载器等能力。

所以这里不能说 MyFramework 替代 YooAsset。

更准确地说:

MyFramework 有自己项目需要的下载和本地文件维护逻辑。

YooAsset 提供的是更完整、更通用的资源更新体系。


十二、适合场景不同

YooAsset 更适合这些情况:

  • 项目需要成熟通用资源管理框架
  • 需要完整资源版本管理
  • 需要完善下载器
  • 需要断点续传和文件校验
  • 需要多模式自由切换
  • 需要资源包构建管线
  • 需要可寻址资源定位
  • 需要通用工具和分析器支持
  • 希望资源系统独立于业务框架

MyFramework 的资源管理更适合这些情况:

  • 已经使用 MyFramework
  • 资源路径规则希望强制统一
  • 资源生命周期要接入框架对象池
  • 异步加载要和IRecyclable生命周期绑定
  • 资源引用希望接入ResourceRef<T>UN_CLASS
  • 编辑器下使用 AssetDatabase
  • 打包后使用 AssetBundle
  • AssetBundle 规则由项目自己控制
  • 不希望业务层直接接触资源系统细节

所以两者不是同一个目标。

YooAsset 更像一套完整资源基础设施。

MyFramework 更像框架内部的一层资源调用和生命周期管理模块。


总结

MyFramework 的资源管理和 YooAsset 最大的区别,不是某个接口怎么写。

而是定位不同。

YooAsset 是一个通用 Unity 资源管理系统。

它重点解决资源构建、定位、下载、版本、缓存、卸载、多运行模式等问题。

MyFramework 的资源管理是框架内部模块。

它重点解决资源怎么进入自己的框架生命周期。

从代码上可以看到:

  • ResourceManager根据编辑器和运行时选择加载源
  • loadGameResource<T>返回ResourceRef<T>,不是直接返回裸资源
  • ResourceRef<T>通过 token 接入引用管理
  • loadGameResourceAsyncSafe把资源异步加载和IRecyclable生命周期绑定
  • checkRelativePath强制项目资源路径规则
  • AssetDataBaseLoaderAssetBundleLoader分别处理编辑器和运行时加载
  • AssetBundleInfo自己维护父依赖、子依赖、资源列表和延迟卸载

所以 MyFramework 的资源管理不是为了替代 YooAsset。

它更像是:

把资源加载变成框架生命周期的一部分。

如果项目需要成熟通用资源系统,YooAsset 是更完整的选择。

如果项目已经深度使用 MyFramework,并且希望资源加载、引用、卸载、对象池、UI、热更新流程都由框架统一控制,那 MyFramework 自己的资源管理模块会更贴合。

http://www.cnnetsun.cn/news/3118966.html

相关文章:

  • DVWA从入门到精通(三):Command Injection(命令注入)
  • WorkshopDL终极指南:无需Steam账号免费下载创意工坊模组
  • LLM开发者生存图谱:大模型工程化落地的四层架构与成本可控实践
  • Navicat Mac版无限试用终极指南:三种简单方法免费使用Navicat Premium
  • 3个妙招解决Quark-Auto-Save转存失败:从空间不足到自动化管理的完整指南
  • BLDC电机FOC控制:A89307驱动芯片与PIC32MX795F512L方案详解
  • STM32与TB9051FTG实现静音直流电机控制方案
  • 5种ExplorerPatcher安装失败的深度解析与专业修复方法
  • ICM-42688-P高精度IMU与STM32的工业运动感知实践
  • 计算机毕业设计之 基于大语言模型的课程答疑系统的设计与实现
  • API-First无头CMS构建指南:从原理到实践
  • 如何通过在线旅游营销课程实现传统旅行社转型?
  • 告别网盘下载限制:浏览器脚本解锁九大云盘直链下载新体验
  • 基于Qt的NodeEditor节点编辑器开发指南
  • 4-20mA电流环原理与STM32工业信号采集实战
  • 锂电牵引辊需具备哪些核心性能?靠谱生产厂家怎么选?
  • 终极方案:Scroll Reverser专业解决macOS多设备滚动冲突
  • 实时 3D 场景重建新突破:LingBot-Map 前馈式模型,万帧视频秒变点云
  • 远程协助软件哪个好 手机怎么远程办公
  • Steam创意工坊跨平台下载技术解析:WorkshopDL分布式下载引擎架构实现
  • Fast-GitHub技术深度解析:浏览器扩展加速GitHub访问的技术实现
  • 实战指南:OpenSpeedy游戏加速引擎的完全使用方案
  • AI Agent安全攻防体系:OWASP、沙箱化与权限治理的工程落地
  • 制药企业2026年智能化改造项目备案数据分析
  • 终极免费方案:如何用Wand-Enhancer突破游戏修改器的时间限制
  • WebRTC弱网测试怎么做?从指标到工具,一套完整方案
  • 在 Python 中何时使用 classmethod、staticmethod 或实例方法
  • 开源字体库终极指南:15款专业字体一站式获取方案
  • 三步解锁Wand专业版功能:免费畅享完整游戏修改体验的终极指南
  • Mermaid Live Editor:重塑技术图表创作体验的在线利器