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

245 lines
9.7 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>
// 注:重启游戏不要随意摧毁这个组件,这个组件的数据,有部分是无法重新绑定的,比如:正在下载的资源
// 尚未加载完成的部分可以主动析构
// 注:清晰结构流程 - 所有操作均有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);
}
}
}