using System; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.Events; namespace AssetManagement { /// /// 最终的资源管理器 /// // 注:重启游戏不要随意摧毁这个组件,这个组件的数据,有部分是无法重新绑定的,比如:正在下载的资源 // 尚未加载完成的部分可以主动析构 // 注:清晰结构流程 - 所有操作均有Manager接收,然后操作具体BundleRecord。BundleRecord回调自身状态通知Manager,Manager再调整监视列表。 public class BundleManager { public readonly List activeRecords = new List(); public readonly Dictionary finishRecords = new Dictionary(); public event UnityAction 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; } /// /// 试图获得一个资源包,返回是否完成 /// /// 资源包名 /// 资源包记录 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; } // /// // /// 阻塞加载Bundle的流程 - 立刻加载成功,不支持需要下载的Bundle // /// // // 注:阻塞加载有更严格的预先检查 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 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); } } }