初学者,自用笔记
热更完整链路:标记 → 构建 → 上传 → 下载 → 依赖解析 → 加载 → 卸载 → 热更
YooAsset
核心管理类
整个热更 / 资源管理的总入口,全局唯一,静态调用
功能:初始化、检查下载、下载、加载 / 卸载资源、场景管理、清理缓存
public static class YooAssetManager
{
// 默认包名
public const string DefaultPackageName = "DefaultPackage";
// 资源版本
public static string ReVersion { get; private set; }
// 是否初始化完成
public static bool IsInitialized { get; private set; }
private static ResourcePackage _defaultPackage;
private static ResourceDownloaderOperation _downloader;
// 资源引用结构(用于引用计数)
private class AssetRef
{
public AssetHandle Handle; // 资源句柄
public int RefCount; // 引用次数
}
// 资源引用池
private static readonly Dictionary<string, AssetRef> _assetRefs = new();
// 场景句柄池
private static readonly List<SceneHandle> _sceneHandles = new();
// 1. 初始化YooAsset(只初始化,不下载)
public static async UniTask<bool> InitializeAsync(EPlayMode playMode)
{
if (IsInitialized) return true;
try
{
// 初始化YooAsset底层
YooAssets.Initialize();
// 获取或创建默认包
_defaultPackage = YooAssets.TryGetPackage(DefaultPackageName) ?? YooAssets.CreatePackage(DefaultPackageName);
YooAssets.SetDefaultPackage(_defaultPackage);
// 初始化对应运行模式
var initializer = PackageInitializerRegistry.Get(playMode);
await initializer.InitializeAsync(_defaultPackage);
// 更新资源清单
await UpdatePackageManifestAsync();
IsInitialized = true;
Debug.Log("初始化完成");
return true;
}
catch
{
Debug.LogError("初始化失败");
return false;
}
}
// 2. 检查需要下载的资源总大小
public static async UniTask<long> CheckDownloadSizeAsync()
{
if (!IsInitialized) return 0;
// 创建下载器
_downloader = _defaultPackage.CreateResourceDownloader(10, 3);
await _downloader;
// 没有可下载资源
if (_downloader.TotalDownloadCount <= 0)
return 0;
return _downloader.TotalDownloadBytes;
}
// 3. 开始下载资源
public static async UniTask<bool> DownloadAsync(Action<long, long> onProgress, Action<bool> onComplete, Action<string> onError)
{
try
{
// 无需要下载
if (_downloader == null || _downloader.TotalDownloadCount <= 0)
{
onComplete?.Invoke(true);
return true;
}
// 注册下载进度回调
_downloader.DownloadUpdateCallback = data => onProgress?.Invoke(data.CurrentDownloadBytes, data.TotalDownloadBytes);
// 注册错误回调
_downloader.DownloadErrorCallback = data => onError?.Invoke(data.ErrorInfo);
// 开始下载并等待完成
_downloader.BeginDownload();
await _downloader;
bool success = _downloader.Status == EOperationStatus.Succeed;
onComplete?.Invoke(success);
return success;
}
catch (Exception e)
{
onError?.Invoke(e.Message);
return false;
}
}
// 加载资源(带引用计数)
public static async UniTask<T> LoadAssetAsync<T>(string assetPath) where T : UnityEngine.Object
{
// 如果已加载,引用+1
if (_assetRefs.TryGetValue(assetPath, out var assetRef))
{
assetRef.RefCount++;
return assetRef.Handle.AssetObject as T;
}
// 异步加载资源
var handle = _defaultPackage.LoadAssetAsync<T>(assetPath);
await handle;
if (handle.Status != EOperationStatus.Succeed)
{
Debug.LogError("加载失败:" + assetPath);
return null;
}
// 存入引用池
_assetRefs[assetPath] = new AssetRef
{
Handle = handle,
RefCount = 1
};
return handle.AssetObject as T;
}
// 卸载资源(引用-1,为0时真正释放)
public static void UnloadAsset(string assetPath)
{
if (!_assetRefs.TryGetValue(assetPath, out var assetRef))
return;
assetRef.RefCount--;
// 没人使用了,释放资源
if (assetRef.RefCount <= 0)
{
assetRef.Handle.Release();
_assetRefs.Remove(assetPath);
}
}
// 卸载所有资源
public static void UnloadAllAssets()
{
foreach (var assetRef in _assetRefs.Values)
assetRef.Handle.Release();
_assetRefs.Clear();
Resources.UnloadUnusedAssets();
}
// 加载场景
public static async UniTask LoadSceneAsync(string sceneName, LoadSceneMode mode = LoadSceneMode.Single)
{
var handle = _defaultPackage.LoadSceneAsync(sceneName, mode);
await handle;
_sceneHandles.Add(handle);
}
// 卸载所有场景
public static void UnloadAllScenes()
{
foreach (var handle in _sceneHandles)
handle.Release();
_sceneHandles.Clear();
Resources.UnloadUnusedAssets();
}
// 清理所有AB缓存
public static async UniTask ClearAllCacheAsync()
{
var op = _defaultPackage.ClearCacheFilesAsync(EFileClearMode.ClearAllBundleFiles);
await op;
Debug.Log(op.Status == EOperationStatus.Succeed ? "缓存清理成功" : "失败");
}
// 清理版本AB缓存
public static async UniTask ClearCacheByVersionAsync(string version)
{
var clearOp = _defaultPackage.ClearCacheFilesAsync(version);
await clearOp;
Debug.Log(clearOp.Status == EOperationStatus.Succeed ? $"缓存[{version}]清理成功" : "清理失败");
}
// 更新资源清单
private static async UniTask UpdatePackageManifestAsync()
{
var versionOp = _defaultPackage.RequestPackageVersionAsync();
await versionOp;
ReVersion = versionOp.PackageVersion;
await _defaultPackage.UpdatePackageManifestAsync(ReVersion);
}
// 游戏退出时自动释放
[RuntimeInitializeOnLoadMethod]
private static void AutoCleanup()
{
Application.quitting += UnloadAllAssets;
}
}
运行模式初始化器
// 模式初始化接口
public interface IPackageInitializer
{
UniTask InitializeAsync(ResourcePackage package);
}
// 编辑器模拟模式初始化
public class EditorSimulateModeInitializer : IPackageInitializer
{
public async UniTask InitializeAsync(ResourcePackage package)
{
var buildResult = EditorSimulateModeHelper.SimulateBuild(YooAssetManager.DefaultPackageName);
var param = FileSystemParameters.CreateDefaultEditorFileSystemParameters(buildResult.PackageRootDirectory);
await package.InitializeAsync(new EditorSimulateModeParameters { EditorFileSystemParameters = param });
}
}
// 远端地址配置
public class RemoteServices : IRemoteServices
{
private readonly string _main, _fallback;
public RemoteServices(string m, string f) { _main = m; _fallback = f; }
string IRemoteServices.GetRemoteMainURL(string file) => $"{_main}/{file}";
string IRemoteServices.GetRemoteFallbackURL(string file) => $"{_fallback}/{file}";
}
// 真机热更模式初始化
public class HostPlayModeInitializer : IPackageInitializer
{
public async UniTask InitializeAsync(ResourcePackage package)
{
// 获取自动适配平台的服务器地址
string url = GetHotServerURL();
var remote = new RemoteServices(url, url);
var param = new HostPlayModeParameters
{
BuildinFileSystemParameters = FileSystemParameters.CreateDefaultBuildinFileSystemParameters(),
CacheFileSystemParameters = FileSystemParameters.CreateDefaultCacheFileSystemParameters(remote)
};
await package.InitializeAsync(param);
}
// 根据不同平台,自动拼接热更服务器地址
private static string GetHotServerURL()
{
string host = "http://127.0.0.1";
string appVersion = Application.version;
#if UNITY_EDITOR
// 编辑器下根据当前构建目标平台拼接地址
var target = UnityEditor.EditorUserBuildSettings.activeBuildTarget;
if (target == UnityEditor.BuildTarget.Android)
return $"{host}/Android/{appVersion}";
if (target == UnityEditor.BuildTarget.iOS)
return $"{host}/iOS/{appVersion}";
return $"{host}/PC/{appVersion}";
#else
// 真机运行时根据平台自动选择
if (Application.platform == RuntimePlatform.Android)
return $"{host}/Android/{appVersion}";
if (Application.platform == RuntimePlatform.IPhonePlayer)
return $"{host}/iOS/{appVersion}";
return $"{host}/PC/{appVersion}";
#endif
}
}
// 初始化器注册中心
public static class PackageInitializerRegistry
{
private static Dictionary<EPlayMode, IPackageInitializer> _dict = new()
{
{ EPlayMode.EditorSimulateMode, new EditorSimulateModeInitializer() },
{ EPlayMode.HostPlayMode, new HostPlayModeInitializer() }
};
public static IPackageInitializer Get(EPlayMode mode) => _dict[mode];
}

京公网安备 11010502036488号