【UGF】(三)Resource模块的使用及资源热更新

ResourceComponent有两种Resource Mode: Package(单机模式),Updatable(可更新模式)

一.Resource Mode: Package(单机模式)

单机模式:资源打包后将AssetBundle Builder生成的Package下对应平台文件夹下的资源直接复制到StreamingAssets,资源随游戏一起打包

举个栗子:

①设置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");
        }
    }

二.Resource Mode:Updatable(可更新模式)

资源热更流程图:

【UGF】(三)Resource模块的使用及资源热更新_第1张图片

AssetBundle Tools的使用中介绍过,Output FullPath是可更新模式生成的完整文件包的目录,需要将此目录放到服务器供客户端资源更新。

1.向服务端发送WebRequest请求获取最新的版本数据:

需要将最新游戏版本号、最新资源版本号/下载地址/资源大小/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进行版本资源列表检查

2.CheckVersionList检查版本资源列表(version.dat)是否有更新

该函数需要传入LatestInternalResourceVersion值,跟当前本地资源版本进行比较判断是否需要更新

3.UpdateVersionList更新资源列表

此过程更新的是version.dat文件,打开Full文件夹下的资源可以看到资源名后面都接了校验码,该函数需要传入ResourceLength,ResourceHashCode, ResourceZipLength, ResourceZipHashCode,其中HashCode用来产生对应的文件名。如果HashCode填错就会因服务端没有对应文件而导致请求超时。

该函数需要订阅VersionListUpdateSuccessEventArgs事件,更新资源列表成功后调用CheckResources检测资源

4.CheckResources检查是否有资源需要更新

根据version.dat检查是否有可更新资源,ResourceCheckCompleteEventArgs.UpdateCount表示需要更新的资源个数,大于0表示需要更新。
该函数需要订阅ResourceCheckCompleteEventArgs事件,资源检测完成后根据是否有可更新资源决定是否调用UpdateResources下载资源

5.UpdateResources从服务器更新资源

CheckResources成功后,开始从服务器下载需要更新的资源。
该函数可以注册的回调事件:

  • ResourceUpdateStartEventArgs:开始下载文件时调用,每个文件调用一次
  • ResourceUpdateFailureEventArgs:文件下载失败时调用
  • ResourceUpdateSuccessEventArgs:文件下载完成时调用
  • ResourceUpdateAllCompleteEventArgs:所有文件下载完成时调用
  • ResourceUpdateChangedEventArgs:下载进度改变时调用, 可以获取当前已下载的大小

资源热更新示例代码:

CheckVersionProcedure:
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;
        }
    }
}
UpdateResourceProcedure:
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("资源全部更新完成!");
        }
    }
}



你可能感兴趣的:(Unity游戏框架)