using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; namespace AssetManagement { /// /// 抽象类型:异步加载管理工具 /// public abstract class AsyncLoadHub where T : AsyncLoadUnit, new() { public event UnityAction onComplete; public List rules; public readonly List activeUnits = new List(); public readonly AsyncLoadQueue waitQueue = new AsyncLoadQueue(); protected AssetPathHub pathHub; public void Init(AssetPathHub pathHub, List 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机制限制 /// /// 停止一个加载单位,返回是否可以停止 /// 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 where T : AsyncLoadUnit, new() { public readonly List list = new List(); 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, } /// /// 加载规则:小于该优先的物体,不能同时加载超过maxActive /// public class AsyncLoadRule { public int priority; public int maxActive; public AsyncLoadRule(int priority, int maxActive) { this.priority = priority; this.maxActive = maxActive; } } }