在做之前,我们需要研究一下,常用的几款资源管理方案。
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);
}
}
}
}