ResourceComponent有两种Resource Mode: Package(单机模式),Updatable(可更新模式)
①设置ResourceComponent的Resource Mode为Package(单机模式)
②将AssetBundle Builder生成的Package下对应平台的文件复制到StreamingAssets
③使用UGF.Resource.InitResources()初始化资源,在ResourceInitComplete时就可以使用资源了。
using GameFramework;
using GameFramework.Fsm;
using GameFramework.Procedure;
using UnityGameFramework.Runtime;
using GameFramework.Event;
public class LaunchProcedure : ProcedureBase
{
private bool isLoadSceneSuccess = false;
protected override void OnEnter(IFsm procedureOwner)
{
base.OnEnter(procedureOwner);
Log.Info("LaunchProcedure Enter...");
UGF.Event.Subscribe(LoadSceneSuccessEventArgs.EventId, OnLoadSceneSuccess);
UGF.Event.Subscribe(ResourceInitCompleteEventArgs.EventId, OnResourceInitComplete);
UGF.Resource.InitResources();
}
protected override void OnUpdate(IFsm procedureOwner, float elapseSeconds, float realElapseSeconds)
{
base.OnUpdate(procedureOwner, elapseSeconds, realElapseSeconds);
if (isLoadSceneSuccess)
ChangeState(procedureOwner);//场景加载成功,切换流程
}
protected override void OnLeave(IFsm procedureOwner, bool isShutdown)
{
base.OnLeave(procedureOwner, isShutdown);
UGF.Event.Unsubscribe(LoadSceneSuccessEventArgs.EventId, OnLoadSceneSuccess);
UGF.Event.Unsubscribe(ResourceInitCompleteEventArgs.EventId, OnResourceInitComplete);
}
private void OnLoadSceneSuccess(object sender, GameEventArgs e)
{
var ne = (LoadSceneSuccessEventArgs)e;
Log.Info("场景{0}加载成功", ne.SceneAssetName);
isLoadSceneSuccess = true;
}
private void OnResourceInitComplete(object sender, GameEventArgs e)
{
//AssetBundle资源初始化完成,从AssetBundle中加载场景
Log.Info("OnResourceInitComplete...");
UGF.Scene.LoadScene("Assets/GameMain/Scenes/MainMenu.unity");
}
}
资源热更流程图:
AssetBundle Tools的使用中介绍过,Output FullPath是可更新模式生成的完整文件包的目录,需要将此目录放到服务器供客户端资源更新。
需要将最新游戏版本号、最新资源版本号/下载地址/资源大小/HashCode等信息,例如:
{
"LatestGameVersion": "1.2",//最新游戏版本号
"ApplicableGameVersion": [ "1.0", "1.1", "1.2" ],//最新资源适用的游戏版本号
"LatestInternalResourceVersion": 1,//最新资源版本号,数值由AB Builder生成,在GameResourceVersion.xml中
"ResourceUpdateUrl": "http://localhost/Full",//资源地址
"ResourceLength": 331,
"ResourceHashCode": -2073543694,
"ResourceZipLength": 211,
"ResourceZipHashCode": 371407597
}
AssetBundle Builder打包资源后会在用户设置的Output Directory下生成GameResourceVersion.xml文件,此文件
中记录了版本文件的校验码(HashCode),在做资源更新服务的时候会用到这些值。将对应数据写入文件,然后将文件放到服务器,由客户端通过WebRequest获取最新app版本信息及资源信息。
需要订阅WebRequestSuccessEventArgs事件,在WebRequest成功后再调用CheckVersionList进行版本资源列表检查
此过程更新的是version.dat文件,打开Full文件夹下的资源可以看到资源名后面都接了校验码,该函数需要传入ResourceLength,ResourceHashCode, ResourceZipLength, ResourceZipHashCode,其中HashCode用来产生对应的文件名。如果HashCode填错就会因服务端没有对应文件而导致请求超时。
该函数需要订阅VersionListUpdateSuccessEventArgs事件,更新资源列表成功后调用CheckResources检测资源
- ResourceUpdateStartEventArgs:开始下载文件时调用,每个文件调用一次
- ResourceUpdateFailureEventArgs:文件下载失败时调用
- ResourceUpdateSuccessEventArgs:文件下载完成时调用
- ResourceUpdateAllCompleteEventArgs:所有文件下载完成时调用
- ResourceUpdateChangedEventArgs:下载进度改变时调用, 可以获取当前已下载的大小
资源热更新示例代码:
CheckVersionProcedure:UpdateResourceProcedure:using System; using UnityEngine; using GameFramework; using GameFramework.Fsm; using GameFramework.Procedure; using GameFramework.Event; using UnityGameFramework.Runtime; namespace ShadowTank { [Serializable] public class VersionInfo { public string LatestGameVersion; public string[] ApplicableGameVersion; public int LatestInternalResourceVersion; public string ResourceUpdateUrl; public int ResourceLength; public int ResourceHashCode; public int ResourceZipLength; public int ResourceZipHashCode; } public class CheckVersioProcedure : ProcedureBase { private bool mUpdateVerListCompleted = false; private VersionInfo mVersionInfo = null; protected override void OnEnter(IFsm
procedureOwner) { base.OnEnter(procedureOwner); Log.Info("CheckVersionProcedure Enter..."); UGF.Event.Subscribe(WebRequestSuccessEventArgs.EventId, OnWebRequestSuccess); UGF.Event.Subscribe(VersionListUpdateSuccessEventArgs.EventId, OnVersionListUpdateSuccess); UGF.WebRequest.AddWebRequest("http://localhost/version.json"); } protected override void OnUpdate(IFsm procedureOwner, float elapseSeconds, float realElapseSeconds) { base.OnUpdate(procedureOwner, elapseSeconds, realElapseSeconds); if (mUpdateVerListCompleted) ChangeState (procedureOwner); } private void OnWebRequestSuccess(object sender, GameEventArgs eventArgs) { var e = (WebRequestSuccessEventArgs)eventArgs; mVersionInfo = Utility.Json.ToObject (e.GetWebResponseBytes()); Log.Info("CurrentGameVersion:{0},LatestGameVersion:{1}", Application.version,mVersionInfo.LatestGameVersion); Version currentVer = new Version(Application.version); Version latestVer = new Version(mVersionInfo.LatestGameVersion); if(latestVer.CompareTo(currentVer)>0) { Log.Info("游戏有新版本"); } UGF.Resource.UpdatePrefixUri = Utility.Path.GetCombinePath(mVersionInfo.ResourceUpdateUrl, GetResourceVersion(),GetOSName()); Log.Info(UGF.Resource.UpdatePrefixUri); if (UGF.Resource.CheckVersionList(mVersionInfo.LatestInternalResourceVersion)==GameFramework.Resource.CheckVersionListResult.Updated) { Log.Info("version.dat已是最新"); mUpdateVerListCompleted = true; return; } //更新Version.dat UGF.Resource.UpdateVersionList(mVersionInfo.ResourceLength, mVersionInfo.ResourceHashCode, mVersionInfo.ResourceZipLength, mVersionInfo.ResourceZipHashCode); } private void OnVersionListUpdateSuccess(object sender, GameEventArgs e) { Log.Info("version.dat更新完成!"); mUpdateVerListCompleted = true; } private string GetResourceVersion() { var versionStr = Application.version.Replace('.', '_'); return string.Format("{0}_{1}", versionStr, mVersionInfo.LatestInternalResourceVersion); } //获取操作系统名 private string GetOSName() { string platformName = string.Empty; if (Application.platform == RuntimePlatform.Android) platformName = "android"; else if (Application.platform == RuntimePlatform.WindowsEditor || Application.platform == RuntimePlatform.WindowsPlayer) platformName = "windows"; else if (Application.platform == RuntimePlatform.IPhonePlayer) platformName = "ios"; else if (Application.platform == RuntimePlatform.OSXEditor || Application.platform == RuntimePlatform.OSXPlayer) platformName = "osx"; return platformName; } } } using GameFramework; using GameFramework.Fsm; using GameFramework.Procedure; using GameFramework.Event; using UnityGameFramework.Runtime; namespace ShadowTank { public class UpdateResourcesProcedure : ProcedureBase { private bool mResourceUpdateAllComplete = false; protected override void OnEnter(IFsm
procedureOwner) { base.OnEnter(procedureOwner); Log.Info("UpdateResourcesProcedure Enter..."); UGF.Event.Subscribe(ResourceCheckCompleteEventArgs.EventId, OnResourceCheckComplete); UGF.Event.Subscribe(ResourceUpdateSuccessEventArgs.EventId, OnResourceUpdateSuccess); UGF.Event.Subscribe(ResourceUpdateAllCompleteEventArgs.EventId, OnResourceUpdateAllComplete); UGF.Resource.CheckResources(); } protected override void OnUpdate(IFsm procedureOwner, float elapseSeconds, float realElapseSeconds) { base.OnUpdate(procedureOwner, elapseSeconds, realElapseSeconds); //更新完成,切换到MainMenu场景 if (mResourceUpdateAllComplete) ChangeState (procedureOwner); } private void OnResourceCheckComplete(object sender, GameEventArgs e) { var checkInfo = (ResourceCheckCompleteEventArgs)e; Log.Info("OnResourceCheckComplete,需要更新{0}个资源...", checkInfo.UpdateCount); if (checkInfo.UpdateCount <= 0) { //无可更新资源 mResourceUpdateAllComplete = true; return; } //更新资源 UGF.Resource.UpdateResources(); } private void OnResourceUpdateSuccess(object sender, GameEventArgs e) { var info = (ResourceUpdateSuccessEventArgs)e; Log.Info("{0}下载完成...", info.Name); } private void OnResourceUpdateAllComplete(object sender, GameEventArgs e) { mResourceUpdateAllComplete = true; Log.Info("资源全部更新完成!"); } } }