Gameframework资源管理适配其他资源管理方案

在做之前,我们需要研究一下,常用的几款资源管理方案。
1、unity editor
Resources.Load()
构建assetbundle,使用assetBundle.LoadAsset()进行加载。
assetBundle.unload()进行卸载

2、addressable
每次构建时,都会生成唯一的版本。缺陷是,发布同一版本不同版本,比如内部测试版,和正式版时。两个版本由于是单独构建的,因此无法实现一次打包,两个版本互用的情况。
需要使用分支来管理。即通过切换分支,保证对应的build正确。

Addressables.Load(“AssetAddress”);

第一次加载Addressable的API是,会自动初始化。也可以自己调用Addressables.InitializeAsync
初始化,
1、会设置ResourceManager和ResourceLocators。
2、加载Addressale在StreamingAssets中的配置数据
3、执行任意对象的初始化操作
4、加载内容目录。默认情况下,会检查更新,然后下载可用的新目录
按地址加载
handle = Addressables.LoadAssetAsync(address); handle.Completed += Handle_Completed;
释放资源
Addressables.Release(handle);

3、yooasset
同样分为初始化、资源更新。加载

在资源管理方案中,通常的接口为
1、loadAsset
2、unloadAsset
3、loadscene
4、unloadScene
5、init
6、updata

我们专门定义一个接口ICustomResourceManager,来适配这些常用的资源管理方案
然后写一个继承自IResourceManager来调用这个接口

    /// <summary>
    /// 资源加载适配器
    /// </summary>
    public class GFAdapterResourceManager : IResourceManager
    {
        public static GFAdapterResourceManager instance = new GFAdapterResourceManager();
        public ICustomResourceManager CustomResourceManager;

        #region 暂不需要实现的功能
        


        #endregion
        
        public HasAssetResult HasAsset(string assetName)
        {
            throw new NotImplementedException();
        }

        public void LoadAsset(string assetName, LoadAssetCallbacks loadAssetCallbacks)
        {
            LoadAsset(assetName, null, 0, loadAssetCallbacks, null);
        }

        public void LoadAsset(string assetName, Type assetType, LoadAssetCallbacks loadAssetCallbacks)
        {
            LoadAsset(assetName, assetType, 0, loadAssetCallbacks, null);
        }

        public void LoadAsset(string assetName, int priority, LoadAssetCallbacks loadAssetCallbacks)
        {
            LoadAsset(assetName, null, priority, loadAssetCallbacks, null);
        }

        public void LoadAsset(string assetName, LoadAssetCallbacks loadAssetCallbacks, object userData)
        {
            LoadAsset(assetName, null, 0, loadAssetCallbacks, userData);
        }

        public void LoadAsset(string assetName, Type assetType, int priority, LoadAssetCallbacks loadAssetCallbacks)
        {
            LoadAsset(assetName, assetType, priority, loadAssetCallbacks, null);
        }

        public void LoadAsset(string assetName, Type assetType, LoadAssetCallbacks loadAssetCallbacks, object userData)
        {
            LoadAsset(assetName, assetType, 0, loadAssetCallbacks, userData);
        }

        public void LoadAsset(string assetName, int priority, LoadAssetCallbacks loadAssetCallbacks, object userData)
        {
            LoadAsset(assetName, null, priority, loadAssetCallbacks, userData);
        }

        public void LoadAsset(string assetName, Type assetType, int priority, LoadAssetCallbacks loadAssetCallbacks, object userData)
        {
            CustomResourceManager.ToLoadAsset(assetName,assetType,priority,loadAssetCallbacks,userData);
        }

        public void UnloadAsset(object asset)
        {
            CustomResourceManager.ToUnloadAsset(asset);
        }

        public void LoadScene(string sceneAssetName, LoadSceneCallbacks loadSceneCallbacks)
        {
            LoadScene(sceneAssetName,0,loadSceneCallbacks, null);
        }

        public void LoadScene(string sceneAssetName, int priority, LoadSceneCallbacks loadSceneCallbacks)
        {
            LoadScene(sceneAssetName,priority,loadSceneCallbacks, null);
        }

        public void LoadScene(string sceneAssetName, LoadSceneCallbacks loadSceneCallbacks, object userData)
        {
            LoadScene(sceneAssetName, 0,loadSceneCallbacks, userData);
        }

        public void LoadScene(string sceneAssetName, int priority, LoadSceneCallbacks loadSceneCallbacks, object userData)
        {
            CustomResourceManager.ToLoadScene(sceneAssetName,loadSceneCallbacks,priority,userData);
        }

        public void UnloadScene(string sceneAssetName, UnloadSceneCallbacks unloadSceneCallbacks)
        {
            UnloadScene(sceneAssetName, unloadSceneCallbacks, null);
        }

        public void UnloadScene(string sceneAssetName, UnloadSceneCallbacks unloadSceneCallbacks, object userData)
        {
            CustomResourceManager.ToUnloadScene(sceneAssetName,unloadSceneCallbacks,userData);
        }

        public void InitResources(InitResourcesCompleteCallback initResourcesCompleteCallback)
        {
            CustomResourceManager.ToInitResources(initResourcesCompleteCallback);
        }
        public void UpdateResources(UpdateResourcesCompleteCallback updateResourcesCompleteCallback)
        {
            CustomResourceManager.ToUpdateResources(updateResourcesCompleteCallback);
        }
    }

}

此处的接口是按照ugf的接口进行提出的,方便适配

在这里插入代码片
    /// <summary>
    /// 使用Resource组件时,请按照该组件的接口进行调用。
    /// </summary>
    public interface ICustomResourceManager
    {
        void ToLoadScene(string sceneAssetName, LoadSceneCallbacks loadSceneCallbacks, int priority, object userData);
        void ToUnloadScene(string sceneAssetName, UnloadSceneCallbacks unloadSceneCallbacks, object userData);

        void ToLoadAsset(string assetName, Type assetType, int priority,
            GameFramework.Resource.LoadAssetCallbacks loadAssetCallbacks, object userData);

        void ToUnloadAsset(object asset);

        /// <summary>
        /// 组件初始化
        /// </summary>
        void ToInitResources(InitResourcesCompleteCallback initResourcesCompleteCallback);

        /// <summary>
        /// 更新资源
        /// </summary>
        void ToUpdateResources(UpdateResourcesCompleteCallback updateResourcesCompleteCallback);
    }

使用mono来实现这个接口,在启动应用的时候注册到GFAdapterResourceManager 中,操作和ufg的editor资源加载方案相似。

在这里插入代码片
    public abstract class CustomResourceManagerBase : MonoBehaviour,ICustomResourceManager
    {
        public abstract void ToLoadScene(string sceneAssetName, LoadSceneCallbacks loadSceneCallbacks, int priority,
            object userData);

        public abstract void ToUnloadScene(string sceneAssetName, UnloadSceneCallbacks unloadSceneCallbacks,
            object userData);

        public abstract void ToLoadAsset(string assetName, Type assetType, int priority,
            LoadAssetCallbacks loadAssetCallbacks,
            object userData);

        public abstract void ToUnloadAsset(object asset);
        public abstract void ToInitResources(InitResourcesCompleteCallback initResourcesCompleteCallback);

        public abstract void ToUpdateResources(UpdateResourcesCompleteCallback updateResourcesCompleteCallback);

        public virtual void Awake()
        {
            GFAdapterResourceManager.instance.CustomResourceManager = this;
        }
    }

实现CustomResourceManagerBase,简单的把ugf中的editor的加载方案移植过来。这里就是写其他资源管理方案的地方。

 public class EditorResourceManager: CustomResourceManagerBase
    {
        
        private static readonly int AssetsStringLength = "Assets".Length;
        
        public override void ToLoadScene(string sceneAssetName, LoadSceneCallbacks loadSceneCallbacks, int priority, object userData)
        {
            AsyncOperation asyncOperation = SceneManager.LoadSceneAsync(sceneAssetName, LoadSceneMode.Additive);
            if(asyncOperation==null) return;
            asyncOperation.completed += (operation) =>
            {
                if(loadSceneCallbacks==null) return;
                if (operation.allowSceneActivation)
                {
                    loadSceneCallbacks.LoadSceneSuccessCallback(sceneAssetName, 0, userData);
                }
                else
                {
                    loadSceneCallbacks.LoadSceneFailureCallback(sceneAssetName,LoadResourceStatus.NotReady,"未知错误",userData);
                }
            };
        }

        public override void ToUnloadScene(string sceneAssetName, UnloadSceneCallbacks unloadSceneCallbacks, object userData)
        {
            AsyncOperation asyncOperation = SceneManager.UnloadSceneAsync(sceneAssetName);
            if (asyncOperation == null)
            {
                return;
            }
            asyncOperation.completed += (operation) =>
            {
                if(unloadSceneCallbacks==null) return;
                
                if (operation.isDone)
                {
                    unloadSceneCallbacks.UnloadSceneSuccessCallback(sceneAssetName, userData);
                }
                else
                {
                    unloadSceneCallbacks.UnloadSceneFailureCallback(sceneAssetName, userData);
                }
            };
        }

        public override void ToLoadAsset(string assetName, Type assetType, int priority, LoadAssetCallbacks loadAssetCallbacks,
            object userData)
        {
            if (loadAssetCallbacks == null)
            {
                Log.Error("Load asset callbacks is invalid.");
                return;
            }

            if (string.IsNullOrEmpty(assetName))
            {
                if (loadAssetCallbacks.LoadAssetFailureCallback != null)
                {
                    loadAssetCallbacks.LoadAssetFailureCallback(assetName, LoadResourceStatus.NotExist, "Asset name is invalid.", userData);
                }

                return;
            }

            if (!assetName.StartsWith("Assets/", StringComparison.Ordinal))
            {
                if (loadAssetCallbacks.LoadAssetFailureCallback != null)
                {
                    loadAssetCallbacks.LoadAssetFailureCallback(assetName, LoadResourceStatus.NotExist, Utility.Text.Format("Asset name '{0}' is invalid.", assetName), userData);
                }

                return;
            }

            if (!HasFile(assetName))
            {
                if (loadAssetCallbacks.LoadAssetFailureCallback != null)
                {
                    loadAssetCallbacks.LoadAssetFailureCallback(assetName, LoadResourceStatus.NotExist, Utility.Text.Format("Asset '{0}' is not exist.", assetName), userData);
                }

                return;
            }
#if UNITY_EDITOR
            if (assetType != null)
            {
                var asset = UnityEditor.AssetDatabase.LoadAssetAtPath(assetName, assetType);
            }
            else
            {
               var  asset = UnityEditor.AssetDatabase.LoadMainAssetAtPath(assetName);
            }
#endif            
           
        }

        public override void ToUnloadAsset(object asset)
        {
            return;
        }

        public override void ToInitResources(InitResourcesCompleteCallback initResourcesCompleteCallback)
        {
            initResourcesCompleteCallback?.Invoke();
        }

        public override void ToUpdateResources(UpdateResourcesCompleteCallback updateResourcesCompleteCallback)
        {
            updateResourcesCompleteCallback?.Invoke(null,true);
        }
        private bool HasFile(string assetName)
        {
            if (string.IsNullOrEmpty(assetName))
            {
                return false;
            }

            if (HasCachedAsset(assetName))
            {
                return true;
            }

            string assetFullName = Application.dataPath.Substring(0, Application.dataPath.Length - AssetsStringLength) + assetName;
            if (string.IsNullOrEmpty(assetFullName))
            {
                return false;
            }

            string[] splitedAssetFullName = assetFullName.Split('/');
            string currentPath = Path.GetPathRoot(assetFullName);
            for (int i = 1; i < splitedAssetFullName.Length - 1; i++)
            {
                string[] directoryNames = Directory.GetDirectories(currentPath, splitedAssetFullName[i]);
                if (directoryNames.Length != 1)
                {
                    return false;
                }

                currentPath = directoryNames[0];
            }

            string[] fileNames = Directory.GetFiles(currentPath, splitedAssetFullName[splitedAssetFullName.Length - 1]);
            if (fileNames.Length != 1)
            {
                return false;
            }

            string fileFullName = Utility.Path.GetRegularPath(fileNames[0]);
            if (fileFullName == null)
            {
                return false;
            }

            if (assetFullName != fileFullName)
            {
                if (assetFullName.ToLowerInvariant() == fileFullName.ToLowerInvariant())
                {
                    Log.Warning("The real path of the specific asset '{0}' is '{1}'. Check the case of letters in the path.", assetName, "Assets" + fileFullName.Substring(Application.dataPath.Length));
                }

                return false;
            }

            return true;
        }
        private bool HasCachedAsset(string assetName)
        {
            return false;
        }
    }

最后注册到ResourceComponent中。需要修改ResourceComponent的代码。
只需要 m_ResourceManager = m_EditorResourceMode ? baseComponent.EditorResourceHelper : GameFrameworkEntry.GetModule();这一行前额外加一个判断。由于我们实际使用的过程中只会使用上面定义的接口,所以可以直接return了。

在这里插入代码片
private void Start()
        {
            BaseComponent baseComponent = GameEntry.GetComponent<BaseComponent>();
            if (baseComponent == null)
            {
                Log.Fatal("Base component is invalid.");
                return;
            }

            m_EventComponent = GameEntry.GetComponent<EventComponent>();
            if (m_EventComponent == null)
            {
                Log.Fatal("Event component is invalid.");
                return;
            }

            m_EditorResourceMode = baseComponent.EditorResourceMode;
            ///如果使用自定义,则使用自定义,否则按照原来的方案执行
#region 存在修改的代码
            if (GFAdapterResourceManager.instance.CustomResourceManager!=null)
            {
                m_ResourceManager = GFAdapterResourceManager.instance;
                return;
            }
            else
            {
            //原来的代码
                m_ResourceManager = m_EditorResourceMode ? baseComponent.EditorResourceHelper : GameFrameworkEntry.GetModule<IResourceManager>();
            }
 #endregion
            

这样我们就完成了resourceManager的改造。
但是在使用ui、scene、sound、entity这些组件功能时,依旧会调用原生的resourcemanager,也就是GF中的资源加载方案。这个时候需要在对应的代码中,将资源管理器替换掉。
下面是个简单的例子UIComponent

private void Start()
        {
            BaseComponent baseComponent = GameEntry.GetComponent<BaseComponent>();
            if (baseComponent == null)
            {
                Log.Fatal("Base component is invalid.");
                return;
            }

            m_EventComponent = GameEntry.GetComponent<EventComponent>();
            if (m_EventComponent == null)
            {
                Log.Fatal("Event component is invalid.");
                return;
            }
#region 增加的代码
            if (GFAdapterResourceManager.instance.CustomResourceManager != null)
            {
                m_UIManager.SetResourceManager(GFAdapterResourceManager.instance);
            }
            else
            {
            
				//原来的代码
                if (baseComponent.EditorResourceMode)
                {
                    m_UIManager.SetResourceManager(baseComponent.EditorResourceHelper);
                }
                else
                {
                    
                    m_UIManager.SetResourceManager(GameFrameworkEntry.GetModule<IResourceManager>());
                }
#endregion             
            }
            ....省略其他代码

最后我简单接入了yooasset分享一下接入方案

绝大数代码是从yooasset的案例里copy的。
YooAssetResourceManager.cs

using System;
using System.Collections.Generic;
using GameFramework.Resource;
using UnityEngine;
using UnityEngine.SceneManagement;
using YooAsset;
using Object = UnityEngine.Object;

namespace GameFrameworkExtension
{

    public partial class YooAssetResourceManager : CustomResourceManagerBase
    {
        [SerializeField]
        public EPlayMode Mode;

        private ResourcePackage package;
        
        /// <summary>
        /// 记录对应handler
        /// </summary>
        private Dictionary<string, Queue<AssetHandle>> assetHandleNameMap;
        private Dictionary<int, string> assetInstanceIdNameMap;
        
        private Dictionary<string, SceneHandle> sceneHandles;
        public override void ToLoadScene(string sceneAssetName, LoadSceneCallbacks loadSceneCallbacks, int priority, object userData)
        {
            var handler= package.LoadSceneAsync(sceneAssetName, LoadSceneMode.Additive,true,(uint)priority);
          
            float time = Time.realtimeSinceStartup;
            handler.Completed += (handler) =>
            {
                if (handler.SceneObject != null)
                {
                    loadSceneCallbacks.LoadSceneSuccessCallback.Invoke(sceneAssetName,Time.realtimeSinceStartup-time,userData);
                    sceneHandles.Add(sceneAssetName,handler);
                }
                else
                {
                    loadSceneCallbacks.LoadSceneFailureCallback.Invoke(sceneAssetName,LoadResourceStatus.NotExist,handler.LastError,userData);
                }
               
            };

        }

        public override void ToUnloadScene(string sceneAssetName, UnloadSceneCallbacks unloadSceneCallbacks, object userData)
        {
            SceneHandle handler= sceneHandles[sceneAssetName];
            handler.UnloadAsync();
            sceneHandles.Remove(sceneAssetName);

        }

        public override void ToLoadAsset(string assetName, Type assetType, int priority, LoadAssetCallbacks loadAssetCallbacks,
            object userData)
        {
            if (!assetHandleNameMap.ContainsKey(assetName))
            {
                assetHandleNameMap.Add(assetName,new Queue<AssetHandle>());
            }
            // package.GetAssetInfo(assetName).
            ///如果是同一个资产,那么就会找到对应的引用。返回同一个对象。但是handler却是新的
            /// handler仅仅只是句柄,对应引用的是同一个。因此可以维持一个dic<name,list<handler>>,卸载对象时,从list中取出。再去一个dic<instanceid,name>作为映射
            var handler= package.LoadAssetAsync(assetName, assetType,(uint)priority);
            float time = Time.realtimeSinceStartup;
            assetHandleNameMap[assetName].Enqueue(handler);
            handler.Completed += (handler) =>
            {
                if (handler.AssetObject != null)
                {
                    loadAssetCallbacks.LoadAssetSuccessCallback.Invoke(assetName,handler.AssetObject,Time.realtimeSinceStartup-time,userData);
                    int instanceId = handler.AssetObject.GetInstanceID();
                    if (!assetInstanceIdNameMap.ContainsKey(instanceId))
                    {
                        assetInstanceIdNameMap.Add(instanceId,assetName);
                    }
                }
                else
                {
                    loadAssetCallbacks.LoadAssetFailureCallback.Invoke(assetName,LoadResourceStatus.NotExist,handler.LastError,userData);
                }
               
            };
        }

        public override void ToUnloadAsset(object asset)
        {
            int instanceID = (asset as Object).GetInstanceID();
            if (assetInstanceIdNameMap.ContainsKey(instanceID))
            {
                string name = assetInstanceIdNameMap[instanceID];
                if (assetHandleNameMap[name].Count > 0)
                {
                    var handler = assetHandleNameMap[name].Dequeue();
                    handler.Release();
                }
            }
           
        }

        public override void ToInitResources(InitResourcesCompleteCallback initResourcesCompleteCallback)
        {

            assetHandleNameMap = new Dictionary<string, Queue<AssetHandle>>();
            assetInstanceIdNameMap = new Dictionary<int, string>();
            sceneHandles = new Dictionary<string, SceneHandle>();
            // 初始化资源系统
            YooAssets.Initialize();
            // 创建默认的资源包
            package = YooAssets.CreatePackage("DefaultPackage");
            // 设置该资源包为默认的资源包,可以使用YooAssets相关加载接口加载该资源包内容。
            YooAssets.SetDefaultPackage(package);
            switch (Mode)
            {
                case EPlayMode.EditorSimulateMode:
                    StartCoroutine(EditorModeInit(initResourcesCompleteCallback));
                    break;
                case EPlayMode.HostPlayMode:
                    StartCoroutine(HostPlayModeModeInit(initResourcesCompleteCallback));
                    break;
                case EPlayMode.OfflinePlayMode:
                    StartCoroutine(OfflineModeInit(initResourcesCompleteCallback));
                    break;
                case EPlayMode.WebPlayMode:
                    StartCoroutine(WebPlayModeInit(initResourcesCompleteCallback));
                    break;
            }
        }

        public override void ToUpdateResources(UpdateResourcesCompleteCallback updateResourcesCompleteCallback)
        {
            StartCoroutine(UpdateResources(updateResourcesCompleteCallback));
        }
    }

}

YooAssetResourceManager.Definition.cs

using System.IO;
using UnityEngine;
using YooAsset;

namespace GameFrameworkExtension
{

    public partial class YooAssetResourceManager
    {
                private string GetHostServerURL()
        {
            //string hostServerIP = "http://10.0.2.2"; //安卓模拟器地址
            string hostServerIP = "http://127.0.0.1";
            string appVersion = "v1.0";

#if UNITY_EDITOR
            if (UnityEditor.EditorUserBuildSettings.activeBuildTarget == UnityEditor.BuildTarget.Android)
                return $"{hostServerIP}/CDN/Android/{appVersion}";
            else if (UnityEditor.EditorUserBuildSettings.activeBuildTarget == UnityEditor.BuildTarget.iOS)
                return $"{hostServerIP}/CDN/IPhone/{appVersion}";
            else if (UnityEditor.EditorUserBuildSettings.activeBuildTarget == UnityEditor.BuildTarget.WebGL)
                return $"{hostServerIP}/CDN/WebGL/{appVersion}";
            else
                return $"{hostServerIP}/CDN/PC/{appVersion}";
#else
        if (Application.platform == RuntimePlatform.Android)
            return $"{hostServerIP}/CDN/Android/{appVersion}";
        else if (Application.platform == RuntimePlatform.IPhonePlayer)
            return $"{hostServerIP}/CDN/IPhone/{appVersion}";
        else if (Application.platform == RuntimePlatform.WebGLPlayer)
            return $"{hostServerIP}/CDN/WebGL/{appVersion}";
        else
            return $"{hostServerIP}/CDN/PC/{appVersion}";
#endif
        }
        /// <summary>
        /// 远端资源地址查询服务类
        /// </summary>
        private class RemoteServices : IRemoteServices
        {
            private readonly string _defaultHostServer;
            private readonly string _fallbackHostServer;

            public RemoteServices(string defaultHostServer, string fallbackHostServer)
            {
                _defaultHostServer = defaultHostServer;
                _fallbackHostServer = fallbackHostServer;
            }
            string IRemoteServices.GetRemoteMainURL(string fileName)
            {
                return $"{_defaultHostServer}/{fileName}";
            }
            string IRemoteServices.GetRemoteFallbackURL(string fileName)
            {
                return $"{_fallbackHostServer}/{fileName}";
            }
        }
        /// <summary>
        /// 资源文件流加载解密类
        /// </summary>
        private class FileStreamDecryption : IDecryptionServices
        {
            /// <summary>
            /// 同步方式获取解密的资源包对象
            /// 注意:加载流对象在资源包对象释放的时候会自动释放
            /// </summary>
            AssetBundle IDecryptionServices.LoadAssetBundle(DecryptFileInfo fileInfo, out Stream managedStream)
            {
                BundleStream bundleStream = new BundleStream(fileInfo.FileLoadPath, FileMode.Open, FileAccess.Read, FileShare.Read);
                managedStream = bundleStream;
                return AssetBundle.LoadFromStream(bundleStream, fileInfo.ConentCRC, GetManagedReadBufferSize());
            }

            /// <summary>
            /// 异步方式获取解密的资源包对象
            /// 注意:加载流对象在资源包对象释放的时候会自动释放
            /// </summary>
            AssetBundleCreateRequest IDecryptionServices.LoadAssetBundleAsync(DecryptFileInfo fileInfo, out Stream managedStream)
            {
                BundleStream bundleStream = new BundleStream(fileInfo.FileLoadPath, FileMode.Open, FileAccess.Read, FileShare.Read);
                managedStream = bundleStream;
                return AssetBundle.LoadFromStreamAsync(bundleStream, fileInfo.ConentCRC, GetManagedReadBufferSize());
            }

            private static uint GetManagedReadBufferSize()
            {
                return 1024;
            }
        }
        /// <summary>
        /// 资源文件偏移加载解密类
        /// </summary>
        private class FileOffsetDecryption : IDecryptionServices
        {
            /// <summary>
            /// 同步方式获取解密的资源包对象
            /// 注意:加载流对象在资源包对象释放的时候会自动释放
            /// </summary>
            AssetBundle IDecryptionServices.LoadAssetBundle(DecryptFileInfo fileInfo, out Stream managedStream)
            {
                managedStream = null;
                return AssetBundle.LoadFromFile(fileInfo.FileLoadPath, fileInfo.ConentCRC, GetFileOffset());
            }

            /// <summary>
            /// 异步方式获取解密的资源包对象
            /// 注意:加载流对象在资源包对象释放的时候会自动释放
            /// </summary>
            AssetBundleCreateRequest IDecryptionServices.LoadAssetBundleAsync(DecryptFileInfo fileInfo, out Stream managedStream)
            {
                managedStream = null;
                return AssetBundle.LoadFromFileAsync(fileInfo.FileLoadPath, fileInfo.ConentCRC, GetFileOffset());
            }

            private static ulong GetFileOffset()
            {
                return 32;
            }
        }
    }
    /// <summary>
    /// 资源文件解密流
    /// </summary>
    public class BundleStream : FileStream
    {
        public const byte KEY = 64;

        public BundleStream(string path, FileMode mode, FileAccess access, FileShare share) : base(path, mode, access, share)
        {
        }
        public BundleStream(string path, FileMode mode) : base(path, mode)
        {
        }

        public override int Read(byte[] array, int offset, int count)
        {
            var index = base.Read(array, offset, count);
            for (int i = 0; i < array.Length; i++)
            {
                array[i] ^= KEY;
            }
            return index;
        }
    }
}

YooAssetResourceManager.Init.cs

using System.Collections;
using System.IO;
using GameFramework.Resource;
using UnityEngine;
using YooAsset;

namespace GameFrameworkExtension
{

    public partial class YooAssetResourceManager
    {
        IEnumerator EditorModeInit(InitResourcesCompleteCallback initResourcesCompleteCallback)
        {
            var initParameters = new EditorSimulateModeParameters();
            var simulateManifestFilePath = EditorSimulateModeHelper.SimulateBuild(EDefaultBuildPipeline.BuiltinBuildPipeline, "DefaultPackage");
            initParameters.SimulateManifestFilePath  = simulateManifestFilePath;
            yield return package.InitializeAsync(initParameters);
            initResourcesCompleteCallback.Invoke();
        }

        IEnumerator OfflineModeInit(InitResourcesCompleteCallback initResourcesCompleteCallback)
        {
            var initParameters = new OfflinePlayModeParameters();
            yield return package.InitializeAsync(initParameters);
            initResourcesCompleteCallback.Invoke();
        }

        IEnumerator HostPlayModeModeInit(InitResourcesCompleteCallback initResourcesCompleteCallback)
        {
            // 注意:GameQueryServices.cs 太空战机的脚本类,详细见StreamingAssetsHelper.cs
            string defaultHostServer = "http://127.0.0.1/CDN/Android/v1.0";
            string fallbackHostServer = "http://127.0.0.1/CDN/Android/v1.0";
            var initParameters = new HostPlayModeParameters();
            initParameters.BuildinQueryServices = new GameQueryServices(); 
            initParameters.DecryptionServices = new FileOffsetDecryption();
            initParameters.RemoteServices = new RemoteServices(defaultHostServer, fallbackHostServer);
            var initOperation = package.InitializeAsync(initParameters);
            yield return initOperation;
        
            if(initOperation.Status == EOperationStatus.Succeed)
            {
                Debug.Log("资源包初始化成功!");
                initResourcesCompleteCallback.Invoke();
            }
            else 
            {
                Debug.LogError($"资源包初始化失败:{initOperation.Error}");
            }
        }
        
        IEnumerator WebPlayModeInit(InitResourcesCompleteCallback initResourcesCompleteCallback)
        {
            // 注意:GameQueryServices.cs 太空战机的脚本类,详细见StreamingAssetsHelper.cs
            string defaultHostServer = "http://127.0.0.1/CDN/WebGL/v1.0";
            string fallbackHostServer = "http://127.0.0.1/CDN/WebGL/v1.0";
            var initParameters = new WebPlayModeParameters();
            initParameters.BuildinQueryServices = new GameQueryServices();
            initParameters.RemoteServices = new RemoteServices(defaultHostServer, fallbackHostServer);
            var initOperation = package.InitializeAsync(initParameters);
            yield return initOperation;
        
            if(initOperation.Status == EOperationStatus.Succeed)
            {
                Debug.Log("资源包初始化成功!");
                initResourcesCompleteCallback.Invoke();
            }
            else 
            {
                Debug.LogError($"资源包初始化失败:{initOperation.Error}");
            }
        }


    }





}

YooAssetResourceManager.Update.cs


using System.Collections;
using GameFramework.Resource;
using UnityEngine;
using YooAsset;

namespace GameFrameworkExtension
{

    public partial class YooAssetResourceManager
    {
        private string packageVersion;

        IEnumerator UpdateResources(UpdateResourcesCompleteCallback updateResourcesCompleteCallback)
        {
            yield return StartCoroutine(UpdatePackageVersion(updateResourcesCompleteCallback));
            if (packageVersion != null)
            {
                yield return StartCoroutine(UpdatePackageManifest(updateResourcesCompleteCallback));
            }
        }
        private IEnumerator UpdatePackageVersion(UpdateResourcesCompleteCallback updateResourcesCompleteCallback)
        {
            var package = YooAssets.GetPackage("DefaultPackage");
            var operation = package.UpdatePackageVersionAsync();
            yield return operation;

            if (operation.Status == EOperationStatus.Succeed)
            {
                //更新成功
                packageVersion = operation.PackageVersion;
                Debug.Log($"Updated package Version : {packageVersion}");
            }
            else
            {
                updateResourcesCompleteCallback.Invoke(null,false);
                //更新失败
                Debug.LogError(operation.Error);
            }
        }
        private IEnumerator UpdatePackageManifest(UpdateResourcesCompleteCallback updateResourcesCompleteCallback)
        {
            // 更新成功后自动保存版本号,作为下次初始化的版本。
            // 也可以通过operation.SavePackageVersion()方法保存。
            bool savePackageVersion = true;
            var package = YooAssets.GetPackage("DefaultPackage");
            var operation = package.UpdatePackageManifestAsync(packageVersion, savePackageVersion);
            yield return operation;

            if (operation.Status == EOperationStatus.Succeed)
            {
                //更新成功
                updateResourcesCompleteCallback.Invoke(null,true);
            }
            else
            {
                updateResourcesCompleteCallback.Invoke(null,false);
                //更新失败
                Debug.LogError(operation.Error);
            }
        }
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值