GameFramework框架详解之(三)资源管理器

资源加载

一.前言

GF实现了一套资源加载模块,只提供了一套异步加载的接口,而且也并不打算提供同步加载的接口。

二.资源框架简介

1. 资源类型定义

GF资源管理内部,实现了两个关于资源类的定义:

  • 1.Asset类,主要用于描述可以直接食用的资源,比如:预制体,Sprite,声音等
  • 2.Resource类,主要用于描述AssetBundle类型的资源,或者Asset转化为二进制形式。

使用过程中GF的方法一般都是面向Asset的,Resource 的方法都隐藏在内部了,为了降低使用的复杂度。


2.资源加载模式

GF加载资源分为了两种模式:
1.编辑器模式:开发时使用,直接使用AssetDatabase加载本地文件。
2.模拟模式:模拟发布时资源加载的情况,从AssetBundle读取并解析资源,不过在使用模拟模式之前,你需要先build出assetbundle包。


3.资源更新模式

资源的更新模式,一般的框架都是预下载的形式,就是游戏每次运行前就提前检测热更并提前下载好。不过GF框架提供了两种热更模式:

  • 1.运行前更新模式:不多解释
  • 2:运行时更新模式:游戏运行过程中,根据需要,选在是否下载某个文件。

一般的网络游戏都是采用运行前热更,很少采用运行时热更,因为这样体验太差。不过如果你的游戏是分模块的,比如:大厅式棋牌类游戏。游戏大厅内,用户根据需求下载喜欢的小游戏去玩,这样不仅灵活,还能节省用户的流量。


三.资源管理结构

1.GF.ResourceManager

首先来看下GF框架最核心的GF.ResourceManager类的简化代码结构

//框架底层的资源管理器
public class GF.ResourceManager
{
	private const string RemoteVersionListFileName = "GameFrameworkVersion.dat";
	private const string LocalVersionListFileName = "GameFrameworkList.dat";
    
   	private VersionListSerializer xxxVersionListSerializer;//版本资源列表序列化器
	...
    
    private IFileSystemManager m_FileSystemManager;//文件系统管理器
    private ResourceIniter m_ResourceIniter;//资源初始化
    private VersionListProcessor m_VersionListProcessor;//版本资源列表处理器
    private ResourceChecker m_ResourceChecker;//资源更新检查器
    private ResourceUpdater m_ResourceUpdater;//资源更新器
    private ResourceLoader m_ResourceLoader;//资源加载器
    private IResourceHelper m_ResourceHelper;//资源辅助器
    
    //资源管理器轮询
    void Update()
	{
		m_ResourceUpdater.Update();
		m_ResourceLoader.Update();
	}
}

2.UGF.ResourceComponent

再来看下UGF框架中UGF.ResourceComponent类的简化代码结构

//Unity引擎对资源管理器的封装
public class UGF.ResourceComponent
{
    private ResourceManager m_ResourceManager;//资源总管理器
    private EventComponent m_EventComponent;//时间管理器
    
    private ResourceHelper m_CustomResourceHelper;//加载AssetBundle资源辅助器
    private LoadResourceAgentHelper m_CustomLoadResourceAgentHelper;//加载set资源代理辅助器
    
    private void Start()
    {
        //m_ResourceManager初始化
    }
    
    // 异步加载资源。
    public void LoadAsset(string assetName, LoadAssetCallbacks loadAssetCallbacks)
    // 卸载资源。
    public void UnloadAsset(object asset)
}

以上是对两个资源管理类进行了最大的精简,就是为了让大家理解,这俩类主要是干什么的。ResourceManager其实是真正的资源管理器,但是基本所有的逻辑都在子模块中,不管是资源初始化,资源更新还是资源加载,基本都在子模块中实现,可以理解为一种总分的结构,非常清晰,需要什么功能就去功能特定的模块去使用,但是各个模块都需要Mananger进行统筹管理。而ResourceComponent类主要是用于给ResourceManager进行初始化操作还有加载资源和卸载资源的入口。

ResourceComponent中有两个Helper,分别书ResourceHelper和LoadResourceAgentHelper,这俩类是框架给开发者留的一个后门,用于开发者扩展资源加载的方式的类。但是结构必须按照GF框架设定的走。


四.ResourceManager子模块

接下来是ResourceManager中的子模块的介绍

它们包括:

  • 1.ResourceIniter
  • 2.VersionListProcessor
  • 3.ResourceChecker
  • 4.ResourceUpdater
  • 5.ResourceLoader

下面对这5个子模块进行拆解分析

1.ResourceIniter

资源初始化器

//资源初始化器
private sealed class ResourceIniter
{
    private readonly ResourceManager m_ResourceManager;
	private readonly Dictionary<ResourceName, string> m_CachedFileSystemNames;
    
	public void InitResources(string currentVariant)
	{
        //加载GameFrameworkVersion.dat版本文件
		ResourceManager.ResourceHelper.LoadBytes(m_ReadOnlyPath, OnLoadSuccess, OnLoadFailure));
	}
	private void OnLoadSuccess(string fileUri, byte[] bytes, float duration, object userData)
    {
        //解析版本文件
        memoryStream = new MemoryStream(bytes, false);
        PackageVersionList versionList = ResourceManager.PackageVersionListSerializer.Deserialize(memoryStream);
        
        //读取版本中的文件列表
        PackageVersionList.Asset[] assets = versionList.GetAssets();
        PackageVersionList.Resource[] resources = versionList.GetResources();
        PackageVersionList.FileSystem[] fileSystems = versionList.GetFileSystems();
        PackageVersionList.ResourceGroup[] resourceGroups = versionList.GetResourceGroups();
    }
}

2.VersionListProcessor

资源版本列表处理器

// 版本资源列表处理器
private sealed class VersionListProcessor
{
    private readonly ResourceManager m_ResourceManager;
    private IDownloadManager m_DownloadManager;
    private int m_VersionListLength;
    private int m_VersionListHashCode;
    private int m_VersionListZipLength;
    private int m_VersionListZipHashCode;
    
    /// 检查版本资源列表。
    /// <param name="latestInternalResourceVersion">最新的内部资源版本号。</param>
    /// <returns>检查版本资源列表结果。</returns>
    public CheckVersionListResult CheckVersionList(int latestInternalResourceVersion)
    {
        if (internalResourceVersion != latestInternalResourceVersion)
        {
			return CheckVersionListResult.NeedUpdate;
		}
        return CheckVersionListResult.Updated;
    }
    /// 更新版本资源列表。
    /// <param name="versionListLength">版本资源列表大小。</param>
    /// <param name="versionListHashCode">版本资源列表哈希值。</param>
    /// <param name="versionListZipLength">版本资源列表压缩后大小。</param>
    /// <param name="versionListZipHashCode">版本资源列表压缩后哈希值。</param>
    public void UpdateVersionList(int versionListLength, int versionListHashCode, int versionListZipLength, int versionListZipHashCode)
    {
        VersionListProcessor versionListProcessor = e.UserData as VersionListProcessor;
        using (FileStream fileStream = new FileStream(e.DownloadPath, FileMode.Open, FileAccess.ReadWrite))
        {
            fileStream.Position = 0L;
            fileStream.SetLength(0L);
            //写入新版本资源列表到本地
            fileStream.Write(m_ResourceManager.m_DecompressCachedStream.GetBuffer(), 0, (int)m_ResourceManager.m_DecompressCachedStream.Length);
        }
    }
}

3.ResourceChecker

资源更新检查器

 /// <summary>
/// 资源检查器。检测是否需要更新,移动,删除
/// </summary>
private sealed partial class ResourceChecker
{
	private readonly ResourceManager m_ResourceManager;
	private readonly Dictionary<ResourceName, CheckInfo> m_CheckInfos;
    
	public void CheckResources(string currentVariant, bool ignoreOtherVariant)
	{
        //加载persistent沙盒目录下remote_version文件
 		m_ResourceManager.m_ResourceHelper.LoadBytes(Utility.Path.GetRemotePath(Path.Combine(m_ResourceManager.m_ReadWritePath, RemoteVersionListFileName)), new LoadBytesCallbacks(OnLoadUpdatableVersionListSuccess, OnLoadUpdatableVersionListFailure), null);
        //加载streaming目录下lcoal_version文件
        m_ResourceManager.m_ResourceHelper.LoadBytes(Utility.Path.GetRemotePath(Path.Combine(m_ResourceManager.m_ReadOnlyPath, LocalVersionListFileName)), new LoadBytesCallbacks(OnLoadReadOnlyVersionListSuccess, OnLoadReadOnlyVersionListFailure), null);

        //加载persistent沙盒目录下lcoal_version文件
        m_ResourceManager.m_ResourceHelper.LoadBytes(Utility.Path.GetRemotePath(Path.Combine(m_ResourceManager.m_ReadWritePath, LocalVersionListFileName)), new LoadBytesCallbacks(OnLoadReadWriteVersionListSuccess, OnLoadReadWriteVersionListFailure), null);
    }
    
    //检测所有文件状态
    private void RefreshCheckInfoStatus()
    {
      	if (!m_UpdatableVersionListReady || !m_ReadOnlyVersionListReady || !m_ReadWriteVersionListReady)
		{
			return;
  		}
        foreach (KeyValuePair<ResourceName, CheckInfo> checkInfo in m_CheckInfos)
        {
        	CheckInfo ci = checkInfo.Value;
            //刷新单个文件状态
        	ci.RefreshStatus(m_CurrentVariant, m_IgnoreOtherVariant);
            if (ci.Status == CheckInfo.CheckStatus.StorageInReadOnly)
			{}
            else if (ci.Status == CheckInfo.CheckStatus.StorageInReadWrite)
			{
             	if (ci.NeedMoveToDisk || ci.NeedMoveToFileSystem)
                {
                	if (ci.NeedMoveToDisk)
                    {}
					if (ci.NeedMoveToFileSystem)
					{}
                 }
            }
            else if (ci.Status == CheckInfo.CheckStatus.Update)
            {
                ResourceNeedUpdate(ci);//可更新通知
            }
            else if (ci.Status == CheckInfo.CheckStatus.Unavailable || ci.Status == CheckInfo.CheckStatus.Disuse)
            {}
            else
            {}
            
            if (ci.NeedRemove)
            {}
        }
        //检查完成
        ResourceCheckComplete(movedCount, removedCount, updateCount, updateTotalLength, updateTotalZipLength);
     }
}

ResourceNeedUpdate回调函数

private void OnCheckerResourceNeedUpdate(ResourceName resourceName, string fileSystemName, LoadType loadType, int length, int hashCode, int zipLength, int zipHashCode)
{
	m_ResourceUpdater.AddResourceUpdate(resourceName,fileSystemName,loadType,length,hashCode,zipLength,zipHashCode,path));
}

4.ResourceUpdater

资源更新器

/// <summary>
/// 资源更新器。
/// </summary>
private sealed partial class ResourceUpdater
{
	private readonly ResourceManager m_ResourceManager;
    private IDownloadManager m_DownloadManager;
	private readonly List<UpdateInfo> m_UpdateWaitingInfo;//更新列表
	private readonly Dictionary<ResourceName, UpdateInfo> m_UpdateCandidateInfo;//可更新列表
    
    //添加更新文件
    public void AddResourceUpdate(ResourceName resourceName, string fileSystemName, LoadType loadType)
	{
		m_UpdateCandidateInfo.Add(resourceName, new UpdateInfo(resourceName, fileSystemName, loadType));
	}
    
    //刷新更新列表
	public void UpdateResource(ResourceName resourceName)
	{
		UpdateInfo updateInfo = null;
		if (m_UpdateCandidateInfo.TryGetValue(resourceName, out updateInfo))
		{
			m_UpdateWaitingInfo.Add(updateInfo);
			m_UpdateCandidateInfo.Remove(resourceName);
		}
	}
    //轮询更新文件,添加到Download列表
    public void Update(float elapseSeconds, float realElapseSeconds)
    {
        if (m_UpdateWaitingInfo.Count > 0)
        {
            if (m_DownloadManager.FreeAgentCount > 0)
            {
                UpdateInfo updateInfo = m_UpdateWaitingInfo[0];
                m_UpdateWaitingInfo.RemoveAt(0);
                string resourceFullNameWithCrc32 = updateInfo.ResourceName.Variant != null ? Utility.Text.Format("{0}.{1}.{2:x8}.{3}", updateInfo.ResourceName.Name, updateInfo.ResourceName.Variant, updateInfo.HashCode, DefaultExtension) : Utility.Text.Format("{0}.{1:x8}.{2}", updateInfo.ResourceName.Name, updateInfo.HashCode, DefaultExtension);
                m_DownloadManager.AddDownload(updateInfo.ResourcePath, Utility.Path.GetRemotePath(Path.Combine(m_ResourceManager.m_UpdatePrefixUri, resourceFullNameWithCrc32)), updateInfo);
                m_UpdatingCount++;
            }
            return;
        }
    }
}

5.ResourceLoader

资源加载器

/// <summary>
/// 加载资源器。
/// </summary>
private sealed partial class ResourceLoader
{
    private readonly ResourceManager m_ResourceManager;
    private readonly TaskPool<LoadResourceTaskBase> m_TaskPool;//任务池
    private readonly Dictionary<object, int> m_AssetDependencyCount;//asset引用计数
    private readonly Dictionary<object, int> m_ResourceDependencyCount;//resource引用计数
    private readonly Dictionary<object, object> m_AssetToResourceMap;//资源和bundle映射map

	private const int CachedHashBytesLength = 4;
    private readonly byte[] m_CachedHashBytes;//加密解密bytes
    private IObjectPool<AssetObject> m_AssetPool;//assetObject对象池
    private IObjectPool<ResourceObject> m_ResourcePool;//resourceObject对象池

    /// <summary>
    /// 加载资源器轮询。
    /// </summary>
    /// <param name="elapseSeconds">逻辑流逝时间,以秒为单位。</param>
    /// <param name="realElapseSeconds">真实流逝时间,以秒为单位。</param>
    public void Update(float elapseSeconds, float realElapseSeconds)
    {
        m_TaskPool.Update(elapseSeconds, realElapseSeconds);
    }

    /// <summary>
    /// 异步加载资源。
    /// </summary>
    /// <param name="assetName">要加载资源的名称。</param>
    /// <param name="assetType">要加载资源的类型。</param>
    /// <param name="priority">加载资源的优先级。</param>
    /// <param name="loadAssetCallbacks">加载资源回调函数集。</param>
    /// <param name="userData">用户自定义数据。</param>
    public void LoadAsset(string assetName, Type assetType, int priority, LoadAssetCallbacks loadAssetCallbacks, object userData)
    {
        ResourceInfo resourceInfo = null;
        string[] dependencyAssetNames = null;
        CheckAsset(assetName, out resourceInfo, out dependencyAssetNames)
		//创建加载asset任务
        LoadAssetTask mainTask = LoadAssetTask.Create(assetName, assetType, priority, resourceInfo, dependencyAssetNames, loadAssetCallbacks, userData);
        foreach (string dependencyAssetName in dependencyAssetNames)
        {
            LoadDependencyAsset(dependencyAssetName, priority, mainTask, userData)
        }

        m_TaskPool.AddTask(mainTask);
    }
    /// <summary>
    /// 卸载资源。
    /// </summary>
    /// <param name="asset">要卸载的资源。</param>
    public void UnloadAsset(object asset)
    {
        m_AssetPool.Unspawn(asset);
    }

    //加载依赖
    private bool LoadDependencyAsset(string assetName, int priority, LoadResourceTaskBase mainTask, object userData)
    {
        ResourceInfo resourceInfo = null;
        string[] dependencyAssetNames = null;
        CheckAsset(assetName, out resourceInfo, out dependencyAssetNames)
		//创建加载依赖任务
        LoadDependencyAssetTask dependencyTask = LoadDependencyAssetTask.Create(assetName, priority, resourceInfo, dependencyAssetNames, mainTask, userData);
        foreach (string dependencyAssetName in dependencyAssetNames)
        {
            LoadDependencyAsset(dependencyAssetName, priority, dependencyTask, userData))
        }

        m_TaskPool.AddTask(dependencyTask);
        return true;
    }
}

UGF.ResourceComponent子模块

下面我们对ResourceComponent类进行更详细的描述

UGF.ResourceComponent结构基本和GF.ResourceManager结构基本一样。因为UGF.Component就是对GF.Manager管理器的一种封装。这些方法不再重复粘贴。除此之外ResourceComponent主要是在Start方法中对ResourceMananger进行初始化,包括事件绑定,子模块初始化和赋值。

如下:

private void Start()
{
    //获取base系统
    baseComponent = GameEntry.GetComponent<BaseComponent>();
    //获取事件系统
    m_EventComponent = GameEntry.GetComponent<EventComponent>();

    //编辑器模式使用EditorResourceManager
    m_EditorResourceMode = baseComponent.EditorResourceMode;
    m_ResourceManager = m_EditorResourceMode ? baseComponent.EditorResourceHelper : GameFrameworkEntry.GetModule<IResourceManager>();
    SetResourceMode(m_ResourceMode);

    //绑定事件
    m_ResourceManager.ResourceApplySuccess += OnResourceApplySuccess;
    m_ResourceManager.ResourceApplyFailure += OnResourceApplyFailure;
    m_ResourceManager.ResourceUpdateStart += OnResourceUpdateStart;
    m_ResourceManager.ResourceUpdateChanged += OnResourceUpdateChanged;
    m_ResourceManager.ResourceUpdateSuccess += OnResourceUpdateSuccess;
    m_ResourceManager.ResourceUpdateFailure += OnResourceUpdateFailure;

    //设置路径
    m_ResourceManager.SetReadOnlyPath(Application.streamingAssetsPath);
    m_ResourceManager.SetReadWritePath(Application.persistentDataPath);
   
    //设置辅助管理器
    m_ResourceManager.SetObjectPoolManager(GameFrameworkEntry.GetModule<IObjectPoolManager>());
    m_ResourceManager.SetFileSystemManager(GameFrameworkEntry.GetModule<IFileSystemManager>());
    m_ResourceManager.SetDownloadManager(GameFrameworkEntry.GetModule<IDownloadManager>());

    //设置资源自动释放的参数
    m_ResourceManager.AssetAutoReleaseInterval = m_AssetAutoReleaseInterval;
    m_ResourceManager.AssetCapacity = m_AssetCapacity;
    m_ResourceManager.AssetExpireTime = m_AssetExpireTime;
    m_ResourceManager.AssetPriority = m_AssetPriority;
    m_ResourceManager.ResourceAutoReleaseInterval = m_ResourceAutoReleaseInterval;
    m_ResourceManager.ResourceCapacity = m_ResourceCapacity;
    m_ResourceManager.ResourceExpireTime = m_ResourceExpireTime;
    m_ResourceManager.ResourcePriority = m_ResourcePriority;

    //设置ResourceHelper(AssetBundle加载器。如www或者unityWebRequest逻辑在helper中)
    m_ResourceHelper = Helper.CreateHelper(m_ResourceHelperTypeName, m_CustomResourceHelper);
    m_ResourceHelper.name = "Resource Helper";
    m_ResourceManager.SetResourceHelper(m_ResourceHelper);

    //添加ResourceAgentHelper(Asset加载器。如预制体,图片等)
    for (int i = 0; i < m_LoadResourceAgentHelperCount; i++)
    {
        LoadResourceAgentHelperBase loadResourceAgentHelper = Helper.CreateHelper(m_LoadResourceAgentHelperTypeName, m_CustomLoadResourceAgentHelper, index);
        m_ResourceManager.AddLoadResourceAgentHelper(loadResourceAgentHelper);
    }
}

其中两个类比较重要:DefaultResourceHelper和DefaultLoadResourceAgentHelper,这两个类都是辅助器类,但却是ResourceManager类底层框架往外延伸的两个非常重要的抓手,我们对GF的资源框架的自定义扩展,也仅限于此。

1.UGF.DefaultResourceHelper

ResourceHelper是AssetBundle的加载器,在内部调用。

namespace UnityGameFramework.Runtime
{
    /// <summary>
    /// 默认资源辅助器。
    /// </summary>
    public class DefaultResourceHelper : ResourceHelperBase
    {
        /// <summary>
        /// 直接从指定文件路径加载数据流。
        /// </summary>
        /// <param name="fileUri">文件路径。</param>
        /// <param name="loadBytesCallbacks">加载数据流回调函数集。</param>
        /// <param name="userData">用户自定义数据。</param>
        public override void LoadBytes(string fileUri, LoadBytesCallbacks loadBytesCallbacks, object userData)
        {
            StartCoroutine(LoadBytesCo(fileUri, loadBytesCallbacks, userData));
        }

        private IEnumerator LoadBytesCo(string fileUri, LoadBytesCallbacks loadBytesCallbacks, object userData)
        {
            bool isError = false;
            byte[] bytes = null;
            string errorMessage = null;
            DateTime startTime = DateTime.Now;

#if UNITY_5_4_OR_NEWER
            UnityWebRequest unityWebRequest = UnityWebRequest.Get(fileUri);
#if UNITY_2017_2_OR_NEWER
            yield return unityWebRequest.SendWebRequest();
#else
            yield return unityWebRequest.Send();
#endif

            isError = unityWebRequest.isNetworkError || unityWebRequest.isHttpError;
            bytes = unityWebRequest.downloadHandler.data;
            errorMessage = isError ? unityWebRequest.error : null;
            unityWebRequest.Dispose();
#else
            WWW www = new WWW(fileUri);
            yield return www;

            isError = !string.IsNullOrEmpty(www.error);
            bytes = www.bytes;
            errorMessage = www.error;
            www.Dispose();
#endif

            if (!isError)
            {
                float elapseSeconds = (float)(DateTime.Now - startTime).TotalSeconds;
                loadBytesCallbacks.LoadBytesSuccessCallback(fileUri, bytes, elapseSeconds, userData);
            }
            else if (loadBytesCallbacks.LoadBytesFailureCallback != null)
            {
                loadBytesCallbacks.LoadBytesFailureCallback(fileUri, errorMessage, userData);
            }
        }

        /// <summary>
        /// 释放资源。
        /// </summary>
        /// <param name="objectToRelease">要释放的资源。</param>
        public override void Release(object objectToRelease)
        {
            AssetBundle assetBundle = objectToRelease as AssetBundle;
            if (assetBundle != null)
            {
                assetBundle.Unload(true);
                return;
            }
        }
    }
}

2.UGF.DefaultLoadResourceAgentHelper基本结构

LoadResourceAgentHelper辅助器是加载单个Asset(预制体,图片等)的辅助器

如下:

namespace UnityGameFramework.Runtime
{
    /// <summary>
    /// 单个资源 加载辅助器。
    /// </summary>
    public class DefaultLoadResourceAgentHelper : LoadResourceAgentHelperBase, IDisposable
    {
        private string m_FileFullPath = null;
        private string m_FileName = null;
        private string m_BytesFullPath = null;
        private string m_AssetName = null;
        private float m_LastProgress = 0f;
        private bool m_Disposed = false;
        
#if UNITY_5_4_OR_NEWER
        private UnityWebRequest m_UnityWebRequest = null;
#else
        private WWW m_WWW = null;
#endif
        private AssetBundleCreateRequest m_FileAssetBundleCreateRequest = null;
        private AssetBundleCreateRequest m_BytesAssetBundleCreateRequest = null;
        private AssetBundleRequest m_AssetBundleRequest = null;
        private AsyncOperation m_AsyncOperation = null;

        /// <summary>
        /// 通过加载资源代理辅助器开始异步读取资源文件。
        /// </summary>
        /// <param name="fullPath">要加载资源的完整路径名。</param>
        public override void ReadFile(string fullPath)
        {
            m_FileFullPath = fullPath;
            m_FileAssetBundleCreateRequest = AssetBundle.LoadFromFileAsync(fullPath);
        }

        /// <summary>
        /// 通过加载资源代理辅助器开始异步读取资源二进制流。
        /// </summary>
        /// <param name="fullPath">要加载资源的完整路径名。</param>
        public override void ReadBytes(string fullPath)
        {
            m_BytesFullPath = fullPath;
#if UNITY_2017_2_OR_NEWER
            m_UnityWebRequest.SendWebRequest();
#else
            m_WWW = new WWW(Utility.Path.GetRemotePath(fullPath));
#endif
        }

        /// <summary>
        /// 通过加载资源代理辅助器开始异步将资源二进制流转换为加载对象。
        /// </summary>
        /// <param name="bytes">要加载资源的二进制流。</param>
        public override void ParseBytes(byte[] bytes)
        {
            m_BytesAssetBundleCreateRequest = AssetBundle.LoadFromMemoryAsync(bytes);
        }

        /// <summary>
        /// 通过加载资源代理辅助器开始异步加载资源。
        /// </summary>
        /// <param name="resource">资源。</param>
        /// <param name="assetName">要加载的资源名称。</param>
        /// <param name="assetType">要加载资源的类型。</param>
        /// <param name="isScene">要加载的资源是否是场景。</param>
        public override void LoadAsset(object resource, string assetName, Type assetType, bool isScene)
        {
            AssetBundle assetBundle = resource as AssetBundle;
            m_AssetName = assetName;
            m_AssetBundleRequest = assetBundle.LoadAssetAsync(assetName, assetType);
        }

        private void Update()
        {
#if UNITY_5_4_OR_NEWER
            UpdateUnityWebRequest();
#else
            UpdateWWW();
#endif
            UpdateFileAssetBundleCreateRequest();
            UpdateBytesAssetBundleCreateRequest();
            UpdateAssetBundleRequest();
            UpdateAsyncOperation();
        }

#if UNITY_5_4_OR_NEWER
        private void UpdateUnityWebRequest(){}
#else
        private void UpdateWWW(){}
#endif
        private void UpdateFileAssetBundleCreateRequest(){}

        private void UpdateBytesAssetBundleCreateRequest(){}

        private void UpdateAssetBundleRequest(){}

        private void UpdateAsyncOperation(){}
    }
}

五.加载一个飞船的全部流程

  • 1.Procedure从Launch->Splash游戏启动->Preload->InitResource加载游戏配置资源->Menu展示主页菜单->Main加载SurvivalGame进入游戏

  • 2.SurvivalGame中,每隔1s,创建一个小行星Asteroid

//创建一个小行星游戏数据类
var asteroidData = new AsteroidData(GameEntry.Entity.GenerateSerialId(), 60000 + Utility.Random.GetRandom(dtAsteroid.Count))
//调用EntityManager的创建小行星方法
GameEntry.Entity.ShowAsteroid(asteroidData)
  • 3.进入EntityComponnet.ShowEntity方法
  • 4.进入EntityManager.ShowEntity方法,
//从实体组中获取一个InstanceObject,其实就是对象池
EntityInstanceObject entityInstanceObject = entityGroup.SpawnEntityInstanceObject(entityAssetName);
if (entityInstanceObject == null)//如果获取不到,说明首次加载,就去资源管理器中LoadAsset
{
    int serialId = ++m_Serial;
    m_EntitiesBeingLoaded.Add(entityId, serialId);
    
    //异步加载小行星的预制体
    m_ResourceManager.LoadAsset(entityAssetName, priority, m_LoadAssetCallbacks, ShowEntityInfo.Create(serialId, entityId, entityGroup, userData));
    return;
}

//实体组,对象池中获取到小行星游戏对象,直接展示小行星
InternalShowEntity(entityId, entityAssetName, entityGroup, entityInstanceObject.Target, false, 0f, userData);
  • 5.资源管理器->异步加载,ResourceManager.LoadAsset
  • 6.资源加载器->异步加载,ResourceLoader.LoadAsset
/// <summary>
/// 异步加载资源。
/// </summary>
/// <param name="assetName">要加载资源的名称。</param>
/// <param name="assetType">要加载资源的类型。</param>
/// <param name="priority">加载资源的优先级。</param>
/// <param name="loadAssetCallbacks">加载资源回调函数集。</param>
/// <param name="userData">用户自定义数据。</param>
public void LoadAsset(string assetName, Type assetType, int priority, LoadAssetCallbacks loadAssetCallbacks, object userData)
{
    ResourceInfo resourceInfo = null;
    string[] dependencyAssetNames = null;

    //从映射文件,获取asset的bundle信息和依赖bundle信息
    CheckAsset(assetName, out resourceInfo, out dependencyAssetNames)

    //创建一个LoadAssetTask任务
    LoadAssetTask mainTask = LoadAssetTask.Create(assetName, assetType, priority, resourceInfo, dependencyAssetNames, loadAssetCallbacks, userData);

    //加载资源bundle的依赖bundle
    foreach (string dependencyAssetName in dependencyAssetNames)
    {
        LoadDependencyAsset(dependencyAssetName, priority, mainTask, userData)
    }

    //添加到任务池
    m_TaskPool.AddTask(mainTask);
}
  • 7.加载任务添加到任务池后,TaskPool会在Update中进行轮询ProcessRunningTasks和ProcessWaitingTasks方法
/// 任务池轮询。
public void Update(float elapseSeconds, float realElapseSeconds)
{
	ProcessRunningTasks(elapseSeconds, realElapseSeconds);
	ProcessWaitingTasks(elapseSeconds, realElapseSeconds);
}
  • 8.ProcessWaitingTasks方法会把LoadAssetTask转化为一个ITaskAgent:LoadResourceAgent,并调用Start方法,开始执行一个任务
Task task = m_WaitingTasks.First;
ITaskAgent<T> agent = m_FreeAgents.Pop();
agent.Start(task);
  • 9.LoadResourceAgent类中的Start方法执行,如果是
/// <summary>
/// 开始处理加载资源任务。
/// </summary>
/// <param name="task">要处理的加载资源任务。</param>
/// <returns>开始处理任务的状态。</returns>
public StartTaskStatus Start(LoadResourceTaskBase task)
{
    //**逻辑太多,直接伪代码**
    
	//1.如果assetBundle没有加载就执行m_Helper.ReadFile或者m_Helper.ReadBytes,其实就是AssetBundle.LoadFromFileAsync;异步加载完后回调OnLoadResourceAgentHelperReadFileComplete,再调用OnResourceObjectReady
    //2.如果asstBundle已经加载,Asset没有加载,就执行OnResourceObjectReady
    //3.如果Asset已经加载,就直接执行OnAssetObjectReady,返回Done
}
  • 10.OnResourceObjectReady函数,执行m_Helper.LoadMain()后由执行m_Helper.LoadAsset方法。注:LoadResourceAgent类在ResourceManager启动的时候绑定过一个m_Helper = DefaultLoadResourceAgentHelper,即资源加载辅助器
/// <summary>
/// 通过加载资源代理辅助器开始异步加载资源。
/// </summary>
/// <param name="resource">资源。</param>
/// <param name="assetName">要加载的资源名称。</param>
/// <param name="assetType">要加载资源的类型。</param>
/// <param name="isScene">要加载的资源是否是场景。</param>
public override void LoadAsset(object resource, string assetName, Type assetType, bool isScene)
{
    AssetBundle assetBundle = resource as AssetBundle;
    m_AssetBundleRequest = assetBundle.LoadAssetAsync(assetName, assetType);
}

//异步加载完毕的回调
private void UpdateAssetBundleRequest()
{
	if (m_AssetBundleRequest.isDone)
	{
		m_LoadResourceAgentHelperLoadCompleteEventHandler(this,args)
	}
}
  • 11.回调绑定的LoadResourceAgent的OnLoadResourceAgentHelperLoadComplete方法,一波判空后再执行OnAssetObjectReady方法

  • 12.LoadResourceAgent.OnAssetObjectReady方法中通知m_Task即LoadAssetTask的OnLoadAssetSuccess方法,并设置Done=true。回调函数中执行m_LoadAssetCallbacks.LoadAssetSuccessCallback,再次回调到m_ResourceManager.LoadAssetSuccessCallback方法中。资源加载完成。

  • 13.异步加载成功,获得小行星预制体

//上一步,异步加载预制体成功回调。entityAsset就是加载的预制体
private void LoadAssetSuccessCallback(string entityAssetName,Object entityAsset, float duration, ShowEntityInfo userData)
{
    //创建实体游戏对象
    EntityInstanceObject entityInstanceObject = EntityInstanceObject.Create(entityAssetName, entityAsset, m_EntityHelper.InstantiateEntity(entityAsset), m_EntityHelper);
    
    //把实体游戏对象加入到实体组中。后续对象池可以回收和复用
    showEntityInfo.EntityGroup.RegisterEntityInstanceObject(entityInstanceObject, true);
	
    //展示小行星
    InternalShowEntity(showEntityInfo.EntityId, entityAssetName, showEntityInfo.EntityGroup, entityInstanceObject.Target, true, duration, showEntityInfo.UserData);
}

终于,终于,终于,一个小行星加载完了,好辛苦。当然,所有的异步逻辑都是隐藏在框架内部的,我们只需要调用LoadAsset就行了。

六.总结

以上是GameFramework中最核心的资源管理器框架的剖析,GF实际就是一个总分总的一个框架结构,GF.DLL框架是总框架,里面定义了所有模块的接口,并且在ResourceMananger中把资源加载,卸载等接口等都实现了,但是,大量的资源加载,更新等逻辑都是在多个子模块中实现的,然后ResourceMananger的方法中只是调用子模块的实现。这就是总-分结构了。

本来我们可以直接去使用ResourceMananger进行开发,但是作者却对GF类库进行了密封,防止我们对框架基础结构的修改,作者通过ResourceComponent进行总封装,公开需要开发者使用的方法,隐藏不需要开发者使用的方法。简化ResourceManager框架的使用。这就是分-总结构了
所以整体就是总分总结构,

其实GF框架整体思路还是非常清晰的。只不过里面涉及太多的功能,有些模块或者功能如果没用过,首次接触会不太好理解,所以今天我们就对整个框架结构进行梳理,然后后续文章中,会对细节功能单个梳理。希望这篇文章对你有所帮助。

相关推荐
©️2020 CSDN 皮肤主题: 游动-白 设计师:白松林 返回首页
实付 19.90元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值