209 lines
6.3 KiB
C#
209 lines
6.3 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
using UnityEngine.Events;
|
||
|
||
namespace AssetManagement
|
||
{
|
||
/// <summary>
|
||
/// 抽象类型:异步加载管理工具
|
||
/// </summary>
|
||
public abstract class AsyncLoadHub<T> where T : AsyncLoadUnit, new()
|
||
{
|
||
public event UnityAction<T> onComplete;
|
||
public List<AsyncLoadRule> rules;
|
||
public readonly List<T> activeUnits = new List<T>();
|
||
public readonly AsyncLoadQueue<T> waitQueue = new AsyncLoadQueue<T>();
|
||
protected AssetPathHub pathHub;
|
||
|
||
public void Init(AssetPathHub pathHub, List<AsyncLoadRule> rules)
|
||
{
|
||
this.pathHub = pathHub;
|
||
this.rules = rules;
|
||
this.rules.Sort((a, b) => a.priority.CompareTo(b.priority));
|
||
}
|
||
|
||
public T LoadUnit(string assetName, int priority = 0)
|
||
{
|
||
var unit = activeUnits.Find(a => a.name == assetName);
|
||
if (unit == null)
|
||
{
|
||
var waitState = waitQueue.TrySetPriority(assetName, priority, out unit);
|
||
switch (waitState)
|
||
{
|
||
case SetPriorityResult.NoItem:
|
||
{
|
||
unit = new T();
|
||
unit.Init(pathHub, assetName, priority);
|
||
if (CanActive(unit.priority))
|
||
ActivateUnit(unit);
|
||
else
|
||
waitQueue.AddInQueue(unit);
|
||
break;
|
||
}
|
||
case SetPriorityResult.NoUpdate:
|
||
// Nothing Happened
|
||
break;
|
||
case SetPriorityResult.Update:
|
||
if (CanActive(priority))
|
||
ActivateWaitUnits(1);
|
||
break;
|
||
|
||
default:
|
||
throw new ArgumentOutOfRangeException();
|
||
}
|
||
}
|
||
else if (unit.priority < priority)
|
||
unit.priority = priority;
|
||
return unit;
|
||
}
|
||
|
||
// 注:加载中的单位是不可停止的,Unity机制限制
|
||
/// <summary>
|
||
/// 停止一个加载单位,返回是否可以停止
|
||
/// </summary>
|
||
public bool StopUnit(T unit)
|
||
{
|
||
var result = false;
|
||
if (waitQueue.RemoveInQueue(unit.name))
|
||
result = true;
|
||
else if (activeUnits.Find(a => a.name == unit.name) == null)
|
||
result = true;
|
||
return result;
|
||
}
|
||
|
||
private void OnLoadComplete(AsyncLoadUnit unit)
|
||
{
|
||
var activeUnit = unit as T;
|
||
if (activeUnit == null)
|
||
Debug.LogError(string.Format("Cannot convert unit {0} to {1}!", unit.GetType(), typeof(T)));
|
||
else
|
||
{
|
||
activeUnits.Remove(activeUnit);
|
||
if (onComplete != null)
|
||
onComplete.Invoke(activeUnit);
|
||
ActivateWaitUnits(1);
|
||
}
|
||
}
|
||
|
||
private void ActivateWaitUnits(int count)
|
||
{
|
||
for (var i = 0; i < count; i++)
|
||
{
|
||
if (waitQueue.list.Count < 1)
|
||
break;
|
||
var last = waitQueue.list.Count - 1;
|
||
var unit = waitQueue.list[last];
|
||
if (!CanActive(unit.priority))
|
||
break;
|
||
waitQueue.list.RemoveAt(last);
|
||
ActivateUnit(unit);
|
||
}
|
||
}
|
||
|
||
private void ActivateUnit(T unit)
|
||
{
|
||
activeUnits.Add(unit);
|
||
unit.onComplete += OnLoadComplete;
|
||
unit.Load();
|
||
}
|
||
|
||
private bool CanActive(int priority)
|
||
{
|
||
return GetMaxActive(priority) > activeUnits.Count;
|
||
}
|
||
|
||
private int GetMaxActive(int priority)
|
||
{
|
||
var result = -1;
|
||
foreach (var rule in rules)
|
||
{
|
||
if (rule.priority > priority)
|
||
result = rule.maxActive;
|
||
}
|
||
return result;
|
||
}
|
||
}
|
||
|
||
public class AsyncLoadQueue<T> where T : AsyncLoadUnit, new()
|
||
{
|
||
public readonly List<T> list = new List<T>();
|
||
|
||
public bool RemoveInQueue(string name)
|
||
{
|
||
var result = false;
|
||
for (var i = 0; i < list.Count; i++)
|
||
{
|
||
if (list[i].name == name)
|
||
{
|
||
result = true;
|
||
list.RemoveAt(i);
|
||
break;
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
public void AddInQueue(T actionUnit)
|
||
{
|
||
var add = true;
|
||
for (var i = 0; i < list.Count; i++)
|
||
{
|
||
if (list[i].priority >= actionUnit.priority)
|
||
{
|
||
add = false;
|
||
list.Insert(i, actionUnit);
|
||
break;
|
||
}
|
||
}
|
||
if (add)
|
||
list.Add(actionUnit);
|
||
}
|
||
|
||
public SetPriorityResult TrySetPriority(string name, int priority, out T unit)
|
||
{
|
||
unit = null;
|
||
var result = SetPriorityResult.NoItem;
|
||
for (var i = 0; i < list.Count; i++)
|
||
if (list[i].name == name)
|
||
{
|
||
unit = list[i];
|
||
if (priority > list[i].priority && i < list.Count - 1)
|
||
{
|
||
result = SetPriorityResult.Update;
|
||
var actionUnit = list[i];
|
||
actionUnit.priority = priority;
|
||
list.RemoveAt(i);
|
||
AddInQueue(actionUnit);
|
||
}
|
||
else
|
||
result = SetPriorityResult.NoUpdate;
|
||
break;
|
||
}
|
||
return result;
|
||
}
|
||
}
|
||
|
||
public enum SetPriorityResult
|
||
{
|
||
NoItem,
|
||
NoUpdate,
|
||
Update,
|
||
}
|
||
|
||
/// <summary>
|
||
/// 加载规则:小于该优先的物体,不能同时加载超过maxActive
|
||
/// </summary>
|
||
public class AsyncLoadRule
|
||
{
|
||
public int priority;
|
||
public int maxActive;
|
||
|
||
public AsyncLoadRule(int priority, int maxActive)
|
||
{
|
||
this.priority = priority;
|
||
this.maxActive = maxActive;
|
||
}
|
||
}
|
||
} |