6.YooAsset资源热更新实践
6.1 知识点
导入资源
导入一些资源作为热更新的资源
手写热更新流程
创建场景和主入口,逐步调用YooAsset对应API模拟热更流程
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.Serialization;
using YooAsset;
public class MyYooAssetTest : MonoBehaviour
{
public EPlayMode playMode = EPlayMode.HostPlayMode; //运行模式
public string packageName = "DefaultPackage"; //默认包名
public string packageVersion = ""; //服务器资源版本号
private ResourcePackage _package = null; //资源包对象
//网络相关
public string defaultHostServer = "http://127.0.0.1/CDN/PC/v1.0";
public string fallbackHostServer = "http://127.0.0.1/CDN/PC/v1.0";
//下载相关
public int downloadingMaxNum = 10;
public int filedTryAgain = 3;
private ResourceDownloaderOperation _downloader;
private void Awake()
{
DontDestroyOnLoad(gameObject);
}
IEnumerator Start()
{
yield return null;
//1.初始化YooAsset
YooAssets.Initialize();
// 获取或创建资源包对象
_package = YooAssets.TryGetPackage(packageName);
if (_package == null)
{
_package = YooAssets.CreatePackage(packageName);
}
// 创建远端服务实例,用于资源请求
IRemoteServices remoteServices = new RemoteServices(defaultHostServer, fallbackHostServer);
// 创建联机模式参数,并设置内置及缓存文件系统参数
HostPlayModeParameters createParameters = new HostPlayModeParameters
{
//创建内置文件系统参数
BuildinFileSystemParameters = FileSystemParameters.CreateDefaultBuildinFileSystemParameters(),
//创建缓存系统参数
CacheFileSystemParameters = FileSystemParameters.CreateDefaultCacheFileSystemParameters(remoteServices)
};
//执行异步初始化
InitializationOperation initializationOperation =
_package.InitializeAsync(createParameters);
yield return initializationOperation;
// 处理初始化结果
if (initializationOperation.Status != EOperationStatus.Succeed)
{
Debug.LogWarning(initializationOperation.Error);
}
else
{
Debug.Log("初始化成功-------------------------");
}
//2.获取资源版本
// 发起异步版本请求
RequestPackageVersionOperation operation = _package.RequestPackageVersionAsync();
yield return operation;
// 处理版本请求结果
if (operation.Status != EOperationStatus.Succeed)
{
Debug.LogWarning(operation.Error);
}
else
{
Debug.Log($"请求的版本: {operation.PackageVersion}");
packageVersion = operation.PackageVersion;
}
//3.获取文件清单
UpdatePackageManifestOperation operationManifest = _package.UpdatePackageManifestAsync(packageVersion);
yield return operationManifest;
// 处理文件清单结果
if (operationManifest.Status != EOperationStatus.Succeed)
{
Debug.LogWarning(operationManifest.Error);
}
else
{
Debug.Log("更新资源清单成功-------------------");
}
//4.创建下载器
_downloader = _package.CreateResourceDownloader(downloadingMaxNum, filedTryAgain);
if (_downloader.TotalDownloadCount == 0)
{
Debug.Log("没有需要更新的文件");
UpdateDone();
StartCoroutine(UpdateDoneCoroutine());
yield break;
}
else
{
int count = _downloader.TotalDownloadCount;
long bytes = _downloader.TotalDownloadBytes;
Debug.Log($"需要更新{count}个文件, 大小是{bytes / 1024 / 1024}MB");
}
//5.开始下载
_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("下载成功-------------------");
}
//6.清理文件
// 清理未使用的文件
var operationClear = _package.ClearCacheFilesAsync(EFileClearMode.ClearUnusedBundleFiles);
// 添加清理完成回调
operationClear.Completed += Operation_Completed;
}
//热更新结束
private async void UpdateDone()
{
Debug.Log("热更新结束");
//跳转场景
//模拟资源加载
}
//热更新结束协程
IEnumerator UpdateDoneCoroutine()
{
yield return null;
}
// 单个文件下载失败
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");
}
//文件清理完成
private void Operation_Completed(AsyncOperationBase obj)
{
UpdateDone();
StartCoroutine(UpdateDoneCoroutine());
}
}
将汽车图片打包中
同步加载资源
//热更新结束
private async void UpdateDone()
{
Debug.Log("热更新结束");
//跳转场景
//模拟资源加载
Sprite car = _package.LoadAssetSync<Sprite>("Assets/AB/Image/汽车.png")
.AssetObject as Sprite;
// Sprite car = _package.LoadAssetSync<Sprite>("Assets/AB/Image/汽车")
// .AssetObject as Sprite;//后缀名可以省略
GameObject go = new GameObject();
go.AddComponent<SpriteRenderer>().sprite = car;
}
开启运行从http下载
复制打包出来的资源到本地资源服务器,启动本地资源服务器
运行游戏,可以看到热更和加载成功
委托异步加载资源
//热更新结束
private async void UpdateDone()
{
Debug.Log("热更新结束");
//异步委托资源加载
AssetHandle handle = _package.LoadAssetAsync<Sprite>("Assets/AB/Image/汽车");
handle.Completed += Handle_Completed;
}
// 异步委托回调
private void Handle_Completed(AssetHandle handle)
{
Sprite car = handle.AssetObject as Sprite;
GameObject go = new GameObject();
go.AddComponent<SpriteRenderer>().sprite = car;
}
协程加载资源
//热更新结束协程
IEnumerator UpdateDoneCoroutine()
{
// 协程方式加载资源
AssetHandle handle = _package.LoadAssetAsync<Sprite>("Assets/AB/Image/汽车");
yield return handle;
Sprite car = handle.AssetObject as Sprite;
GameObject go = new GameObject();
go.AddComponent<SpriteRenderer>().sprite = car;
}
Task加载资源
//热更新结束
private async void UpdateDone()
{
// Task加载资源
AssetHandle handle = _package.LoadAssetAsync<Sprite>("Assets/AB/Image/汽车");
await handle.Task;
Sprite car = handle.AssetObject as Sprite;
GameObject go = new GameObject();
go.AddComponent<SpriteRenderer>().sprite = car;
}
场景加载
//热更新结束
private async void UpdateDone()
{
// 场景加载
string scenePath = "Assets/AB/Scene/Main.unity"; //
var sceneMode = UnityEngine.SceneManagement.LoadSceneMode.Single;
var physicsMode = LocalPhysicsMode.None;
bool suspendLoad = false;
SceneHandle handle = _package.LoadSceneAsync(scenePath, sceneMode, physicsMode, suspendLoad);
await handle.Task;
Debug.Log("Scene name is " + handle.SceneName);
}
需要重新打AB包放在服务器后,再运行
预制体加载和创建
//热更新结束
private async void UpdateDone()
{
//预制体加载和创建
AssetHandle handle = _package.LoadAssetAsync<GameObject>
("Assets/AB/Prefab/Car.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包放在服务器后,再运行
图片子对象加载
//热更新结束
private async void UpdateDone()
{
//图片子对象加载
SubAssetsHandle handle =
_package.LoadSubAssetsAsync<Sprite>("Assets/AB/Image/Farms");
await handle.Task;
var sprite = handle.GetSubAssetObject<Sprite>("hen");
new GameObject().AddComponent<SpriteRenderer>().sprite = sprite;
Debug.Log("Sprite name: " + sprite.name);
}
需要在2D图片中设置一个子图片叫hen
需要重新打AB包放在服务器后,再运行
卸载相关
//卸载未使用的资源包
var operation = package.UnloadUnusedAssetsAsync();
await operation.Task;
//强制卸载资源包
var operation = package.UnloadAllAssetsAsync();
await operation.Task;
//卸载资源包中某个资源 (要未被引用的,否则无效)
package.TryUnloadUnusedAsset("Assets/GameRes/Panel/login.prefab");
6.2 知识点代码
RemoteServices.cs
using YooAsset;
public 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}";
}
}
MyYooAssetTest.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.Serialization;
using YooAsset;
public class MyYooAssetTest : MonoBehaviour
{
public EPlayMode playMode = EPlayMode.HostPlayMode; //运行模式
public string packageName = "DefaultPackage"; //默认包名
public string packageVersion = ""; //服务器资源版本号
private ResourcePackage _package = null; //资源包对象
//网络相关
public string defaultHostServer = "http://127.0.0.1/CDN/PC/v1.0";
public string fallbackHostServer = "http://127.0.0.1/CDN/PC/v1.0";
//下载相关
public int downloadingMaxNum = 10;
public int filedTryAgain = 3;
private ResourceDownloaderOperation _downloader;
private void Awake()
{
DontDestroyOnLoad(gameObject);
}
IEnumerator Start()
{
yield return null;
//1.初始化YooAsset
YooAssets.Initialize();
// 获取或创建资源包对象
_package = YooAssets.TryGetPackage(packageName);
if (_package == null)
{
_package = YooAssets.CreatePackage(packageName);
}
// 创建远端服务实例,用于资源请求
IRemoteServices remoteServices = new RemoteServices(defaultHostServer, fallbackHostServer);
// 创建联机模式参数,并设置内置及缓存文件系统参数
HostPlayModeParameters createParameters = new HostPlayModeParameters
{
//创建内置文件系统参数
BuildinFileSystemParameters = FileSystemParameters.CreateDefaultBuildinFileSystemParameters(),
//创建缓存系统参数
CacheFileSystemParameters = FileSystemParameters.CreateDefaultCacheFileSystemParameters(remoteServices)
};
//执行异步初始化
InitializationOperation initializationOperation =
_package.InitializeAsync(createParameters);
yield return initializationOperation;
// 处理初始化结果
if (initializationOperation.Status != EOperationStatus.Succeed)
{
Debug.LogWarning(initializationOperation.Error);
}
else
{
Debug.Log("初始化成功-------------------------");
}
//2.获取资源版本
// 发起异步版本请求
RequestPackageVersionOperation operation = _package.RequestPackageVersionAsync();
yield return operation;
// 处理版本请求结果
if (operation.Status != EOperationStatus.Succeed)
{
Debug.LogWarning(operation.Error);
}
else
{
Debug.Log($"请求的版本: {operation.PackageVersion}");
packageVersion = operation.PackageVersion;
}
//3.获取文件清单
UpdatePackageManifestOperation operationManifest = _package.UpdatePackageManifestAsync(packageVersion);
yield return operationManifest;
// 处理文件清单结果
if (operationManifest.Status != EOperationStatus.Succeed)
{
Debug.LogWarning(operationManifest.Error);
}
else
{
Debug.Log("更新资源清单成功-------------------");
}
//4.创建下载器
_downloader = _package.CreateResourceDownloader(downloadingMaxNum, filedTryAgain);
if (_downloader.TotalDownloadCount == 0)
{
Debug.Log("没有需要更新的文件");
UpdateDone();
StartCoroutine(UpdateDoneCoroutine());
yield break;
}
else
{
int count = _downloader.TotalDownloadCount;
long bytes = _downloader.TotalDownloadBytes;
Debug.Log($"需要更新{count}个文件, 大小是{bytes / 1024 / 1024}MB");
}
//5.开始下载
_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("下载成功-------------------");
}
//6.清理文件
// 清理未使用的文件
var operationClear = _package.ClearCacheFilesAsync(EFileClearMode.ClearUnusedBundleFiles);
// 添加清理完成回调
operationClear.Completed += Operation_Completed;
}
//热更新结束
private async void UpdateDone()
{
Debug.Log("热更新结束");
//跳转场景
//模拟资源加载
// Sprite car = _package.LoadAssetSync<Sprite>("Assets/AB/Image/汽车.png")
// .AssetObject as Sprite;
// // Sprite car = _package.LoadAssetSync<Sprite>("Assets/AB/Image/汽车")
// // .AssetObject as Sprite;//后缀名可以省略
// GameObject go = new GameObject();
// go.AddComponent<SpriteRenderer>().sprite = car;
//异步委托资源加载
// AssetHandle handle = _package.LoadAssetAsync<Sprite>("Assets/AB/Image/汽车");
// handle.Completed += Handle_Completed;
// task加载资源
// AssetHandle handle = _package.LoadAssetAsync<Sprite>("Assets/AB/Image/汽车");
// await handle.Task;
//
// Sprite car = handle.AssetObject as Sprite;
// GameObject go = new GameObject();
// go.AddComponent<SpriteRenderer>().sprite = car;
//获取所有信息
// AssetInfo[] assetInfos = package.GetAssetInfos("hot");
// foreach (var assetInfo in assetInfos)
// {
// Debug.Log(assetInfo.AssetPath);
// }
// 场景加载
// string scenePath = "Assets/AB/Scene/Main.unity"; //
// var sceneMode = UnityEngine.SceneManagement.LoadSceneMode.Single;
// var physicsMode = LocalPhysicsMode.None;
// bool suspendLoad = false;
// SceneHandle handle = _package.LoadSceneAsync(scenePath, sceneMode, physicsMode, suspendLoad);
// await handle.Task;
// Debug.Log("Scene name is " + handle.SceneName);
//预制体加载和创建
// AssetHandle handle = _package.LoadAssetAsync<GameObject>
// ("Assets/AB/Prefab/Car.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);
//图片子对象加载
SubAssetsHandle handle =
_package.LoadSubAssetsAsync<Sprite>("Assets/AB/Image/Farms");
await handle.Task;
var sprite = handle.GetSubAssetObject<Sprite>("hen");
new GameObject().AddComponent<SpriteRenderer>().sprite = sprite;
Debug.Log("Sprite name: " + sprite.name);
//卸载未使用的资源包
// var operation = package.UnloadUnusedAssetsAsync();
// await operation.Task;
//强制卸载资源包
// var operation = package.UnloadAllAssetsAsync();
// await operation.Task;
//卸载资源包中某个资源 (要未被引用的,否则无效)
// package.TryUnloadUnusedAsset("Assets/GameRes/Panel/login.prefab");
//
}
//热更新结束协程
IEnumerator UpdateDoneCoroutine()
{
yield return null;
// 协程方式加载资源
// AssetHandle handle = _package.LoadAssetAsync<Sprite>("Assets/AB/Image/汽车");
// yield return handle;
// Sprite car = handle.AssetObject as Sprite;
// GameObject go = new GameObject();
// go.AddComponent<SpriteRenderer>().sprite = car;
}
// 单个文件下载失败
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");
}
//文件清理完成
private void Operation_Completed(AsyncOperationBase obj)
{
UpdateDone();
StartCoroutine(UpdateDoneCoroutine());
}
// 异步委托回调
private void Handle_Completed(AssetHandle handle)
{
Sprite car = handle.AssetObject as Sprite;
GameObject go = new GameObject();
go.AddComponent<SpriteRenderer>().sprite = car;
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com