245 lines
9.7 KiB
C#
245 lines
9.7 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using UnityEngine;
|
||
using UnityEngine.Events;
|
||
|
||
namespace AssetManagement
|
||
{
|
||
/// <summary>
|
||
/// 最终的资源管理器
|
||
/// </summary>
|
||
// 注:重启游戏不要随意摧毁这个组件,这个组件的数据,有部分是无法重新绑定的,比如:正在下载的资源
|
||
// 尚未加载完成的部分可以主动析构
|
||
// 注:清晰结构流程 - 所有操作均有Manager接收,然后操作具体BundleRecord。BundleRecord回调自身状态通知Manager,Manager再调整监视列表。
|
||
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);
|
||
}
|
||
}
|
||
} |