【unity游戏开发——热更新】YooAsset简化资源加载、打包、更新等流程

注意:考虑到热更新的内容比较多,我将热更新的内容分开,并全部整合放在【unity游戏开发——热更新】专栏里,感兴趣的小伙伴可以前往逐一查看学习。

文章目录

  • 前言
    • 1、什么是YooAsset?
    • 2、系统需求
    • 3、系统特点
  • 一、下载安装
    • 1、方法一:通过PackageManager安装(`推荐`)
    • 2、方法二:通过Packages清单安装
    • 3、方法三:通过Github下载安装
  • 二、导入示例工程
  • 三、YooAsset工具窗口介绍
    • 1、Home Page:跳转至 YooAsset 官方主页
    • 2、AssetBundle Collector:资源配置界面
      • 2.1 顶部功能栏细节
      • 2.2 全局设置项解析
      • 2.3 包裹设置项说明
      • 2.4 资源分组
        • (1)Group Active:分组激活规则
        • (2)Group Name:定义分组名称(如 battle),标识资源分组用途。
        • (3)Grouper Desc:添加分组备注信息,说明分组内容,方便团队协作理解。
        • (4)Asset Tags:为分组内资源添加分类标签(多标签用分号分隔,如 level1;level2),便于资源筛选。
      • 2.5 搜集器
        • (1)Collect Path:指定资源收集路径,可设置文件夹或单个文件,限定收集范围。
        • (2)Collector Type:定义收集器类型:
        • (3)AddressRule:可寻址规则()。内置如 AddressByFileName(按文件名定位)
        • (4)PackRule:打包规则
        • (5)FilterRule:过滤收集的资源类型。内置如 CollectScene(只收集场景文件),示例:
        • (6)UserData:自定义数据,辅助定制 AddressRule 和 PackRule,满足个性化需求。
        • (7)Asset Tags:为搜集器内资源添加分类标签,便于后续资源管理与筛选。
    • 3、AssetBundle Builder:资源构建界面
      • 3.1 界面功能模块
        • (1)Build Package:资源包裹列表,通过下拉选择确定需构建的资源包裹范围。
        • (2)Build Pipeline:构建管线的列表,下拉选择要使用的构建管线。
        • (3)Build Output:构建输出目录,根据Unity当前平台划分构建结果,便于管理不同平台资源。
        • (4)Build Version:设置构建的资源包版本,用于标识补丁包等内容。
        • (5)Clear Build Cache:清理构建缓存,勾选则重新构建所有资源包;不勾选启用增量打包,提升构建速度。
        • (6)Use Asset Depend DB:开启后使用资源依赖关系数据库,加快资源收集与构建效率。
        • (7)Encryption:加密类列表
        • (8)Compression:设置资源包压缩方式,优化包体大小。
        • (9)File Name Style:定义输出资源包文件名样式
        • (10)Copy Buildin File Option:首包资源拷贝方式
      • 3.2 构建操作与结果
        • (1)构建执行:点击“构建”按钮启动多节点流程,任一节点错误则构建失败,错误信息可在控制台查看。
        • (2)补丁包:构建成功后,输出目录生成以“`Build Version`资源版本号”命名的补丁包文件夹,包含:
        • (3)构建报告:DefaultPackage_xxx.report,可通过构建报告窗口查看详细构建信息。
        • (4)重要概念与注意事项
    • 4、AssetBundle Reporter:构建报告界面
    • 5、AssetBundle Debugger:调试工具
      • 5.1 调试器核心功能
      • 5.2 真机远程调试注意事项
      • 5.3 资源对象列表视图
    • 6、AssetArt Scanner:资产扫描器
      • 6.1 工具界面组成
      • 6.2 基础操作按钮
      • 6.3 扫描器配置项详解
      • 6.4 扫描器检视界面
      • 6.5 扫描器接口扩展
    • 7、AssetArt Reporter:资产扫描报告
      • 7.1 工具介绍
      • 7.2 搜索栏支持
  • 四、YooAsset资源系统的运行模式
    • 1、编辑器模拟模式 (EditorSimulateMode)
    • 2、单机运行模式 (OfflinePlayMode)
    • 3、联机运行模式 (HostPlayMode)
    • 4、Web运行模式 (WebPlayMode)
    • 5、自定义运行模式 (CustomPlayMode)
  • 五、YooAsset资源热更新实践
    • 1、导入资源
    • 2、将图片1进行打包
    • 3、复制version文件和bytes文件到我们的流文件夹
    • 4、开启允许从http下载
    • 5、复制前面打包出来的资源到本地资源服务器,启动本地资源服务器
    • 6、手写热更新流程代码
    • 7、挂载脚本运行游戏,可以看到热更和加载成功
    • 8、其他加载资源方式
      • 8.1 使用委托异步加载资源
      • 8.2 协程加载资源
      • 8.3 Task加载资源
    • 9、场景加载
    • 10、预制体加载和创建
    • 11、加载图片子对象切片
    • 12、卸载相关
      • 12.1 回收不再使用的资源
      • 12.2 强制回收所有资源
      • 12.3 尝试卸载指定的资源
  • 六、YooAsset资源加密解密
    • 1、加密资源对象
    • 2、解密资源对象
    • 3、使用解密对象解密加密的资源
  • 专栏推荐
  • 完结

前言

1、什么是YooAsset?

YooAsset 是一款轻量级、高性能的 Unity 资源管理系统,专注于简化资源加载、打包、更新等流程,尤其适合中小型游戏或应用开发。

  • 官方文档:https://www.yooasset.com/docs/Introduce
  • Github地址:https://github.com/tuyoogame/YooAsset

2、系统需求

  • 支持版本: Unity2019.4 & Unity2020.3 & Unity2021.3 & Unity2022.3 & Unity6.0

  • 支持平台: Windows、OSX、Android、iOS、WebGL

  • 开发环境: .NET4.x

3、系统特点

  • 可扩展文件系统

完美支持微信小游戏和抖音小游戏等特殊平台。支持接入虚拟文件系统,支持自定义各类需求。

  • 构建管线无缝切换

支持传统的内置构建管线,也支持可编程构建管线(SBP)。

  • 支持分布式构建

支持分工程构建,支持工程里分内容构建,很方便支持游戏模组(MOD)。

  • 支持可寻址资源定位

默认支持完整路径的资源定位,也支持可寻址资源定位,不需要繁琐的过程即可高效的配置寻址路径。

  • 安全高效的分包方案

基于资源标签的分包方案,自动对依赖资源包进行分类,避免人工维护成本。可以非常方便的实现零资源安装包,或者全量资源安装包。

  • 强大灵活的打包系统

可以自定义打包策略,自动分析依赖实现资源零冗余,基于资源对象的资源包依赖管理方案,天然的避免了资源包之间循环依赖的问题。

  • 基于引用计数方案

基于引用计数的管理方案,可以帮助我们实现安全的资源卸载策略,更好的对内存管理,避免资源对象冗余。还有强大的分析器可帮助发现潜在的资源泄漏问题。

  • 多种模式自由切换

编辑器模拟模式,单机运行模式,联机运行模式,Web运行模式。在编辑器模拟模式下,可以不构建资源包来模拟真实环境,在不修改任何代码的情况下,可以自由切换到其它模式。

  • 强大安全的加载系统

    • 异步加载 支持协程,Task,委托等多种异步加载方式。
    • 同步加载 支持同步加载和异步加载混合使用。
    • 边玩边下载 在加载资源对象的时候,如果资源对象依赖的资源包在本地不存在,会自动从服务器下载到本地,然后再加载资源对象。
    • 多线程下载 支持断点续传,自动验证下载文件,自动修复损坏文件。
    • 多功能下载器 可以按照资源分类标签创建下载器,也可以按照资源对象创建下载器。可以设置同时下载文件数的限制,设置下载失败重试次数,设置下载超时判定时间。多个下载器同时下载不用担心文件重复下载问题,下载器还提供了下载进度以及下载失败等常用接口。
  • 原生格式文件管理

支持原生文件构建关系,可以很方便的实现原生文件的版本管理和下载。

  • 灵活多变的版本管理

支持线上版本快速回退,支持区分审核版本,测试版本,线上版本,支持灰度更新及测试。

  • 多平台的完美适配

支持安卓,苹果,PC等常规平台,支持网页运行。2.x版本完美适配了微信小游戏平台和抖音小游戏平台。

一、下载安装

1、方法一:通过PackageManager安装(推荐

打开管理界面 Edit/Project Settings/Package Manager

// 输入以下内容(国际版)
Name: package.openupm.com
URL: https://package.openupm.com
Scope(s): com.tuyoogame.yooasset

添加仓库配置
在这里插入图片描述

打开管理界面 Edit/Windows/Package Manager,点击安装
在这里插入图片描述

2、方法二:通过Packages清单安装

直接修改项目根目录Packages文件夹下的清单文件manifest.json

{"dependencies": {"com.tuyoogame.yooasset": "2.x.x",......},"scopedRegistries": [{"name": "package.openupm.com","url": "https://package.openupm.com","scopes": ["com.tuyoogame.yooasset"]}]
}

3、方法三:通过Github下载安装

Github地址:https://github.com/tuyoogame/YooAsset

在发布的Release版本中,选择最新版本下载Source Code压缩包。解压后将Assets/YooAsset目录拷贝到项目Assets文件夹中
在这里插入图片描述

二、导入示例工程

在这里插入图片描述

三、YooAsset工具窗口介绍

在这里插入图片描述

1、Home Page:跳转至 YooAsset 官方主页

在这里插入图片描述

2、AssetBundle Collector:资源配置界面

收集项目资源,确定哪些资源需要打包成 AssetBundle。
在这里插入图片描述

2.1 顶部功能栏细节

在这里插入图片描述

  • Fix 修复按钮:当配置中的文件夹位置变动时,修正资源路径关联,确保资源引用正常。比如默认找不到资源路径,点击修复按钮后找到了
    在这里插入图片描述

  • Import 导入按钮:导入保存的 XML 配置文件,快速复用历史配置,减少重复操作。

  • Export 导出按钮:将当前配置数据导出为 XML 文件,便于备份、团队协作共享配置。

2.2 全局设置项解析

在这里插入图片描述

  • Show Package:控制是否展示资源包列表视图,勾选后直观呈现项目资源包结构。
    在这里插入图片描述

  • Show Editor Alias:是否显示为中文模式。
    在这里插入图片描述

  • Unique Bundle Name:资源包名追加PackageName作为前缀。当有多个Package的时候,强烈建议开启此项!

2.3 包裹设置项说明

在这里插入图片描述

  • Enable Addressable:启用可寻址资源定位系统,支持代码加载资源,同时兼容全路径加载方式。
  • Location To Lower:使资源定位地址大小写不敏感,简化资源加载时的地址匹配逻辑。
  • Include Asset GUID:在资源清单中记录资源 GUID 信息,精准追踪管理资源。
  • Auto Collect Shaders:自动将所有着色器打包到独立资源包,优化着色器管理与加载效率。
  • File Ignore Rule:设置全局文件忽略规则(支持扩展),如原生文件配置可选择 RawFileIgnoreRule 过滤特定文件。

2.4 资源分组

在这里插入图片描述

(1)Group Active:分组激活规则

在这里插入图片描述

  • EnableGroup(启用分组)
  • DisableGroup(禁用分组)
  • 支持自定义扩展,示例代码:
    //自定义扩展范例
    public class DisableGroup : IActiveRule {  public bool IsActiveGroup(GroupData groupData) {  return false;  }  
    }  
    
(2)Group Name:定义分组名称(如 battle),标识资源分组用途。
(3)Grouper Desc:添加分组备注信息,说明分组内容,方便团队协作理解。
(4)Asset Tags:为分组内资源添加分类标签(多标签用分号分隔,如 level1;level2),便于资源筛选。

2.5 搜集器

在这里插入图片描述

(1)Collect Path:指定资源收集路径,可设置文件夹或单个文件,限定收集范围。
(2)Collector Type:定义收集器类型:
  • MainAssetCollector:收集主资源并写入清单,支持代码加载。
  • StaticAssetCollector:收集主资源但不写入清单,用于定制化打包。
  • DependAssetCollector:收集依赖资源,未引用依赖自动剔除。
(3)AddressRule:可寻址规则()。内置如 AddressByFileName(按文件名定位)

在这里插入图片描述

  • AddressByFileName 以文件名为定位地址。
  • AddressByFilePath 以文件路径为定位地址。
  • AddressByGrouperAndFileName 以分组名+文件名为定位地址。
  • AddressByFolderAndFileName 以文件夹名+文件名为定位地址。
  • 支持自定义,示例:
    public class AddressByFileName : IAddressRule {  string GetAssetAddress(AddressRuleData data) {  return Path.GetFileNameWithoutExtension(data.AssetPath);  }  
    }  
    
(4)PackRule:打包规则

在这里插入图片描述

  • PackSeparately 以文件路径作为资源包名,每个资源文件单独打包。
  • PackDirectory 以文件所在的文件夹路径作为资源包名,该文件夹下所有文件打进一个资源包。
  • PackTopDirectory 以收集器下顶级文件夹为资源包名,该文件夹下所有文件打进一个资源包。
  • PackCollector 以收集器路径作为资源包名,收集的所有文件打进一个资源包。
  • PackGroup 以分组名称作为资源包名,收集的所有文件打进一个资源包。
  • PackRawFile 目录下的资源文件会被处理为原生资源包。
  • 支持自定义,示例:
    public class PackDirectory : IPackRule {  PackRuleResult GetPackRuleResult(PackRuleData data) {  string bundleName = Path.GetDirectoryName(data.AssetPath);  return new PackRuleResult(bundleName, DefaultPackRule.AssetBundleFileExtension);  }  
    }  
    
(5)FilterRule:过滤收集的资源类型。内置如 CollectScene(只收集场景文件),示例:

在这里插入图片描述

  • CollectAll 收集目录下的所有资源文件
  • CollectScene 只收集目录下的场景文件
  • CollectPrefab 只收集目录下的预制体文件
  • CollectSprite 只收集目录下的精灵类型的文件
  • 支持自定义,示例:
    //自定义扩展范例
    public class CollectScene : IFilterRule
    {public bool IsCollectAsset(FilterRuleData data){return Path.GetExtension(data.AssetPath) == ".unity";}
    }
    
(6)UserData:自定义数据,辅助定制 AddressRule 和 PackRule,满足个性化需求。
(7)Asset Tags:为搜集器内资源添加分类标签,便于后续资源管理与筛选。

3、AssetBundle Builder:资源构建界面

构建工具,用于生成 AssetBundle 包,配置打包参数。
在这里插入图片描述

3.1 界面功能模块

(1)Build Package:资源包裹列表,通过下拉选择确定需构建的资源包裹范围。

在这里插入图片描述

(2)Build Pipeline:构建管线的列表,下拉选择要使用的构建管线。

在这里插入图片描述

  • EditorSimulateBuildPipeline:编辑器模拟构建管线,生成资源清单但不生成Bundle文件,用于模拟真实打包环境。
  • BuiltinBuildPipeline:内置构建管线,常规构建使用。
  • ScriptableBuildPipeline:可编程构建管线(Unity 2021.3+开始推荐使用该构建管线),允许开发者控制打包流程。
  • RawFileBuildPipeline:原生文件构建管线,处理Unity无法识别的资源(如FMOD音频文件)。
(3)Build Output:构建输出目录,根据Unity当前平台划分构建结果,便于管理不同平台资源。
(4)Build Version:设置构建的资源包版本,用于标识补丁包等内容。
(5)Clear Build Cache:清理构建缓存,勾选则重新构建所有资源包;不勾选启用增量打包,提升构建速度。
(6)Use Asset Depend DB:开启后使用资源依赖关系数据库,加快资源收集与构建效率。
(7)Encryption:加密类列表

支持自定义加密(需实现 IEncryptionServices 接口),包含文件偏移、内存、流等解密加载方式。

(8)Compression:设置资源包压缩方式,优化包体大小。

在这里插入图片描述

(9)File Name Style:定义输出资源包文件名样式

在这里插入图片描述

  • HashName(哈希值)
  • BundleName(资源包名)
  • BundleName_HashName(资源包名+哈希值)。
(10)Copy Buildin File Option:首包资源拷贝方式

在这里插入图片描述

  • None:不拷贝文件。
  • ClearAndCopyAll:清空后拷贝所有文件。
  • ClearAndCopyByTags:清空后按资源标签拷贝。
  • OnlyCopyAll:不清空直接拷贝所有文件。
  • OnlyCopyByTags:不清空直接按标签拷贝。

3.2 构建操作与结果

(1)构建执行:点击“构建”按钮启动多节点流程,任一节点错误则构建失败,错误信息可在控制台查看。

在这里插入图片描述

(2)补丁包:构建成功后,输出目录生成以“Build Version资源版本号”命名的补丁包文件夹,包含:
  • DefaultPackage.version:资源版本文件。
  • DefaultPackage_xxx.hash:记录资源清单哈希值。
  • DefaultPackage_xxx.json:Json格式,用于预览信息。
  • DefaultPackage_xxx.bytes:二进制格式,供程序读取加载。
    在这里插入图片描述
(3)构建报告:DefaultPackage_xxx.report,可通过构建报告窗口查看详细构建信息。

在这里插入图片描述

(4)重要概念与注意事项
  • 增量构建:利用Unity资源构建缓存,避免重复构建,提升打包效率。也就是不勾选Clear Build Cache
    在这里插入图片描述

  • 首包资源:构建应用时打入首包的资源,存放于 StreamingAssets/yoo/ 目录,支持热更新。也就是选择Copy Buildin File Option首包资源拷贝方式
    在这里插入图片描述
    在这里插入图片描述

  • Jenkins自动化构建:参考示例代码配置构建参数(如输出目录、版本、加密等),通过代码执行构建流程,实现自动化。

    如果需要自动化构建,可以参考如下代码范例:
    下面是使用内置构建管线来构建资源包的代码。

    private static void BuildInternal(BuildTarget buildTarget)
    {Debug.Log($"开始构建 : {buildTarget}");var buildoutputRoot = AssetBundleBuilderHelper.GetDefaultBuildOutputRoot();var streamingAssetsRoot = AssetBundleBuilderHelper.GetStreamingAssetsRoot();// 构建参数BuiltinBuildParameters buildParameters = new BuiltinBuildParameters();buildParameters.BuildOutputRoot = buildoutputRoot;buildParameters.BuildinFileRoot = streamingAssetsRoot;buildParameters.BuildPipeline = EBuildPipeline.BuiltinBuildPipeline.ToString();buildParameters.BuildBundleType = (int)EBuildBundleType.AssetBundle; //必须指定资源包类型buildParameters.BuildTarget = BuildTarget;buildParameters.PackageName = "DefaultPackage";buildParameters.PackageVersion = "1.0";buildParameters.VerifyBuildingResult = true;buildParameters.EnableSharePackRule = true; //启用共享资源构建模式,兼容1.5x版本buildParameters.FileNameStyle = EFileNameStyle.HashName;buildParameters.BuildinFileCopyOption = EBuildinFileCopyOption.None;buildParameters.BuildinFileCopyParams = string.Empty;buildParameters.EncryptionServices = CreateEncryptionInstance();buildParameters.CompressOption = ECompressOption.LZ4;buildParameters.ClearBuildCacheFiles = false; //不清理构建缓存,启用增量构建,可以提高打包速度!buildParameters.UseAssetDependencyDB = true; //使用资源依赖关系数据库,可以提高打包速度!// 执行构建BuiltinBuildPipeline pipeline = new BuiltinBuildPipeline();var buildResult = pipeline.Run(buildParameters, true);if (buildResult.Success){Debug.Log($"构建成功 : {buildResult.OutputPackageDirectory}");}else{Debug.LogError($"构建失败 : {buildResult.ErrorInfo}");}
    }// 从构建命令里获取参数示例
    private static string GetBuildPackageName()
    {foreach (string arg in System.Environment.GetCommandLineArgs()){if (arg.StartsWith("buildPackage"))return arg.Split("="[0])[1];}return string.Empty;
    }
    
  • SBP构建管线注意事项:需设置内置着色器资源包名称,且与自动收集的着色器资源包名一致,确保构建正确。

    private static void BuildInternal(BuildTarget buildTarget)
    {// 构建参数ScriptableBuildParameters buildParameters = new ScriptableBuildParameters();......buildParameters.BuiltinShadersBundleName = GetBuiltinShaderBundleName();
    }/// <summary>
    /// 内置着色器资源包名称
    /// 注意:和自动收集的着色器资源包名保持一致!
    /// </summary>
    private string GetBuiltinShaderBundleName()
    {var uniqueBundleName = AssetBundleCollectorSettingData.Setting.UniqueBundleName;var packRuleResult = DefaultPackRule.CreateShadersPackRuleResult();return packRuleResult.GetBundleName(PackageName, uniqueBundleName);
    }
    

4、AssetBundle Reporter:构建报告界面

生成构建报告,展示 AssetBundle 依赖关系、大小等信息,便于分析优化。
在这里插入图片描述
YooAsset 构建报告工具支持查看概览信息、资源对象视图、资源包视图,且仅适配 Unity 2019.4+ 版本。要import后选择打包出来的report文件才能查看。各视图功能如下:
在这里插入图片描述
在这里插入图片描述

5、AssetBundle Debugger:调试工具

用于排查 AssetBundle 加载、依赖错误等运行时问题。
在这里插入图片描述

5.1 调试器核心功能

  • 作用:在游戏运行时实时监控资源包加载状态,追踪资源对象引用计数,辅助定位资源泄漏问题。
  • 资源泄漏定位:通过引用计数与资源包卸载条件,快速识别未释放的资源。
  • 性能优化:分析加载耗时与异步任务执行链,优化资源加载策略。
  • 依赖关系验证:校验资源包依赖关系,避免冗余加载或错误引用。

5.2 真机远程调试注意事项

在构建安装包的时候,需要勾选上Development Build和Autoconnect Profiler
在这里插入图片描述

5.3 资源对象列表视图

该页面展示了当前加载的所有资源对象,以及加载耗时,加载状态,引用计数等数据。

Depend Bundles 是选中的资源对象加载所依赖的资源包列表!
在这里插入图片描述

6、AssetArt Scanner:资产扫描器

扫描项目美术资源(如模型、贴图),检测格式、尺寸等规范问题。
在这里插入图片描述

6.1 工具界面组成

  • 左侧:资产扫描器列表,显示所有可用扫描器。
  • 中间:选中扫描器的配置界面,设置扫描规则与参数。
  • 右侧:扫描器参数检视界面,展示自定义扫描器的专属配置。

6.2 基础操作按钮

  • 导入按钮:加载保存的 XML 配置文件,复用历史扫描规则。
  • 导出按钮:将当前配置导出为 XML 文件,便于备份或团队共享。
  • 扫描所有按钮:执行全部扫描器任务,生成多份扫描报告。

6.3 扫描器配置项详解

  • Scanner Name:定义扫描器名称(如 TextureScanner),标识扫描器功能。
  • Scanner Desc:添加扫描器备注信息,说明扫描用途或规则。
  • Scanner Schema:关联扫描器模式文件(继承 ScannerSchema 的 ScriptableObject)。
  • Output Folder:设置报告文件输出目录,默认为 Assets/ScanReports。
  • Collector:指定扫描路径,可配置多个文件夹或文件范围。

6.4 扫描器检视界面

  • 功能:开发者实现自定义扫描器时,可编写专属参数检视面板。
  • 实现方式:继承 SchemaInspector 类,通过 EditorGUI 绘制可视化配置项。

6.5 扫描器接口扩展

继承基类ScannerSchema并实现虚拟方法。ScannerSchema实际上是可序列化类型。

public abstract class ScannerSchema : ScriptableObject
{/// <summary>/// 获取用户指南信息/// </summary>public abstract string GetUserGuide();/// <summary>/// 运行生成扫描报告/// </summary>public abstract ScanReport RunScanner(AssetArtScanner scanner);/// <summary>/// 修复扫描结果/// </summary>public abstract void FixResult(List<ReportElement> fixList);/// <summary>/// 创建检视面板/// </summary>public virtual SchemaInspector CreateInspector()}

太空战机DEMO里实现了一个非常简单的纹理扫描器,可以直接阅读源码来快速学习。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;namespace YooAsset.Editor
{public abstract class ScannerSchema : ScriptableObject{/// <summary>/// 获取用户指南信息/// </summary>public abstract string GetUserGuide();/// <summary>/// 运行生成扫描报告/// </summary>public abstract ScanReport RunScanner(AssetArtScanner scanner);/// <summary>/// 修复扫描结果/// </summary>public abstract void FixResult(List<ReportElement> fixList);/// <summary>/// 创建检视面板/// </summary>public virtual SchemaInspector CreateInspector(){return null;}}
}

7、AssetArt Reporter:资产扫描报告

生成美术资源分析报告,汇总扫描结果,辅助资源管理与优化。
在这里插入图片描述

7.1 工具介绍

  • 导入按钮(Import):可以导入单个报告文件。

  • 导入按钮(Multi-Import):可以同时导入多个报告文件并合并显示(注意:扫描器必须一致)。

  • 导出选择按钮(Export Select):导出所有勾选的元素关联的资源文件(文件拷贝)。

  • 修复所有按钮(Fix All):修复所有未通过元素(排除白名单和隐藏元素)。

  • 修复选择按钮(Fix Select):修复所有勾选的元素(包含白名单和隐藏元素)。

  • 显示隐藏元素:显示隐藏元素(眼睛开关控制显隐)。

  • 显示通过元素:显示通过元素。

  • 显示白名单元素:显示白名单元素。

  • 注意:该工具仅支持Unity2019.4+

7.2 搜索栏支持

  • 通用关键字搜索

例如:输入某个资源的文件名称,会自动过滤。

  • 指定项的关键字搜索

例如:搜索“错误信息”项的内容,可以输入以下命令。

注意:中间冒号为小写。左侧为指定项,右侧为关键字。

错误信息:安卓格式不对
  • 指定项的数值比较搜索

例如:比较“内存大小”项的数值,可以输入以下命令。

注意:中间为比较符号。左侧为指定项,右侧为比较数值。

内存大小=1024
图片宽度>1024
图片宽度>=1024
图片高度<1024
图片高度<=1024

四、YooAsset资源系统的运行模式

在这里插入图片描述

1、编辑器模拟模式 (EditorSimulateMode)

在编辑器下,不需要构建资源包,来模拟运行游戏。直接运行即可。注意该模式只在编辑器下起效
在这里插入图片描述

2、单机运行模式 (OfflinePlayMode)

对于不需要热更新资源的游戏,可以使用单机运行模式。注意该模式需要构建资源包。

打包时,选择Copy Buildin File Option首包资源文件的拷贝方式ClearAndCopyAll(清空后拷贝所有文件)模式
在这里插入图片描述
打包出来的所有文件会复制一份到Assets\StreamingAssets\yoo\DefaultPackage目录下,当然你也可以手动复制过来
在这里插入图片描述
运行效果
在这里插入图片描述

3、联机运行模式 (HostPlayMode)

对于需要热更新资源的游戏,可以使用联机运行模式。注意:该模式需要构建资源包。

清空之前复制在流文件夹下的资源
在这里插入图片描述
本地电脑模拟服务器环境,我这里使用的是phpstudy。

下载安装phpstudy:https://old.xp.cn/download.html
在这里插入图片描述
把打包好的文件复制放入服务器路径
在这里插入图片描述
这里我放在。。。\CDN\PC\v1.0, 是因为示例代码中写好了这个路径
在这里插入图片描述
配置网站域名,记得开启nginx服务器
在这里插入图片描述
记得打开高级设置里最下面的“目录索引”,这样我没才可以直接访问服务器目录
在这里插入图片描述

网页打开 http://localhost/CDN/PC/v1.0/ 可以看到资源
在这里插入图片描述

把之前打好的version文件和bytes文件复制放回流文件夹。这是提供当前版本号,为了对比服务器版本号的
在这里插入图片描述
在这里插入图片描述
修改为联网运行模式
在这里插入图片描述
将游戏打为PC包,记得添加对应的场景
在这里插入图片描述
点击运行,首次运行游戏会提示资源更新。
在这里插入图片描述

点击yes,下载更新补丁。即可开始游玩游戏
在这里插入图片描述

我们修改内容,试试热更效果,比如我在首页UI面板添加一个按钮
在这里插入图片描述
重新构建资源
在这里插入图片描述
在这里插入图片描述

复制新构建的资源到服务器..\CDN\PC\v1.0目录下
在这里插入图片描述
重新运行前面打的PC包,可以看到检查到资源更新需要下载资源。
在这里插入图片描述
点击yes,下载后可以看到更新后的按钮,至此完成资源热更新
在这里插入图片描述

4、Web运行模式 (WebPlayMode)

针对WebGL平台的专属模式,包括微信小游戏,抖音小游戏都需要选择该模式。注意:该模式需要构建资源包。

具体请参考官方文档:小游戏方案

5、自定义运行模式 (CustomPlayMode)

支持多个文件系统。

注意:列表最后一个元素作为主文件系统!

五、YooAsset资源热更新实践

1、导入资源

随便导入一些资源作为热更新的资源
在这里插入图片描述
记得修改图片类型为Sprite
在这里插入图片描述

2、将图片1进行打包

在这里插入图片描述

在这里插入图片描述
结果
在这里插入图片描述

3、复制version文件和bytes文件到我们的流文件夹

把打好的version文件和bytes文件复制放回流文件夹。这是提供当前版本号,为了对比服务器版本号的。
在这里插入图片描述

4、开启允许从http下载

修改项目设置,开启允许从http下载
在这里插入图片描述

5、复制前面打包出来的资源到本地资源服务器,启动本地资源服务器

这里使用phpstudy模拟本地服务器,具体操作可以看前面
在这里插入图片描述

6、手写热更新流程代码

其实就是改造了YooAsset官方例子,并且我已经加了详细的注释,就不多介绍了。

using System.Collections;
using UnityEngine;
using YooAsset;/// <summary>
/// YooAsset资源系统测试脚本
/// </summary>
public class MyYooAssetTest : MonoBehaviour
{string hostServerIP = "http://127.0.0.1";//服务器地址string appVersion = "v1.0"; //版本号public EPlayMode PlayMode = EPlayMode.HostPlayMode;//资源系统运行模式public string packageName = "DefaultPackage"; //默认包名private ResourcePackage _package = null; //资源包对象public int downloadingMaxNum = 10;//最大下载数量public int filedTryAgain = 3;//失败重试次数private ResourceDownloaderOperation _downloader;//下载器private UpdatePackageManifestOperation _operationManifest;//更新清单void Awake(){Debug.Log($"资源系统运行模式:{PlayMode}");Application.targetFrameRate = 60;//设置帧率Application.runInBackground = true;//设置后台运行DontDestroyOnLoad(gameObject);//确保该对象不会在场景切换时销毁}IEnumerator Start(){//1.初始化YooAsset资源系统YooAssets.Initialize();//2.初始化资源包yield return StartCoroutine(InitPackage());//3.获取资源版本yield return StartCoroutine(UpdatePackageVersion());//4.获取文件清单yield return StartCoroutine(UpdateManifest());//5.创建资源下载器CreateDownloader();//5.开始下载资源文件yield return StartCoroutine(BeginDownload());//6.清理未使用的缓存文件ClearFiles();}#region 初始化资源包private IEnumerator InitPackage(){// 获取或创建资源包对象_package = YooAssets.TryGetPackage(packageName);if (_package == null)_package = YooAssets.CreatePackage(packageName);// 编辑器下的模拟模式InitializationOperation initializationOperation = null;if (PlayMode == EPlayMode.EditorSimulateMode){var buildResult = EditorSimulateModeHelper.SimulateBuild(packageName);var packageRoot = buildResult.PackageRootDirectory;var createParameters = new EditorSimulateModeParameters();createParameters.EditorFileSystemParameters = FileSystemParameters.CreateDefaultEditorFileSystemParameters(packageRoot);initializationOperation = _package.InitializeAsync(createParameters);}// 单机运行模式if (PlayMode == EPlayMode.OfflinePlayMode){var createParameters = new OfflinePlayModeParameters();createParameters.BuildinFileSystemParameters = FileSystemParameters.CreateDefaultBuildinFileSystemParameters();initializationOperation = _package.InitializeAsync(createParameters);}// 联机运行模式if (PlayMode == EPlayMode.HostPlayMode){//创建远端服务实例,用于资源请求string defaultHostServer = GetHostServerURL();string fallbackHostServer = GetHostServerURL();IRemoteServices remoteServices = new RemoteServices(defaultHostServer, fallbackHostServer);// 创建联机模式参数,并设置内置及缓存文件系统参数HostPlayModeParameters createParameters = new HostPlayModeParameters{//创建内置文件系统参数BuildinFileSystemParameters = FileSystemParameters.CreateDefaultBuildinFileSystemParameters(),//创建缓存系统参数CacheFileSystemParameters = FileSystemParameters.CreateDefaultCacheFileSystemParameters(remoteServices)};//执行异步初始化initializationOperation = _package.InitializeAsync(createParameters);}// WebGL运行模式if (PlayMode == EPlayMode.WebPlayMode){
#if UNITY_WEBGL && WEIXINMINIGAME && !UNITY_EDITORvar createParameters = new WebPlayModeParameters();string defaultHostServer = GetHostServerURL();string fallbackHostServer = GetHostServerURL();string packageRoot = $"{WeChatWASM.WX.env.USER_DATA_PATH}/__GAME_FILE_CACHE"; //注意:如果有子目录,请修改此处!IRemoteServices remoteServices = new RemoteServices(defaultHostServer, fallbackHostServer);createParameters.WebServerFileSystemParameters = WechatFileSystemCreater.CreateFileSystemParameters(packageRoot, remoteServices);initializationOperation = _package.InitializeAsync(createParameters);
#elsevar createParameters = new WebPlayModeParameters();createParameters.WebServerFileSystemParameters = FileSystemParameters.CreateDefaultWebServerFileSystemParameters();initializationOperation = _package.InitializeAsync(createParameters);
#endif}yield return initializationOperation;// 如果初始化失败弹出提示界面if (initializationOperation.Status != EOperationStatus.Succeed){Debug.LogWarning($"{initializationOperation.Error}");PatchEventDefine.InitializeFailed.SendEventMessage();}else{Debug.Log("初始化成功-------------------------");}}/// <summary>/// 获取资源服务器地址/// </summary>private string GetHostServerURL(){#if UNITY_EDITORif (UnityEditor.EditorUserBuildSettings.activeBuildTarget == UnityEditor.BuildTarget.Android)return $"{hostServerIP}/CDN/Android/{appVersion}";else if (UnityEditor.EditorUserBuildSettings.activeBuildTarget == UnityEditor.BuildTarget.iOS)return $"{hostServerIP}/CDN/IPhone/{appVersion}";else if (UnityEditor.EditorUserBuildSettings.activeBuildTarget == UnityEditor.BuildTarget.WebGL)return $"{hostServerIP}/CDN/WebGL/{appVersion}";elsereturn $"{hostServerIP}/CDN/PC/{appVersion}";
#elseif (Application.platform == RuntimePlatform.Android)return $"{hostServerIP}/CDN/Android/{appVersion}";else if (Application.platform == RuntimePlatform.IPhonePlayer)return $"{hostServerIP}/CDN/IPhone/{appVersion}";else if (Application.platform == RuntimePlatform.WebGLPlayer)return $"{hostServerIP}/CDN/WebGL/{appVersion}";elsereturn $"{hostServerIP}/CDN/PC/{appVersion}";
#endif}/// <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}";}}#endregion#region 获取资源版本private IEnumerator UpdatePackageVersion(){// 发起异步版本请求RequestPackageVersionOperation operation = _package.RequestPackageVersionAsync();yield return operation;// 处理版本请求结果if (operation.Status != EOperationStatus.Succeed){Debug.LogWarning(operation.Error);}else{Debug.Log($"请求的版本: {operation.PackageVersion}");appVersion = operation.PackageVersion;}}#endregion#region 获取文件清单private IEnumerator UpdateManifest(){_operationManifest = _package.UpdatePackageManifestAsync(appVersion);yield return _operationManifest;// 处理文件清单结果if (_operationManifest.Status != EOperationStatus.Succeed){Debug.LogWarning(_operationManifest.Error);PatchEventDefine.PackageManifestUpdateFailed.SendEventMessage();yield break;}else{Debug.Log("更新资源清单成功-------------------");}}#endregion#region 创建资源下载器void CreateDownloader(){_downloader = _package.CreateResourceDownloader(downloadingMaxNum, filedTryAgain);if (_downloader.TotalDownloadCount == 0){Debug.Log("没有需要更新的文件");UpdateDone();}else{// 发现新更新文件后,挂起流程系统// 注意:开发者需要在下载前检测磁盘空间不足int count = _downloader.TotalDownloadCount;long bytes = _downloader.TotalDownloadBytes;Debug.Log($"需要更新{count}个文件, 大小是{bytes / 1024 / 1024}MB");}}#endregion#region 开始下载资源文件private IEnumerator BeginDownload(){_downloader.DownloadErrorCallback = DownloadErrorCallback;// 单个文件下载失败_downloader.DownloadUpdateCallback = DownloadUpdateCallback;// 下载进度更新_downloader.BeginDownload();//开始下载yield return _downloader;// 检测下载结果if (_downloader.Status != EOperationStatus.Succeed){Debug.LogWarning(_operationManifest.Error);yield break;}else{Debug.Log("下载成功-------------------");}}// 单个文件下载失败public static void DownloadErrorCallback(DownloadErrorData errorData){string fileName = errorData.FileName;string errorInfo = errorData.ErrorInfo;Debug.Log($"下载失败, 文件名: {fileName}, 错误信息: {errorInfo}");}// 下载进度更新public static void DownloadUpdateCallback(DownloadUpdateData updateData){int totalDownloadCount = updateData.TotalDownloadCount;int currentDownloadCount = updateData.CurrentDownloadCount;long totalDownloadSizeBytes = updateData.TotalDownloadBytes;long currentDownloadSizeBytes = updateData.CurrentDownloadBytes;Debug.Log($"下载进度: {currentDownloadCount}/{totalDownloadCount}, " +$"{currentDownloadSizeBytes / 1024}KB/{totalDownloadSizeBytes / 1024}KB");}#endregion#region 清理未使用的缓存文件void ClearFiles(){var operationClear = _package.ClearCacheFilesAsync(EFileClearMode.ClearUnusedBundleFiles);// 清理未使用的文件operationClear.Completed += Operation_Completed;// 添加清理完成回调}//文件清理完成private void Operation_Completed(AsyncOperationBase obj){UpdateDone();}#endregion#region 热更新结束回调private void UpdateDone(){Debug.Log("热更新结束");//跳转场景Debug.Log("跳转场景");//同步加载资源Sprite bg = _package.LoadAssetSync<Sprite>("Assets/Res/Image/1.jpg").AssetObject as Sprite;GameObject go = new GameObject();go.AddComponent<SpriteRenderer>().sprite = bg;}#endregion
}

7、挂载脚本运行游戏,可以看到热更和加载成功

在这里插入图片描述

在这里插入图片描述

8、其他加载资源方式

前面我们举例用的是同步加载资源方式,也可以选择其他的方式

8.1 使用委托异步加载资源

我们可以修改UpdateDone方法

#region 热更新结束回调
private void UpdateDone()
{Debug.Log("热更新结束");//跳转场景Debug.Log("跳转场景");// 同步加载资源// Sprite car = _package.LoadAssetSync<Sprite>("Assets/Res/Image/1.jpg")//     .AssetObject as Sprite;// GameObject go = new GameObject();// go.AddComponent<SpriteRenderer>().sprite = car;//异步委托资源加载AssetHandle handle = _package.LoadAssetAsync<Sprite>("Assets/Res/Image/1");handle.Completed += Handle_Completed;
}// 异步委托回调
private void Handle_Completed(AssetHandle handle)
{Sprite bg= handle.AssetObject as Sprite;GameObject go = new GameObject();go.AddComponent<SpriteRenderer>().sprite = bg;
}
#endregion

运行结果
在这里插入图片描述

8.2 协程加载资源

//热更新结束协程
IEnumerator UpdateDoneCoroutine()
{// 协程方式加载资源AssetHandle handle = _package.LoadAssetAsync<Sprite>("Assets/Res/Image/1");yield return handle;Sprite bg = handle.AssetObject as Sprite;GameObject go = new GameObject();go.AddComponent<SpriteRenderer>().sprite = bg;
}

8.3 Task加载资源

//热更新结束
private async void UpdateDone()
{// Task加载资源AssetHandle handle = _package.LoadAssetAsync<Sprite>("Assets/Res/Image/1");await handle.Task;Sprite bg = handle.AssetObject as Sprite;GameObject go = new GameObject();go.AddComponent<SpriteRenderer>().sprite = bg;
}

9、场景加载

新建一个场景Main,随便写一个字上去,做区分
在这里插入图片描述
配置新增的场景,并重新把AB包
在这里插入图片描述
在这里插入图片描述
打包好的AB包资源覆盖之前的服务器资源
在这里插入图片描述
修改UpdateDone方法,加载Main场景

#region 热更新结束回调
private async void UpdateDone()
{string scenePath = "Assets/Res/Scene/Main.unity"; // 场景路径var sceneMode = UnityEngine.SceneManagement.LoadSceneMode.Single; // 场景模式var physicsMode = UnityEngine.SceneManagement.LocalPhysicsMode.None; // 物理模式bool suspendLoad = false; // 场景加载到90%是否自动挂起SceneHandle handle = _package.LoadSceneAsync(scenePath, sceneMode, physicsMode, suspendLoad); // 加载场景await handle.Task; // 等待场景加载完成Debug.Log("Scene name is " + handle.SceneName); // 输出场景名称
}
#endregion

效果,可以看到运行后,资源加载完成了直接跳转到了Main场景
在这里插入图片描述

10、预制体加载和创建

随便新建一个预制体Cube
在这里插入图片描述
修改UpdateDone

private async void UpdateDone()
{//预制体加载和创建AssetHandle handle = _package.LoadAssetAsync<GameObject>("Assets/Res/Prefab/Cube.prefab");await handle.Task;//方式一GameObject go1 = Instantiate(handle.AssetObject as GameObject);Debug.Log("Prefab go1 name:" + go1.name);//方式二GameObject go2 = handle.InstantiateSync();Debug.Log("Prefab go2 name:" + go2.name);
}

重新打AB包放在服务器后,再运行
在这里插入图片描述

11、加载图片子对象切片

需要在2D图片中设置一个子图片切片叫1_1
在这里插入图片描述
修改UpdateDone

private async void UpdateDone()
{//图片子对象加载SubAssetsHandle handle = _package.LoadSubAssetsAsync<Sprite>("Assets/Res/Image/1");await handle.Task;var sprite = handle.GetSubAssetObject<Sprite>("1_1");new GameObject().AddComponent<SpriteRenderer>().sprite = sprite;Debug.Log("Sprite name: " + sprite.name);
}

重新打AB包放在服务器后,再运行
在这里插入图片描述

12、卸载相关

12.1 回收不再使用的资源

说明:卸载引用计数为零的资源

var operation = package.UnloadUnusedAssetsAsync();
await operation.Task;

12.2 强制回收所有资源

var operation = package.UnloadAllAssetsAsync();
await operation.Task;

12.3 尝试卸载指定的资源

说明:要未被引用的,否则无效

package.TryUnloadUnusedAsset("Assets/GameRes/Panel/login.prefab");

六、YooAsset资源加密解密

在YooAsset的包中,可以搜到加密解密的示例代码:加密解密的示例代码
在这里插入图片描述

1、加密资源对象

我们可以修改代码只对Image目录下的资源进行加密,加密偏移设置为32。

注意:BundleStream相关代码会在后面补充

using System;
using System.IO;
using YooAsset;/// <summary>
/// 文件流加密方式:针对资源文件的二进制数据进行逐字节异或加密
/// </summary>
public class FileStreamEncryption : IEncryptionServices
{/// <summary>/// 对传入的文件数据进行加密操作/// </summary>/// <param name="fileInfo">包含文件加载路径和包名等信息</param>/// <returns>返回加密结果,包括是否加密和加密后的数据</returns>public EncryptResult Encrypt(EncryptFileInfo fileInfo){// 只对包名包含 "Image" 字符串的资源进行加密// 注意:包名默认采用的是收集器的父目录名称, 并且大写要转换为下划线_if (fileInfo.BundleName.Contains("_image")){// 读取文件所有字节数据var fileData = File.ReadAllBytes(fileInfo.FileLoadPath);// 对每个字节执行异或运算,异或因子为32,实现简单加密for (int i = 0; i < fileData.Length; i++){//每个字节与密钥进行按位异或(XOR)运算。//异或加密的特点:解密时,只需再次异或同一个密钥即可恢复原始数据。fileData[i] ^= BundleStream.KEY; //KEY是32,BundleStream代码在后面补充}// 构建加密结果对象,标记为已加密并返回加密后的数据EncryptResult result = new EncryptResult();result.Encrypted = true;result.EncryptedData = fileData;return result;}else{// 如果包名不符合条件,不进行加密,返回未加密的结果EncryptResult result = new EncryptResult();result.Encrypted = false;return result;}}
}/// <summary>
/// 文件偏移加密方式:在原始文件数据前增加一定长度的偏移数据,实现简单的加密处理
/// </summary>
public class FileOffsetEncryption : IEncryptionServices
{/// <summary>/// 对传入的文件数据进行加密处理(数据偏移)/// </summary>/// <param name="fileInfo">包含文件加载路径和包名等信息</param>/// <returns>返回加密结果,包括是否加密和加密后的数据</returns>public EncryptResult Encrypt(EncryptFileInfo fileInfo){// 只对包名包含 "Image" 字符串的资源进行加密// 注意:包名默认采用的是收集器的父目录名称, 并且大写要转换为下划线_if (fileInfo.BundleName.Contains("_image")) //判断资源包是否包含 该字符串{// 设置偏移长度(字节数)int offset = 32;// 读取原始文件所有字节数据byte[] fileData = File.ReadAllBytes(fileInfo.FileLoadPath);// 创建一个新的字节数组,新数组长度为原数据长度加上偏移长度var encryptedData = new byte[fileData.Length + offset];// 将原始文件数据拷贝到新数组中,目标位置从偏移量开始(前面的偏移部分为空)//拷贝原始数据到加密数组中, 参数 源数据 源数据起始位置, 目标数组, 目标数组的起始位置, 拷贝的字节数//相当于前面空出了32字节, 剩下的全部往后挪 Buffer.BlockCopy(fileData, 0, encryptedData, offset, fileData.Length);// 构建加密结果对象,标记为已加密并返回带有偏移的新数据EncryptResult result = new EncryptResult();result.Encrypted = true;result.EncryptedData = encryptedData;return result;}else{// 如果包名不符合条件,不进行加密,返回未加密的结果EncryptResult result = new EncryptResult();result.Encrypted = false;return result;}}
}

打包时会多出两个选项,因为YooAsset会自动检查继承IEncryptionServices接口的类。
在这里插入图片描述
我们测试使用偏移加密进行打包,也就是选择FileOffsetEncryption,并将打包后的资源放在服务器。
在这里插入图片描述

2、解密资源对象

同样是复制TestBundleEncryption中的资源文件解密流BundleStream

using System.IO;/// <summary>
/// 资源文件解密流:通过继承 FileStream,实现读取数据时自动对数据进行异或解密处理
/// </summary>
public class BundleStream : FileStream
{// 异或解密的密钥常量,值为 32public const byte KEY = 32;/// <summary>/// 构造函数:以指定的路径、文件模式、文件访问权限和文件共享选项初始化 BundleStream 实例/// </summary>/// <param name="path">文件路径</param>/// <param name="mode">文件操作模式</param>/// <param name="access">文件访问权限</param>/// <param name="share">文件共享选项</param>public BundleStream(string path, FileMode mode, FileAccess access, FileShare share): base(path, mode, access, share){}/// <summary>/// 构造函数:以指定的路径和文件模式初始化 BundleStream 实例,使用默认访问权限和共享选项/// </summary>/// <param name="path">文件路径</param>/// <param name="mode">文件操作模式</param>public BundleStream(string path, FileMode mode): base(path, mode){}/// <summary>/// 重写 Read 方法:读取指定数量的字节到数组中,并对读取到的每个字节进行异或解密操作/// </summary>/// <param name="array">存储读取数据的字节数组</param>/// <param name="offset">数组中开始写入数据的偏移位置</param>/// <param name="count">要读取的最大字节数</param>/// <returns>实际读取的字节数</returns>public override int Read(byte[] array, int offset, int count){// 调用基类 FileStream 的 Read 方法,将数据读取到数组中var index = base.Read(array, offset, count);// 对数组中的所有字节进行异或解密操作(还原原始数据)for (int i = 0; i < array.Length; i++){array[i] ^= KEY;}// 返回实际读取的字节数return index;}
}

以及资源文件流解密类FileStreamDecryption和资源文件偏移解密类。

using System.IO;
using UnityEngine;
using YooAsset;/// <summary>
/// 资源文件流解密类
/// 该类实现了 IDecryptionServices 接口,通过自定义流(BundleStream)对加密的资源包进行解密操作。
/// </summary>
public class FileStreamDecryption : IDecryptionServices
{/// <summary>/// 同步方式加载解密的资源包对象/// 注意:加载的流对象会在资源包对象释放时自动释放,无需手动管理流生命周期/// </summary>/// <param name="fileInfo">包含资源包加载路径、CRC校验码等信息</param>/// <returns>返回包含解密流和加载结果的 DecryptResult 对象</returns>DecryptResult IDecryptionServices.LoadAssetBundle(DecryptFileInfo fileInfo){// 创建自定义的 BundleStream 对象,用于解密文件数据BundleStream bundleStream =new BundleStream(fileInfo.FileLoadPath, FileMode.Open, FileAccess.Read, FileShare.Read);// 构建解密结果对象DecryptResult decryptResult = new DecryptResult();// 保存解密流,便于后续自动释放decryptResult.ManagedStream = bundleStream;// 从解密流中同步加载资源包,传入 CRC 校验码和自定义的读取缓冲区大小decryptResult.Result =AssetBundle.LoadFromStream(bundleStream, fileInfo.FileLoadCRC, GetManagedReadBufferSize());return decryptResult;}/// <summary>/// 异步方式加载解密的资源包对象/// 注意:加载的流对象会在资源包对象释放时自动释放,无需手动管理流生命周期/// </summary>/// <param name="fileInfo">包含资源包加载路径、CRC校验码等信息</param>/// <returns>返回包含解密流和异步加载请求的 DecryptResult 对象</returns>DecryptResult IDecryptionServices.LoadAssetBundleAsync(DecryptFileInfo fileInfo){// 创建自定义的 BundleStream 对象,用于解密文件数据BundleStream bundleStream =new BundleStream(fileInfo.FileLoadPath, FileMode.Open, FileAccess.Read, FileShare.Read);// 构建解密结果对象DecryptResult decryptResult = new DecryptResult();// 保存解密流,便于后续自动释放decryptResult.ManagedStream = bundleStream;// 从解密流中异步加载资源包,传入 CRC 校验码和自定义的读取缓冲区大小decryptResult.CreateRequest =AssetBundle.LoadFromStreamAsync(bundleStream, fileInfo.FileLoadCRC, GetManagedReadBufferSize());return decryptResult;}/// <summary>/// 获取解密后的字节数据/// 当前未实现该方法,调用时会抛出 NotImplementedException 异常/// </summary>/// <param name="fileInfo">包含解密文件相关信息</param>/// <returns>解密后的字节数组</returns>byte[] IDecryptionServices.ReadFileData(DecryptFileInfo fileInfo){throw new System.NotImplementedException();}/// <summary>/// 获取解密后的文本数据/// 当前未实现该方法,调用时会抛出 NotImplementedException 异常/// </summary>/// <param name="fileInfo">包含解密文件相关信息</param>/// <returns>解密后的文本字符串</returns>string IDecryptionServices.ReadFileText(DecryptFileInfo fileInfo){throw new System.NotImplementedException();}/// <summary>/// 获取用于 AssetBundle 加载时的读取缓冲区大小/// </summary>/// <returns>缓冲区大小(单位:字节)</returns>private static uint GetManagedReadBufferSize(){return 1024;}
}/// <summary>
/// 资源文件偏移解密类
/// 该类实现了 IDecryptionServices 接口,通过文件偏移的方式加载资源包,无需对流数据进行解密处理
/// </summary>
public class FileOffsetDecryption : IDecryptionServices
{/// <summary>/// 同步方式加载资源包对象/// 注意:该方式直接通过文件路径加载资源包,加载时会跳过文件开头的偏移数据/// </summary>/// <param name="fileInfo">包含资源包加载路径、CRC校验码等信息</param>/// <returns>返回包含加载结果的 DecryptResult 对象</returns>DecryptResult IDecryptionServices.LoadAssetBundle(DecryptFileInfo fileInfo){// 构建解密结果对象,ManagedStream 设为 null(无自定义流)DecryptResult decryptResult = new DecryptResult();decryptResult.ManagedStream = null;// 从文件中同步加载资源包,跳过文件偏移部分decryptResult.Result = AssetBundle.LoadFromFile(fileInfo.FileLoadPath, fileInfo.FileLoadCRC, GetFileOffset());return decryptResult;}/// <summary>/// 异步方式加载资源包对象/// 注意:该方式直接通过文件路径加载资源包,加载时会跳过文件开头的偏移数据/// </summary>/// <param name="fileInfo">包含资源包加载路径、CRC校验码等信息</param>/// <returns>返回包含异步加载请求的 DecryptResult 对象</returns>DecryptResult IDecryptionServices.LoadAssetBundleAsync(DecryptFileInfo fileInfo){// 构建解密结果对象,ManagedStream 设为 null(无自定义流)DecryptResult decryptResult = new DecryptResult();decryptResult.ManagedStream = null;// 从文件中异步加载资源包,跳过文件偏移部分decryptResult.CreateRequest =AssetBundle.LoadFromFileAsync(fileInfo.FileLoadPath, fileInfo.FileLoadCRC, GetFileOffset());return decryptResult;}/// <summary>/// 获取解密后的字节数据/// 当前未实现该方法,调用时会抛出 NotImplementedException 异常/// </summary>/// <param name="fileInfo">包含解密文件相关信息</param>/// <returns>解密后的字节数组</returns>byte[] IDecryptionServices.ReadFileData(DecryptFileInfo fileInfo){throw new System.NotImplementedException();}/// <summary>/// 获取解密后的文本数据/// 当前未实现该方法,调用时会抛出 NotImplementedException 异常/// </summary>/// <param name="fileInfo">包含解密文件相关信息</param>/// <returns>解密后的文本字符串</returns>string IDecryptionServices.ReadFileText(DecryptFileInfo fileInfo){throw new System.NotImplementedException();}/// <summary>/// 获取文件偏移量,表示资源包文件开头需要跳过的字节数/// </summary>/// <returns>偏移字节数</returns>private static ulong GetFileOffset(){return 32;}
}

3、使用解密对象解密加密的资源

在MyYooAssetTest,创建联机模式参数,并设置内置及缓存文件系统参数时传入偏移解密对象FileOffsetDecryption

// 创建联机模式参数,并设置内置及缓存文件系统参数
HostPlayModeParameters createParameters = new HostPlayModeParameters
{//创建内置文件系统参数BuildinFileSystemParameters = FileSystemParameters.CreateDefaultBuildinFileSystemParameters(),//创建缓存系统参数CacheFileSystemParameters = FileSystemParameters.CreateDefaultCacheFileSystemParameters(remoteServices,new FileOffsetDecryption())
};

其他还是用之前实践的代码

运行效果
在这里插入图片描述
如果改了偏移量,比如前面加密用的32,这里解密我们改成64。加密解密的秘钥对不上,则会加载失败。

/// <summary>
/// 获取文件偏移量,表示资源包文件开头需要跳过的字节数
/// </summary>
/// <returns>偏移字节数</returns>
private static ulong GetFileOffset()
{return 64;
}

运行效果
在这里插入图片描述


专栏推荐

地址
【unity游戏开发入门到精通——C#篇】
【unity游戏开发入门到精通——unity通用篇】
【unity游戏开发入门到精通——unity3D篇】
【unity游戏开发入门到精通——unity2D篇】
【unity实战】
【制作100个Unity游戏】
【推荐100个unity插件】
【实现100个unity特效】
【unity框架/工具集开发】
【unity游戏开发——模型篇】
【unity游戏开发——InputSystem】
【unity游戏开发——Animator动画】
【unity游戏开发——UGUI】
【unity游戏开发——联网篇】
【unity游戏开发——优化篇】
【unity游戏开发——shader篇】
【unity游戏开发——编辑器扩展】
【unity游戏开发——热更新】

完结

好了,我是向宇,博客地址:https://xiangyu.blog.csdn.net,如果学习过程中遇到任何问题,也欢迎你评论私信找我。

赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/85853.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/85853.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AWS RDS/Aurora 开启 Database Insights 高级模式全攻略

想要深入了解数据库性能问题?AWS Database Insights 高级模式为您提供强大的性能分析工具。本文详细对比标准模式与高级模式的功能差异,并提供完整的启用指南和实战测试结果。 一、Database Insights 模式对比 AWS CloudWatch Database Insights 提供两种模式:标准模式和高…

XML SimpleXML

XML SimpleXML 引言 XML&#xff08;可扩展标记语言&#xff09;是一种用于存储和传输数据的标记语言&#xff0c;它被广泛应用于Web服务和数据交换。SimpleXML是PHP中一个处理XML数据非常便捷的库。本文将详细介绍SimpleXML库的基本用法&#xff0c;帮助读者快速掌握XML数据…

Docker简单介绍与使用以及下载对应镜像(项目前置)

DockerDocker安装Docker卸载Docker配置镜像源配置镜像加速 Docker服务命令1.镜像操作命令2.容器操作命令 安装Mysql**数据卷挂载** Docker 在linux中软件安装说起: 以前在linux中安装软件,是直接安装在linux操作系统中,软件和操作系统耦合度很高,不方便管理. 因为linux版本不…

MyBatis 简介

MyBatis 简介 MyBatis 是一款优秀的持久层框架&#xff0c;它支持定制化 SQL、存储过程以及高级映射&#xff0c;能够帮助开发者将 Java 对象与数据库表进行灵活映射&#xff0c;简化数据持久化操作。以下从多个维度详细介绍 MyBatis&#xff1a; 一、核心定位与优势 轻量级…

自监督学习在合成孔径声呐目标识别中的应用之论文阅读

自监督学习在合成孔径声呐目标识别中的应用 BW Sheffield 美国巴拿马城海军水面作战中心 1 引言 在自主水下航行器(AUVs)中应用计算机视觉面临着独特的挑战,因为海洋环境往往条件不可预测且极为严苛。传统计算机视觉研究主要依赖光学相机成像,而在光照不足、悬浮沉积物及水…

进程间通信2(命名管道)linux

1 命名管道 前面讲到匿名管道&#xff0c;有一个很大的限制&#xff0c;那就是只有具有相同祖先&#xff08;具有亲缘关系&#xff09;的进程间才能进行通信&#xff0c;但是如果想实现不同进程间的通信&#xff0c;这个时候命名管道就发挥着巨大作用。 命名管道是一种特殊类…

简单通过SenseVoice给自己配置一个语音转文字服务

首先把代码下载下来 gitgithub.com:FunAudioLLM/SenseVoice.git 然后写一个docker文件 FROM ubuntu:latestRUN apt-get update -y RUN apt-get install -y python3-full python3-pip RUN mkdir -p /SenseVoice WORKDIR /SenseVoice RUN python3 -m venv . ENV USE_CUDA0 EXP…

网络钓鱼攻击

​根据2023年Proofpoint年度网络钓鱼报告显示&#xff1a;91%的针对性攻击始于钓鱼邮件&#xff0c;平均每30秒就有一个企业成为攻击目标&#xff0c;全球损失超过$6.5B​ 一、钓鱼攻击技术深度解析 1. 钓鱼攻击核心技术架构 2. 现代钓鱼技术演进 ​攻击向量升级路线​ ​当前…

uvicorn api:app --host 0.0.0.0 --port 7777容器运行失败

docker logs pycorrector-container-gpu 你这个报错的核心是&#xff1a; ERROR: Error loading ASGI app, Could not import module "api".这说明&#xff1a; uvicorn api:app 没有找到 api.py 文件&#xff0c;或者没法导入 app 对象。 &#x1f50d; 一步步排查…

热成像仪测MOSFET温度

热成像仪测MOSFET温度 根据提供的搜索结果&#xff0c;热测量方法主要分为非接触式和接触式两大类&#xff0c;针对不同材料特性和测量场景各有优势。以下是核心方法的总结及关键技术要点&#xff1a; &#x1f525; 一、非接触式热测量方法 红外热成像技术 原理&#xff1a;通…

Dagster资产元数据与标签:数据治理的利器

在现代数据栈中&#xff0c;有效的数据治理至关重要。Dagster作为领先的数据编排平台&#xff0c;提供了强大的资产元数据和标签功能&#xff0c;帮助团队更好地理解、组织和跟踪数据资产。本文将深入探讨Dagster中的资产元数据和标签功能&#xff0c;展示如何利用这些功能提升…

基于物联网的智能饮水机系统设计

标题:基于物联网的智能饮水机系统设计 内容:1.摘要 随着物联网技术的快速发展&#xff0c;智能设备在日常生活中的应用越来越广泛。本研究的目的是设计一种基于物联网的智能饮水机系统&#xff0c;以提高饮水机的使用便捷性和智能化程度。方法上&#xff0c;通过传感器实时监测…

DP读书:NEC年终小结和显示器【明基rd28u】

一点真实体验_写在ROBOCON2025国赛前 很久没有写这种关于感受的博客了&#xff0c;就用真实感受的角度来看看一次众测的经历&#xff0c;哈哈^ 差不多一个月前&#xff0c;我收到了明基的28寸显示器&#xff0c;体验了差不多2周左右&#xff0c;一直把显示器挂在实验室仓库La…

Solana 一键冷分仓机制详解:如何用技术手段构建健康的持仓结构

在 Solana 的快速发币环境中&#xff0c;许多项目方在成功部署代币后&#xff0c;会面临一个共通问题——如何避免持仓结构过于集中。无论是初始铸造的 Token、流动性预留份额&#xff0c;还是空投分发的准备金&#xff0c;如果长时间停留在单一钱包地址中&#xff0c;将在链上…

【智能体】dify部署本地步骤

从git克隆仓库到本地 git clone https://github.com/langgenius/dify.git设置环境变量 cd dify cd docker cp .env.example .envdocker启动 docker compose up -d在浏览器打开网址 http://localhost/install 登录后即可使用

开源鸿蒙6.0 Beta1版本发布!深圳触觉智能即将适配RK3566/RK3568/RK3576/RK3588等芯片

开放原子开源鸿蒙&#xff08;OpenAtom OpenHarmony&#xff0c;简称“开源鸿蒙”或“OpenHarmony”&#xff09;6.0 Beta1版本正式发布。相比5.1.0 Release版本进一步增强ArkUI组件能力&#xff0c;提供更安全、更灵活的组件布局&#xff1b;增强分布式数据管理能力&#xff0…

机器学习 (ML) 基础入门指南

一、机器学习概述 &#xff08;一&#xff09;定义 在当今科技飞速发展的时代&#xff0c;机器学习作为人工智能的一个重要分支&#xff0c;正深刻地改变着我们的生活和工作方式。根据机器学习泰斗、卡耐基梅隆大学的汤姆米切尔 (Tom Mitchell) 教授的定义&#xff0c;机器学…

基于ARM ubuntu如何进行交叉编译

场景总结&#xff1a; 平台&#xff1a;x86 主机 工具链&#xff1a;aarch64-linux-gnu-gcc&#xff08;用于编译 64-bit ARM 程序&#xff09; 目标&#xff1a;让 gcc 自动使用 ARM Ubuntu rootfs 中的头文件和库&#xff08;位于 /opt/arm64-ubuntu&#xff09; 不希望每…

java+vue+SpringBoo社区药房系统(程序+数据库+报告+部署教程+答辩指导)

源代码数据库LW文档&#xff08;1万字以上&#xff09;开题报告答辩稿ppt部署教程代码讲解代码时间修改工具 技术实现 开发语言&#xff1a;后端&#xff1a;Java 前端&#xff1a;vue框架&#xff1a;springboot数据库&#xff1a;mysql 开发工具 JDK版本&#xff1a;JDK1.…

VS2022打Unity中的脚本断点时出现当前不会命中断点,找不到相应位置

今天遇到一个很傻的问题&#xff0c; 在新电脑中安装了Unity和VS2022后&#xff0c;在Unity中打开一个新脚本&#xff0c;打断点时报警告&#xff0c;如下&#xff1a; 原来在Unity中新建的脚本&#xff0c;如果没有被使用&#xff0c;就会出现找不到位置的错误&#xff01; 反…