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);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|