Files
2024-08-23 15:49:34 +08:00

572 lines
20 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 // 资源加载失败。可能由于资源不存在或者其他网络问题
}
}