using System; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.Events; namespace AssetManagement { /// /// 统一处理加载和下载两个流程的基础单位 /// 外界访问资源包单位的唯一接口 /// public class BundleRecord { // 注:已知问题 fileSize 可能不会在重新初始化后改变 // 但是显然,保存AssetPathData是更糟糕的选择 // 外部处理不当,会导致更新前挂起的资源会有进度条不正确的问题,但是不造成大的影响 public readonly int fileSize; // 基本加载流程: // 从UpdateHelper测试是否存在Persist,如果存在,试图从Persist加载 // 否则试图从Streaming加载 // 加载失败后,试图用资源服务器下载 public readonly BundleManager manager; public readonly string name; public BundleRecord(AssetDependencyItem pathData, BundleManager manager) { name = pathData.name; fileSize = pathData.fileSize; this.manager = manager; } /// /// 是否主动加载的资源包,主动加载需要主动释放 /// public bool active { get; private set; } /// /// 被其他资源包依赖次数 /// public int depended { get; private set; } /// /// 依赖的其他资源包 /// // 注:只有主动加载会记录依赖,依赖加载的资源不记录 public List dependencies { get; private set; } public AssetBundle assetBundle { get; private set; } public BundleLoader loader { get; private set; } public BundleDownloader downloader { get; private set; } /// /// 当前加载状态 /// public BundleState state { get; private set; } // 当前是否处于使用状态 public bool isInUse { get; private set; } // 是否已经下载过一次 // 加载失败时,如果没有下载记录,允许强行执行一次下载流程 public bool hasDownloaded { get; private set; } public event UnityAction onStateUpdate; /// /// 同步主动加载 /// // 注:这个几把玩意现在有已知bug - 如果被依赖包出现Error状态,将不会透传到上层 public void ActiveLoadSync() { #if UNITY_EDITOR BundleDebugManager.AppendBundle(name); #endif var temp = state; if (state == BundleState.Loading) { // 现在肯定是active状态,不需要搞过多判定 active = true; // 使用特殊方法转异步到同步 if (dependencies != null) { // 注:需要先刷新全部依赖树的状态,然后刷新依赖状态 // 注:这堆破烂很蛋疼,需要先更新全部,然后再发状态改变 var beforeState = dependencies.Select(a => a.state).ToArray(); foreach (var dependency in dependencies) dependency.LoadDependencySync(); for (var i = 0; i < dependencies.Count; i++) dependencies[i].SendStateUpdate(beforeState[i]); // foreach (var dependency in dependencies) // if (dependency.active) // dependency.RefreshWaitDependencies(); } } else { // 注:执行之前需要BundleManager进行全面检查 // 异步加载中的被调用情况,应该绝对过滤掉 // 这里不做过多异常处理 if (!active) { active = true; if (dependencies == null) dependencies = manager.GetDependencies(name); // 注:需要全部依赖包加载完成,然后才能刷新本身active的依赖包 // 结构很诡异,AddDependencySync,因此做成private // foreach (var dependency in dependencies) // dependency.LoadSync(); var beforeState = dependencies.Select(a => a.state).ToArray(); foreach (var dependency in dependencies) dependency.AddDependencySync(); for (var i = 0; i < dependencies.Count; i++) dependencies[i].SendStateUpdate(beforeState[i]); // foreach (var dependency in dependencies) // if (dependency.active) // dependency.RefreshWaitDependencies(); } } UpdateInUse(); LoadSync(); state = assetBundle ? BundleState.WaitDependency : BundleState.Error; RefreshWaitDependencies(); SendStateUpdate(temp); } /// /// 同步依赖加载 /// private void AddDependencySync() { depended = Mathf.Max(0, depended) + 1; UpdateInUse(); LoadDependencySync(); } private void LoadDependencySync() { // 注:执行之前需要BundleManager进行全面检查 // 异步加载中的被调用情况,应该绝对过滤掉 // 这里不做过多异常处理 LoadSync(); // 同步加载不处理active被依赖的流程 - 全部给上层处理 state = assetBundle ? BundleState.Done : BundleState.Error; } private void LoadSync() { if (!assetBundle) { // 编辑器额外检查下路径提前报错 // 不节约这点性能了,运行时报错看着好看点 // #if UNITY_EDITOR var pathState = manager.assetPathHub.GetPathState(name); switch (pathState) { case AssetPathState.online: Debug.LogError(string.Format("Unable to sync load online asset {0}!", name)); break; case AssetPathState.unmarked: Debug.LogError(string.Format("Unable to sync load unmarked asset {0}!", name)); break; default: { // #endif if (loader != null) { manager.loadHub.onComplete -= OnLoadComplete; manager.loadHub.StopUnit(loader); } var path = manager.assetPathHub.GetPath(name); assetBundle = AssetBundle.LoadFromFile(path + AssetConst.bundleVariant); // #if UNITY_EDITOR break; } } // #endif } } /// /// 异步依赖加载 /// public void ActiveLoad() { #if UNITY_EDITOR BundleDebugManager.AppendBundle(name); #endif if (!active) { active = true; if (dependencies == null) { dependencies = manager.GetDependencies(name); foreach (var dependency in dependencies) dependency.onStateUpdate += OnDependencyUpdate; } foreach (var t in dependencies) t.AddDependency(); // 特殊处理由依赖包升级到主动包的流程 if (state == BundleState.Done) { state = BundleState.WaitDependency; RefreshWaitDependencies(); } RefreshInUse(); } } public void ActiveUnload() { if (active) { active = false; if (dependencies != null) foreach (var t in dependencies) t.RemoveDependency(); RefreshInUse(); } } /// /// 异步依赖加载 /// public void AddDependency() { depended = Mathf.Max(0, depended) + 1; RefreshInUse(); } public void RemoveDependency() { depended = Mathf.Max(0, depended - 1); RefreshInUse(); } public void Dispose() { manager.downloadHub.onComplete -= OnDownloadComplete; manager.loadHub.onComplete -= OnLoadComplete; RemoveDependencyListen(); } public float GetTotalFinishBytes() { var bytes = GetSelfFinishBytes(); if (dependencies != null) { foreach (var dependency in dependencies) { bytes += dependency.GetSelfFinishBytes(); } } return bytes; } public float GetSelfFinishBytes() { return GetSelfProgress() * fileSize; } public float GetTotalProgress() { var finishBytes = GetTotalFinishBytes(); float totalBytes = fileSize; if (dependencies != null) foreach (var dependency in dependencies) totalBytes += dependency.fileSize; return finishBytes / totalBytes; } public float GetSelfProgress() { float progress; switch (state) { case BundleState.Done: case BundleState.WaitDependency: case BundleState.Error: { progress = 1f; } break; case BundleState.WaitUnload: case BundleState.Unload: { progress = 0f; } break; case BundleState.Downloading: progress = downloader != null ? downloader.GetProgress() : 0f; break; case BundleState.Loading: progress = loader != null ? loader.GetProgress() : 0f; break; default: progress = 0f; throw new ArgumentOutOfRangeException(); } return progress; } private void RefreshInUse() { if (UpdateInUse()) { if (name == "ui/sprite/login") Debug.LogWarning("Update In Use: " + isInUse + " Active: " + active); if (isInUse) TryLoadAssetBundle(); else TryUnloadAssetBundle(); } } private bool UpdateInUse() { var willInUse = active || depended > 0; var temp = willInUse != isInUse; isInUse = willInUse; return temp; } /// /// 如果有主动和被动加载引用,并且AssetBundle尚未加载,就开始加载 /// private void TryLoadAssetBundle() { var temp = state; // 自身已经加成完成的情况 if (state != BundleState.Done && state != BundleState.WaitDependency) { if (assetBundle == null) { // 从等待加载完成后卸载 复活为加载中 if (state == BundleState.WaitUnload) state = BundleState.Loading; else { var pathType = manager.assetPathHub.GetPathState(name); switch (pathType) { case AssetPathState.online: StartDownload(); break; case AssetPathState.persist: case AssetPathState.streaming: { loader = manager.loadHub.LoadUnit(name); if (loader != null) { manager.loadHub.onComplete += OnLoadComplete; state = BundleState.Loading; } else { Debug.LogError("Failed to start load unit for " + name); state = BundleState.Error; } } break; default: { Debug.LogError("Unhandled bundle path type " + pathType); state = BundleState.Error; } break; } } } else if (active) { state = BundleState.WaitDependency; RefreshWaitDependencies(); } else state = BundleState.Done; } SendStateUpdate(temp); } private void StartDownload() { hasDownloaded = true; // 主动下载优先度调整为 1 downloader = manager.downloadHub.LoadUnit(name, 1); if (downloader != null) { manager.downloadHub.onComplete += OnDownloadComplete; state = BundleState.Downloading; } else { Debug.LogError("Failed to start download unit for " + name); state = BundleState.Error; } } /// /// 如果主动和被动加载引用都清空了,开始卸载AssetBundle /// private void TryUnloadAssetBundle() { var temp = state; if (assetBundle != null) try { assetBundle.Unload(true); assetBundle = null; } catch (Exception e) { Debug.LogError(e); } if (loader != null) { state = manager.loadHub.StopUnit(loader) ? BundleState.Unload : BundleState.WaitUnload; if (state != BundleState.WaitUnload) manager.loadHub.onComplete -= OnLoadComplete; } else if (downloader != null) { state = manager.downloadHub.StopUnit(downloader) ? BundleState.Unload : BundleState.WaitUnload; if (state != BundleState.WaitUnload) manager.downloadHub.onComplete -= OnDownloadComplete; } else { state = BundleState.Unload; } SendStateUpdate(temp); } private void OnDownloadComplete(BundleDownloader unit) { var temp = state; var load = false; if (unit == downloader) { manager.downloadHub.onComplete -= OnDownloadComplete; if (downloader.state == AsyncLoadUnitState.Done) { // 如果是WaitUnload状态,直接析构加载流程 if (state == BundleState.WaitUnload) state = BundleState.Unload; else load = true; } else { state = BundleState.Error; } downloader = null; } if (load) TryLoadAssetBundle(); SendStateUpdate(temp); } private void OnLoadComplete(AsyncLoadUnit unit) { var temp = state; var unload = false; if (unit == loader) { manager.loadHub.onComplete -= OnLoadComplete; if (loader.state == AsyncLoadUnitState.Done) assetBundle = loader.bundle; if (!assetBundle) state = BundleState.Error; else { // 如果是WaitUnload状态,直接析构加载的Bundle if (state == BundleState.WaitUnload) unload = true; else state = BundleState.WaitDependency; } loader = null; } if (unload) { TryUnloadAssetBundle(); } else { if (state == BundleState.WaitDependency) RefreshWaitDependencies(); if (state == BundleState.Done) RemoveDependencyListen(); else if (state == BundleState.Error) { manager.assetPathHub.LoadError(name); if (hasDownloaded) RemoveDependencyListen(); else StartDownload(); } SendStateUpdate(temp); } } private void RefreshWaitDependencies() { if (dependencies == null) { state = BundleState.Done; } else { state = BundleState.Done; foreach (var dependency in dependencies) if (dependency.state == BundleState.Error) { state = BundleState.Error; break; } else if (dependency.state != BundleState.Done) { state = BundleState.WaitDependency; } } } private void OnDependencyUpdate(BundleRecord record) { if (state == BundleState.WaitDependency) { var temp = state; RefreshWaitDependencies(); if (state == BundleState.Done || state == BundleState.Error) RemoveDependency(); SendStateUpdate(temp); } } private void RemoveDependencyListen() { if (dependencies != null) foreach (var dependency in dependencies) dependency.onStateUpdate -= OnDependencyUpdate; } private void SendStateUpdate(BundleState oldState) { if (oldState != state && onStateUpdate != null) onStateUpdate.Invoke(this); } } public enum BundleState { Unload, // 已经析构完成 WaitUnload, // 试图析构,但需要等待AssetBundle完成加载以防止泄漏 Downloading, // 自身下载中 Loading, // 自身加载中 WaitDependency, // 自身加载完成,等待依赖项加载完成 Done, // 全部加载完成,可以提取资源 Error // 资源加载失败。可能由于资源不存在或者其他网络问题 } }