Files
JJBB/Assets/Project/Script/AssetManagement/AsyncLoadBase/AsyncLoadHub.cs

209 lines
6.3 KiB
C#
Raw Normal View History

2024-08-23 15:49:34 +08:00
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;
}
}
}