Files

572 lines
20 KiB
C#
Raw Permalink Normal View History

2024-08-23 15:49:34 +08:00
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Events;
namespace AssetManagement
{
/// <summary>
/// 统一处理加载和下载两个流程的基础单位
/// 外界访问资源包单位的唯一接口
/// </summary>
public class BundleRecord
{
// 注:已知问题 fileSize 可能不会在重新初始化后改变
// 但是显然保存AssetPathData是更糟糕的选择
// 外部处理不当,会导致更新前挂起的资源会有进度条不正确的问题,但是不造成大的影响
public readonly int fileSize;
// 基本加载流程:
// 从UpdateHelper测试是否存在Persist如果存在试图从Persist加载
// 否则试图从Streaming加载
// 加载失败后,试图用资源服务器下载
public readonly BundleManager manager;
public readonly string name;
public BundleRecord(AssetDependencyItem pathData, BundleManager manager)
{
name = pathData.name;
fileSize = pathData.fileSize;
this.manager = manager;
}
/// <summary>
/// 是否主动加载的资源包,主动加载需要主动释放
/// </summary>
public bool active { get; private set; }
/// <summary>
/// 被其他资源包依赖次数
/// </summary>
public int depended { get; private set; }
/// <summary>
/// 依赖的其他资源包
/// </summary>
// 注:只有主动加载会记录依赖,依赖加载的资源不记录
public List<BundleRecord> dependencies { get; private set; }
public AssetBundle assetBundle { get; private set; }
public BundleLoader loader { get; private set; }
public BundleDownloader downloader { get; private set; }
/// <summary>
/// 当前加载状态
/// </summary>
public BundleState state { get; private set; }
// 当前是否处于使用状态
public bool isInUse { get; private set; }
// 是否已经下载过一次
// 加载失败时,如果没有下载记录,允许强行执行一次下载流程
public bool hasDownloaded { get; private set; }
public event UnityAction<BundleRecord> onStateUpdate;
/// <summary>
/// 同步主动加载
/// </summary>
// 注这个几把玩意现在有已知bug - 如果被依赖包出现Error状态将不会透传到上层
public void ActiveLoadSync()
{
#if UNITY_EDITOR
BundleDebugManager.AppendBundle(name);
#endif
var temp = state;
if (state == BundleState.Loading)
{
// 现在肯定是active状态不需要搞过多判定
active = true;
// 使用特殊方法转异步到同步
if (dependencies != null)
{
// 注:需要先刷新全部依赖树的状态,然后刷新依赖状态
// 注:这堆破烂很蛋疼,需要先更新全部,然后再发状态改变
var beforeState = dependencies.Select(a => a.state).ToArray();
foreach (var dependency in dependencies)
dependency.LoadDependencySync();
for (var i = 0; i < dependencies.Count; i++)
dependencies[i].SendStateUpdate(beforeState[i]);
// foreach (var dependency in dependencies)
// if (dependency.active)
// dependency.RefreshWaitDependencies();
}
}
else
{
// 注执行之前需要BundleManager进行全面检查
// 异步加载中的被调用情况,应该绝对过滤掉
// 这里不做过多异常处理
if (!active)
{
active = true;
if (dependencies == null)
dependencies = manager.GetDependencies(name);
// 注需要全部依赖包加载完成然后才能刷新本身active的依赖包
// 结构很诡异AddDependencySync因此做成private
// foreach (var dependency in dependencies)
// dependency.LoadSync();
var beforeState = dependencies.Select(a => a.state).ToArray();
foreach (var dependency in dependencies)
dependency.AddDependencySync();
for (var i = 0; i < dependencies.Count; i++)
dependencies[i].SendStateUpdate(beforeState[i]);
// foreach (var dependency in dependencies)
// if (dependency.active)
// dependency.RefreshWaitDependencies();
}
}
UpdateInUse();
LoadSync();
state = assetBundle ? BundleState.WaitDependency : BundleState.Error;
RefreshWaitDependencies();
SendStateUpdate(temp);
}
/// <summary>
/// 同步依赖加载
/// </summary>
private void AddDependencySync()
{
depended = Mathf.Max(0, depended) + 1;
UpdateInUse();
LoadDependencySync();
}
private void LoadDependencySync()
{
// 注执行之前需要BundleManager进行全面检查
// 异步加载中的被调用情况,应该绝对过滤掉
// 这里不做过多异常处理
LoadSync();
// 同步加载不处理active被依赖的流程 - 全部给上层处理
state = assetBundle ? BundleState.Done : BundleState.Error;
}
private void LoadSync()
{
if (!assetBundle)
{
// 编辑器额外检查下路径提前报错
// 不节约这点性能了,运行时报错看着好看点
// #if UNITY_EDITOR
var pathState = manager.assetPathHub.GetPathState(name);
switch (pathState)
{
case AssetPathState.online:
Debug.LogError(string.Format("Unable to sync load online asset {0}!", name));
break;
case AssetPathState.unmarked:
Debug.LogError(string.Format("Unable to sync load unmarked asset {0}!", name));
break;
default:
{
// #endif
if (loader != null)
{
manager.loadHub.onComplete -= OnLoadComplete;
manager.loadHub.StopUnit(loader);
}
var path = manager.assetPathHub.GetPath(name);
assetBundle = AssetBundle.LoadFromFile(path + AssetConst.bundleVariant);
// #if UNITY_EDITOR
break;
}
}
// #endif
}
}
/// <summary>
/// 异步依赖加载
/// </summary>
public void ActiveLoad()
{
#if UNITY_EDITOR
BundleDebugManager.AppendBundle(name);
#endif
if (!active)
{
active = true;
if (dependencies == null)
{
dependencies = manager.GetDependencies(name);
foreach (var dependency in dependencies)
dependency.onStateUpdate += OnDependencyUpdate;
}
foreach (var t in dependencies)
t.AddDependency();
// 特殊处理由依赖包升级到主动包的流程
if (state == BundleState.Done)
{
state = BundleState.WaitDependency;
RefreshWaitDependencies();
}
RefreshInUse();
}
}
public void ActiveUnload()
{
if (active)
{
active = false;
if (dependencies != null)
foreach (var t in dependencies)
t.RemoveDependency();
RefreshInUse();
}
}
/// <summary>
/// 异步依赖加载
/// </summary>
public void AddDependency()
{
depended = Mathf.Max(0, depended) + 1;
RefreshInUse();
}
public void RemoveDependency()
{
depended = Mathf.Max(0, depended - 1);
RefreshInUse();
}
public void Dispose()
{
manager.downloadHub.onComplete -= OnDownloadComplete;
manager.loadHub.onComplete -= OnLoadComplete;
RemoveDependencyListen();
}
public float GetTotalFinishBytes()
{
var bytes = GetSelfFinishBytes();
if (dependencies != null)
{
foreach (var dependency in dependencies)
{
bytes += dependency.GetSelfFinishBytes();
}
}
return bytes;
}
public float GetSelfFinishBytes()
{
return GetSelfProgress() * fileSize;
}
public float GetTotalProgress()
{
var finishBytes = GetTotalFinishBytes();
float totalBytes = fileSize;
if (dependencies != null)
foreach (var dependency in dependencies)
totalBytes += dependency.fileSize;
return finishBytes / totalBytes;
}
public float GetSelfProgress()
{
float progress;
switch (state)
{
case BundleState.Done:
case BundleState.WaitDependency:
case BundleState.Error:
{
progress = 1f;
}
break;
case BundleState.WaitUnload:
case BundleState.Unload:
{
progress = 0f;
}
break;
case BundleState.Downloading:
progress = downloader != null ? downloader.GetProgress() : 0f;
break;
case BundleState.Loading:
progress = loader != null ? loader.GetProgress() : 0f;
break;
default:
progress = 0f;
throw new ArgumentOutOfRangeException();
}
return progress;
}
private void RefreshInUse()
{
if (UpdateInUse())
{
if (name == "ui/sprite/login")
Debug.LogWarning("Update In Use: " + isInUse + " Active: " + active);
if (isInUse)
TryLoadAssetBundle();
else
TryUnloadAssetBundle();
}
}
private bool UpdateInUse()
{
var willInUse = active || depended > 0;
var temp = willInUse != isInUse;
isInUse = willInUse;
return temp;
}
/// <summary>
/// 如果有主动和被动加载引用并且AssetBundle尚未加载就开始加载
/// </summary>
private void TryLoadAssetBundle()
{
var temp = state;
// 自身已经加成完成的情况
if (state != BundleState.Done && state != BundleState.WaitDependency)
{
if (assetBundle == null)
{
// 从等待加载完成后卸载 复活为加载中
if (state == BundleState.WaitUnload)
state = BundleState.Loading;
else
{
var pathType = manager.assetPathHub.GetPathState(name);
switch (pathType)
{
case AssetPathState.online:
StartDownload();
break;
case AssetPathState.persist:
case AssetPathState.streaming:
{
loader = manager.loadHub.LoadUnit(name);
if (loader != null)
{
manager.loadHub.onComplete += OnLoadComplete;
state = BundleState.Loading;
}
else
{
Debug.LogError("Failed to start load unit for " + name);
state = BundleState.Error;
}
}
break;
default:
{
Debug.LogError("Unhandled bundle path type " + pathType);
state = BundleState.Error;
}
break;
}
}
}
else if (active)
{
state = BundleState.WaitDependency;
RefreshWaitDependencies();
}
else
state = BundleState.Done;
}
SendStateUpdate(temp);
}
private void StartDownload()
{
hasDownloaded = true;
// 主动下载优先度调整为 1
downloader = manager.downloadHub.LoadUnit(name, 1);
if (downloader != null)
{
manager.downloadHub.onComplete += OnDownloadComplete;
state = BundleState.Downloading;
}
else
{
Debug.LogError("Failed to start download unit for " + name);
state = BundleState.Error;
}
}
/// <summary>
/// 如果主动和被动加载引用都清空了开始卸载AssetBundle
/// </summary>
private void TryUnloadAssetBundle()
{
var temp = state;
if (assetBundle != null)
try
{
assetBundle.Unload(true);
assetBundle = null;
}
catch (Exception e)
{
Debug.LogError(e);
}
if (loader != null)
{
state = manager.loadHub.StopUnit(loader) ? BundleState.Unload : BundleState.WaitUnload;
if (state != BundleState.WaitUnload)
manager.loadHub.onComplete -= OnLoadComplete;
}
else if (downloader != null)
{
state = manager.downloadHub.StopUnit(downloader) ? BundleState.Unload : BundleState.WaitUnload;
if (state != BundleState.WaitUnload)
manager.downloadHub.onComplete -= OnDownloadComplete;
}
else
{
state = BundleState.Unload;
}
SendStateUpdate(temp);
}
private void OnDownloadComplete(BundleDownloader unit)
{
var temp = state;
var load = false;
if (unit == downloader)
{
manager.downloadHub.onComplete -= OnDownloadComplete;
if (downloader.state == AsyncLoadUnitState.Done)
{
// 如果是WaitUnload状态直接析构加载流程
if (state == BundleState.WaitUnload)
state = BundleState.Unload;
else
load = true;
}
else
{
state = BundleState.Error;
}
downloader = null;
}
if (load)
TryLoadAssetBundle();
SendStateUpdate(temp);
}
private void OnLoadComplete(AsyncLoadUnit unit)
{
var temp = state;
var unload = false;
if (unit == loader)
{
manager.loadHub.onComplete -= OnLoadComplete;
if (loader.state == AsyncLoadUnitState.Done)
assetBundle = loader.bundle;
if (!assetBundle)
state = BundleState.Error;
else
{
// 如果是WaitUnload状态直接析构加载的Bundle
if (state == BundleState.WaitUnload)
unload = true;
else
state = BundleState.WaitDependency;
}
loader = null;
}
if (unload)
{
TryUnloadAssetBundle();
}
else
{
if (state == BundleState.WaitDependency)
RefreshWaitDependencies();
if (state == BundleState.Done)
RemoveDependencyListen();
else if (state == BundleState.Error)
{
manager.assetPathHub.LoadError(name);
if (hasDownloaded)
RemoveDependencyListen();
else
StartDownload();
}
SendStateUpdate(temp);
}
}
private void RefreshWaitDependencies()
{
if (dependencies == null)
{
state = BundleState.Done;
}
else
{
state = BundleState.Done;
foreach (var dependency in dependencies)
if (dependency.state == BundleState.Error)
{
state = BundleState.Error;
break;
}
else if (dependency.state != BundleState.Done)
{
state = BundleState.WaitDependency;
}
}
}
private void OnDependencyUpdate(BundleRecord record)
{
if (state == BundleState.WaitDependency)
{
var temp = state;
RefreshWaitDependencies();
if (state == BundleState.Done || state == BundleState.Error)
RemoveDependency();
SendStateUpdate(temp);
}
}
private void RemoveDependencyListen()
{
if (dependencies != null)
foreach (var dependency in dependencies)
dependency.onStateUpdate -= OnDependencyUpdate;
}
private void SendStateUpdate(BundleState oldState)
{
if (oldState != state && onStateUpdate != null)
onStateUpdate.Invoke(this);
}
}
public enum BundleState
{
Unload, // 已经析构完成
WaitUnload, // 试图析构但需要等待AssetBundle完成加载以防止泄漏
Downloading, // 自身下载中
Loading, // 自身加载中
WaitDependency, // 自身加载完成,等待依赖项加载完成
Done, // 全部加载完成,可以提取资源
Error // 资源加载失败。可能由于资源不存在或者其他网络问题
}
}