Files

245 lines
9.7 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>
// 注:重启游戏不要随意摧毁这个组件,这个组件的数据,有部分是无法重新绑定的,比如:正在下载的资源
// 尚未加载完成的部分可以主动析构
// 注:清晰结构流程 - 所有操作均有Manager接收然后操作具体BundleRecord。BundleRecord回调自身状态通知ManagerManager再调整监视列表。
public class BundleManager
{
public readonly List<BundleRecord> activeRecords = new List<BundleRecord>();
public readonly Dictionary<string, BundleRecord> finishRecords = new Dictionary<string, BundleRecord>();
public event UnityAction<BundleRecord> onComplete;
public BundleManager(AssetPathHub assetPathHub, BundleLoadHub loadHub, BundleDownloadHub downloadHub)
{
this.assetPathHub = assetPathHub;
this.loadHub = loadHub;
this.downloadHub = downloadHub;
}
public AssetPathHub assetPathHub { get; private set; }
public BundleLoadHub loadHub { get; private set; }
public BundleDownloadHub downloadHub { get; private set; }
/// <summary>
/// 试图获得一个资源包,返回是否完成
/// </summary>
/// <param name="name">资源包名</param>
/// <param name="record">资源包记录</param>
public bool TryLoadBundle(string name, out BundleRecord record)
{
var result = finishRecords.TryGetValue(name, out record);
if (!result)
{
record = activeRecords.Find(a => a.name == name) ?? CreateBundle(name);
if (record != null)
record.ActiveLoad();
}
else
record.ActiveLoad();
return result;
}
// /// <summary>
// /// 阻塞加载Bundle的流程 - 立刻加载成功不支持需要下载的Bundle
// /// </summary>
// // 注:阻塞加载有更严格的预先检查
public BundleRecord LoadBundleSync(string name)
{
BundleRecord result = null;
var record = GetBundle(name);
if (record != null)
{
result = record;
switch (record.state)
{
case BundleState.Downloading:
Debug.LogError(name + " is still downloading and cannot load from local position!");
break;
case BundleState.Loading:
case BundleState.WaitUnload:
case BundleState.WaitDependency:
case BundleState.Unload:
record.ActiveLoadSync();
break;
case BundleState.Done:
case BundleState.Error:
// 不需要处理已经加载完成的情况
break;
default:
Debug.LogError(string.Format("{0} has unhandled state {1} in sync load!", name, record.state));
break;
}
}
// 预先检查并防御依赖出现错误的情况
else
{
var valid = true;
var dependencies = assetPathHub.GetAllDependencies(name);
if (dependencies == null)
{
valid = false;
Debug.LogError(string.Format("Try to sync load an unmarked bundle {0}!", name));
}
else
{
foreach (var dependency in dependencies)
{
var pathType = assetPathHub.GetPathState(dependency);
// 注思想是Unmarked 直接跳过不管;报错会在实际获得依赖时出现
if (pathType == AssetPathState.online)
{
Debug.LogError(string.Format(
"{0} is depended by {1}, but not local and cannot be sync loaded!", dependency, name));
valid = false;
break;
}
}
}
Debug.LogWarning("Load Sync: " + name + ", Valid = " + valid);
if (valid)
{
foreach (var dependency in dependencies)
{
var dependRecord = GetBundle(dependency);
if (dependRecord != null)
{
switch (dependRecord.state)
{
case BundleState.Done:
case BundleState.Loading:
break;
case BundleState.Error:
valid = false;
Debug.LogError(string.Format("{0} is depended by {1}, but has error on loading!", dependency, name));
break;
default:
valid = false;
Debug.LogError(string.Format("{0} is depended by {1}, but async loading and cannot be loaded sync!", dependency, name));
break;
}
}
}
}
if (valid)
{
record = CreateBundle(name);
if (record != null)
record.ActiveLoadSync();
result = record;
}
}
return result;
}
public void UnloadBundle(string name)
{
BundleRecord record;
if (finishRecords.TryGetValue(name, out record))
record.ActiveUnload();
if (record == null)
{
record = activeRecords.Find(a => a.name == name);
if (record != null)
record.ActiveUnload();
}
}
public List<BundleRecord> GetDependencies(string name)
{
var dependencies = assetPathHub.GetAllDependencies(name);
// 注CreateBundle 在路径不存在时,会返回空,暂时处理方式是内部报错,这里直接屏蔽防御
return dependencies == null ? null : dependencies.Select(dependency => GetBundle(dependency) ?? CreateBundle(dependency)).Where(item => item != null).ToList();
}
public BundleRecord GetBundle(string name)
{
BundleRecord record;
if (!finishRecords.TryGetValue(name, out record))
record = activeRecords.Find(a => a.name == name);
return record;
}
private BundleRecord CreateBundle(string name)
{
BundleRecord result = null;
var pathData = assetPathHub.GetPathData(name);
if (pathData != null)
{
result = new BundleRecord(pathData, this);
result.onStateUpdate += OnBundleStateUpdate;
// 注新建的Record或者立刻被主动更新或者立刻被依赖更新
// 因此等待回调后再更新列表
}
else
Debug.LogError(string.Format("Unable to Create unmarked asset for {0}!", name));
return result;
}
private void OnBundleStateUpdate(BundleRecord record)
{
switch (record.state)
{
case BundleState.Done:
{
MoveToFinish(record);
if (onComplete != null)
onComplete(record);
}
break;
case BundleState.Unload:
{
if (finishRecords.ContainsKey(record.name))
finishRecords.Remove(record.name);
activeRecords.Remove(record);
record.onStateUpdate -= OnBundleStateUpdate;
record.Dispose();
}
break;
case BundleState.Loading:
case BundleState.Downloading:
case BundleState.WaitUnload:
case BundleState.WaitDependency:
{
MoveToActive(record);
}
break;
case BundleState.Error:
MoveToFinish(record);
//Debug.LogError("Not Implemented Error: Bundle State Error will not be handled now!");
break;
default:
throw new ArgumentOutOfRangeException();
}
}
private void MoveToFinish(BundleRecord record)
{
activeRecords.Remove(record);
#if UNITY_EDITOR
// 额外增加一个编辑器检查,监控可能出现的故障
if (finishRecords.ContainsKey(record.name))
Debug.LogError("Bundle Manager has already marked the record " + record.name);
#endif
finishRecords[record.name] = record;
}
private void MoveToActive(BundleRecord record)
{
var i = activeRecords.IndexOf(record);
if (i < 0)
activeRecords.Add(record);
if (finishRecords.ContainsKey(record.name))
finishRecords.Remove(record.name);
}
}
}