// Copyright (c) 2025 EFramework Innovation. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
using System;
using System.Collections;
using UnityEngine;
using EFramework.Unity.Utility;
namespace EFramework.Unity.Asset
{
///
/// XAsset.Core 提供了内部事件管理器、异步加载处理器等功能,是资源管理器的基础模块。
///
///
///
/// 功能特性
/// - 内部事件管理器:定义资源系统生命周期中的关键事件
/// - 异步加载处理器:负责跟踪和管理异步资源加载的过程
///
/// 使用手册
/// 1. 运行流程
///
/// 以下流程图展示了资源管理器的运行时逻辑,包括资源/场景加载/卸载、引用计数管理、内置事件机制的主要流程:
///
/// stateDiagram-v2
/// direction TB
///
/// state 资源管理流程 {
/// 资源加载请求 --> 资源加载模式 : OnPreLoadResource
///
/// 资源加载模式 --> Bundle资源加载 : bundle = true
/// 资源加载模式 --> Resources资源加载 : bundle == false or resource = true
///
/// Bundle资源加载 --> 手动管理引用 : retain = true
/// Bundle资源加载 --> 自动管理引用 : Refer.Awake
/// Resources资源加载 --> 加载目标资源
///
/// 增加引用计数 --> 加载目标资源
/// 加载目标资源 --> 资源加载完成 : OnPostLoadResource
///
/// 资源加载完成 --> 资源卸载请求
/// 资源卸载请求 --> 手动管理释放 : Unload
/// 资源卸载请求 --> 自动管理释放 : Refer.OnDestroy
/// }
///
/// state 场景管理流程 {
/// 场景加载请求 --> 场景加载模式 : OnPreLoadScene
///
/// 场景加载模式 --> Bundle场景加载 : bundle = true
/// 场景加载模式 --> Resources场景加载 : bundle = false
///
/// Bundle场景加载 --> 自动管理引用
/// Resources场景加载 --> 加载目标场景
///
/// 增加引用计数 --> 加载目标场景
/// 加载目标场景 --> 场景加载完成 : OnPostLoadScene
///
/// 场景加载完成 --> 场景卸载请求
/// 场景卸载请求 --> 手动管理释放 : Unload
/// 场景卸载请求 --> 自动管理释放 : SceneManager.sceneUnloaded
/// }
///
/// state 依赖管理流程 {
/// state 依赖引用流程 {
/// 手动管理引用 --> 增加引用计数 : bundle.Retain
/// 自动管理引用 --> 增加引用计数 : bundle.Retain
/// }
///
/// state 依赖释放流程 {
/// 手动管理释放 --> 减少引用计数 : bundle.Release
/// 自动管理释放 --> 减少引用计数 : bundle.Release
///
/// 减少引用计数 --> 引用计数为零
/// 引用计数为零 --> 卸载资源依赖 : OnPostUnloadBundle
/// }
/// }
///
/// 2. 事件类型
/// - 功能说明:定义资源生命周期中的关键事件
/// - 事件列表:
/// - OnPreLoadResource:资源加载前
/// - OnPostLoadResource:资源加载后
/// - OnPreLoadScene:场景加载前
/// - OnPostLoadScene:场景加载后
/// - OnPostUnloadBundle:资源包卸载后
/// - 使用示例:
/// XAsset.Event.Register(XAsset.EventType.OnPreLoadResource, (asset) => Debug.Log("资源加载前:" + asset));
///
/// 更多信息请参考模块文档。
///
public partial class XAsset
{
///
/// Callback 是资源加载完成后的回调类型,用于通知调用者资源已准备就绪。
///
/// 加载完成的 Unity 资源对象
public delegate void Callback(UnityEngine.Object asset);
///
/// Handler 是资源加载处理器,负责跟踪和管理异步资源加载的过程。
/// 提供加载进度监控、事件通知等功能,支持场景和资源包的异步加载操作。
///
public class Handler : IEnumerator
{
///
/// doneCount 表示当前已完成加载的资源数量。
///
internal int doneCount;
///
/// totalCount 表示需要加载的资源总数。
///
internal int totalCount;
///
/// Progress 获取当前加载进度(0-1之间的浮点数)。
/// 对于单个资源,直接返回其加载进度;
/// 对于多个资源,返回总体完成的百分比。
///
public float Progress
{
get
{
if (totalCount == 0 || totalCount == 1) // Resources 模式或 AssetBundle 单资源
{
if (Request != null) return Request.progress;
else return 0f;
}
else // AssetBundle 资源 + 依赖
{
#region TONOTICE
// 业务层加载进度可能会卡在99%,这里进行调和其实作用并不大
// 本质原因是资源/场景实例化引起的主线程卡顿
// 这样调和了之后反而会引起进度的跳变,所以仍旧保留原来的实现
// // 设置 AssetBundle 文件的加载进度调和值
// // 设置 Resource 的实例化进度调和值
// // 使得资源/场景的加载进度更平滑
// const float bundleWeight = 0.5f;
// const float resourceWeight = 0.5f;
// if (Request != null)
// {
// if (!Request.isDone) return (doneCount + 1) / (float)totalCount * bundleWeight + Request.progress * resourceWeight;
// else return 1f;
// }
// else return doneCount / (float)totalCount * bundleWeight;
#endregion
if (Request != null)
{
if (!Request.isDone) return (doneCount + Request.progress) / totalCount;
else return 1f;
}
else return doneCount / (float)totalCount;
}
}
}
///
/// OnPreload 是资源开始加载前触发的事件,可用于执行预加载准备工作。
///
public event Action OnPreload;
///
/// OnPostload 是资源加载完成后触发的事件,可用于执行加载后的初始化操作。
///
public event Action OnPostload;
///
/// Request 是 Unity 的异步操作对象。
/// 可能是 ResourceRequest 或 AssetBundleRequest,用于跟踪具体的加载进度。
///
public AsyncOperation Request;
///
/// Asset 获取加载完成的资源对象。
/// 根据Operation类型的不同,可能返回 Resources 加载的资源或 AssetBundle 中的资源。
///
public UnityEngine.Object Asset
{
get
{
if (Request != null)
{
if (Request is ResourceRequest) return (Request as ResourceRequest).asset;
else if (Request is AssetBundleRequest) return (Request as AssetBundleRequest).asset;
}
return null;
}
}
public object Current => null;
///
/// Error 表示是否发生错误中断。
///
public bool Error { get; internal set; }
///
/// IsDone 检查资源是否已完成加载。
///
public bool IsDone { get => Error || (Request != null && Request.isDone); }
///
/// MoveNext 推进加载进程。
/// 作为协程迭代器,在加载未完成时返回true,允许 Unity 继续执行异步加载。
///
public bool MoveNext() { return !IsDone; }
///
/// Reset 重置加载器状态,清空所有计数器和事件监听,使其可以重新用于新的加载任务。
///
public void Reset() { doneCount = 0; totalCount = 0; OnPreload = null; OnPostload = null; Request = null; Error = false; }
///
/// InvokePreload 触发预加载事件,并安全处理可能的异常。
///
internal void InvokePreload()
{
try { OnPreload?.Invoke(); }
catch (Exception e) { XLog.Panic(e); }
}
///
/// InvokePostload 触发加载完成事件,并安全处理可能的异常。
///
internal void InvokePostload()
{
try { OnPostload?.Invoke(); }
catch (Exception e) { XLog.Panic(e); }
}
}
///
/// EventType 是资源系统的内置事件类型,定义了资源生命周期中的重要节点。
/// 这些事件可用于在特定时机执行自定义逻辑。
///
public enum EventType
{
///
/// OnPreLoadResource 是资源加载前的事件。
///
OnPreLoadResource,
///
/// OnPostLoadResource 是资源加载完成后的事件。
///
OnPostLoadResource,
///
/// OnPreLoadScene 是场景加载前的事件。
///
OnPreLoadScene,
///
/// OnPostLoadScene 是场景加载完成后的事件。
///
OnPostLoadScene,
///
/// OnPostUnloadBundle 是资源包卸载完成后的事件。
///
OnPostUnloadBundle,
}
///
/// Event 是资源系统的内置事件管理器实例,用于处理资源生命周期相关的事件。
///
public static readonly XEvent.Manager Event = new();
}
}