// // GameApp.cs // Create: // 2019-10-29 // Description: // 资源加载服务类,提供文件的同步异步加载,提供Unity AB的同步异步加载和资源的声明周期。 // Author: // 薛林强 <545626463@qq.com> // // Copyright (c) 2026 虚幻骑士科技 using System; using System.IO; using UnityEngine; using UnityEngine.Networking; using Plugins.XAsset; using UnityEngine.AddressableAssets; using UnityEngine.ResourceManagement.AsyncOperations; using System.Collections.Generic; using UnityEngine.SceneManagement; using UnityEngine.ResourceManagement.ResourceProviders; using UnityEngine.ResourceManagement; namespace Skyunion { abstract class BaseAssetService : Module, IAssetService { protected IAssetService mAssetService; protected GameObject mGameObject; protected Dictionary mObjectHasPool = new Dictionary(); protected List mAssets = new List(); protected string m_obbPath = string.Empty; protected struct InstanceRequest { public Action callback; public string assetName; } protected struct LoadedCallback { public Action callback; public IAsset asset; } private Queue mInstanceOnePerFrameQueue = new Queue(); private Queue mInstanceSlowlyQueue = new Queue(); private Queue mInstanceQueue = new Queue(); private Queue mLoadAssetQueue = new Queue(); public float m_unload_unused_asset_time = 30.0f; protected float m_unload_unused_asset_timer; public override void Update() { while (mInstanceQueue.Count > 0) { var request = mInstanceQueue.Dequeue(); if (request.callback != null) { Instantiate(request.assetName, request.callback); } } int nCount = 0; while(mInstanceSlowlyQueue.Count > 0 && nCount != 20) { var request = mInstanceSlowlyQueue.Dequeue(); if (request.callback != null) { this.Instantiate(request.assetName, request.callback); } nCount++; } if(mInstanceOnePerFrameQueue.Count > 0) { var request = mInstanceOnePerFrameQueue.Dequeue(); if (request.callback != null) { this.Instantiate(request.assetName, request.callback); } } while (mLoadAssetQueue.Count > 0) { var respon = mLoadAssetQueue.Dequeue(); respon.callback?.Invoke(respon.asset); } m_unload_unused_asset_timer += Time.deltaTime; if (m_unload_unused_asset_timer >= m_unload_unused_asset_time) { try { UnloadUnusedAssets(); }catch(Exception ex) { Debug.LogException(ex); } m_unload_unused_asset_timer = 0f; } } public byte[] LoadFile(string path, bool readHotfix = true) { int nIndex = path.IndexOf(Application.streamingAssetsPath); #if !UNITY_EDITOR if (nIndex != -1) { // 需要判断 只有 包内的资源才走热更新 if (readHotfix && VersionUtil.HasHotfix) { var basePath = path.Substring(Application.streamingAssetsPath.Length + 1); var hotfixPath = Path.Combine(VersionUtil.HotfixRuntimePath, basePath); if (File.Exists(hotfixPath)) { return File.ReadAllBytes(hotfixPath); } } // Android 需要处理读取压缩包内的资源 #if UNITY_ANDROID path = path.Substring(Application.streamingAssetsPath.Length + 1); path = Path.Combine("assets", path); byte[] bytes = null; if (string.Empty.Equals(m_obbPath)) { bytes = lzip.entry2Buffer(Application.dataPath, path); } else { bytes = lzip.entry2Buffer(m_obbPath, path); } return bytes; #endif } #endif return File.ReadAllBytes(path); } public void LoadFileAsync(string path, Action completed) { #if !UNITY_EDITOR if (VersionUtil.HasHotfix) { // 需要判断 只有 包内的资源才走热更新 int nIndex = path.IndexOf(Application.streamingAssetsPath); if (nIndex != -1) { var hotfixPath = Path.Combine(VersionUtil.HotfixRuntimePath, path.Substring(Application.streamingAssetsPath.Length+1)); if (File.Exists(hotfixPath)) { path = hotfixPath; path = path.Insert(0, "file://"); } } } #endif #if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IPHONE path = path.Insert(0, "file://"); #endif UnityWebRequest webFile = UnityWebRequest.Get(path); webFile.SendWebRequest().completed += (AsyncOperation op) => { completed?.Invoke(webFile.downloadHandler.data); }; } public void InstantiateOnePerFrame(string assetName, Action completed) { var request = new InstanceRequest(); request.assetName = assetName; request.callback = completed; mInstanceOnePerFrameQueue.Enqueue(request); } public void InstantiateSlowly(string assetName, Action completed) { var request = new InstanceRequest(); request.assetName = assetName; request.callback = completed; mInstanceSlowlyQueue.Enqueue(request); } public void InstantiateNextFrame(string assetName, Action completed) { var request = new InstanceRequest(); request.assetName = assetName; request.callback = completed; mInstanceQueue.Enqueue(request); } protected void LoadCallbackInUpdate(Action completed, IAsset asset) { var respon = new LoadedCallback(); respon.asset = asset; respon.callback = completed; mLoadAssetQueue.Enqueue(respon); } public abstract IAsset LoadAssetAsync(string assetName, Action completed) where T : UnityEngine.Object; public IAsset LoadAssetAsync(string assetName, Action completed, UnityEngine.Object autoDestroyOwner) where T : UnityEngine.Object { IAsset asset = LoadAssetAsync(assetName, completed); asset.Attack(autoDestroyOwner); mAssets.Add(asset); return asset; } public abstract IAsset LoadSceneAssetAsync(string assetName, bool addictive, Action completed); public abstract void Instantiate(string assetName, Action completed); public virtual void Destroy(GameObject gameObject) { GameObject.Destroy(gameObject); } public virtual void Destroy(GameObject gameObject, float fadeTime) { GameObject.Destroy(gameObject, fadeTime); } public virtual void UnloadUnusedAssets() { } public GameObject Instantiate(GameObject gameObject) { return GameObject.Instantiate(gameObject); } public void SetOBBPath(string obbPath) { m_obbPath = obbPath; } } }