《炉石传说》架构设计赏析(4):Asset管理

欢迎转载,请注明作者【燕良@游戏开发】及原文地址:http://blog.csdn.net/neil3d/article/details/39580197

另外,欢迎大家来我的QQ群交流各种游戏引擎相关的技术:游戏引擎能吃吗(264656505)

话说,经过这段时间的学习和摸索,对于Unity3D的开发思路已经基本清晰了。唯独还剩下一个AssetBundle机制还没有搞透,这个涉及到前期项目的资源规划、资源管理代码的写法,以及自动更新机制的实现。

所以,还是想先把游戏逻辑的进一步分析押后,先来看一下《炉石传说》Asset管理。必须得说一下的是,目前分析都是PC版的程序集,对于移动端不一定完全合适,且当做一个案例分析吧。

本文主要讲述《炉石传说》的AssetBundle的管理机制。它的机制比较简单清晰,中规中矩,中间的分析过程就不讲了,直接展现其架构设计和代码逻辑组织。先从Asset管理相关的类讲起。

class Asset :资源信息描述

Asset类,并不管理直接的资源对象,而是保存的一个Asset相关的信息,具体请看上图。

另外,它还有一个“paths”变量,这是一个Dictionary,key是AssetFamily枚举,value是Assetbundle的路径和资源路径。下面的AssetFamily一节详细解释。

enum AssetFamily - 资源分类

如上图所示:

  • 炉石根据资源的不同类型进行分别的AssetBundle打包,一类资源对应一个或者多个资源包;(一类资源分多个包的规则不得而知);
  • 有的资源包真的本地化单独打包,例如“fonts0.unity3d”==》“fontszhCN0.unity3d”;
  • 在程序中,资源包的分类对应枚举类型“AssetFamily”;
  • 资源包的具体路径信息,存储在Asset.paths,这是一个静态变量;在初始化时,手动填写必要的信息,类似这样:
        Dictionary<AssetFamily, AssetFamilyPathInfo> dictionary = new Dictionary<AssetFamily, AssetFamilyPathInfo>();
        AssetFamilyPathInfo info = new AssetFamilyPathInfo {
            format = "Data/Actors/{0}.unity3d",
            sourceDir = "Assets/Game/Actors"
        };
        info.exts = new string[] { "prefab" };
        dictionary.Add(AssetFamily.Actor, info);
    
  • 另外,还有一个class AssetBundleInfo是记录了每种AssetBundle对应的主文件名,以及包文件的个数、对应的对象类型等信息;详见下图:

class AssetLoader :资源加载

游戏运行时需要加载各种资源,基本上都是通过AssetLoader(也有个别情况适用了Resources.Load())。接下来我们就重点看一下AssetLoader的实现思路。

AssetLoader对上层提供资源对象加载接口,对于每种类型的资源都提供一组函数,例如LoadCardPrefab,LoadActor等等。对于对象加载完成、加载进度等提供回调函数。这些函数只是一些简单的包装,其内部都调用到LoadCachedGameObject()或LoadCachedObject()这两个核心函数。

从这两个函数的流程可以看到,资源加载使用到了Cache机制:

  • 首先从AssetCache中查找,如果找到了,则更新Cache项的时间戳,并调用回调;
  • 如果没有找到,则向AssetCache添加一个Request,然后启动Coroutine:CreateCachedAsset(),它的调用步骤是:
    • 调用AssetCache.StartLoading();
    • 启动Coroutine:CreateCachedAsset_FromBundle<RequestType>():
      • 使用AssetLoader.GetBundleForAsset()找到资源所属的AssetBundle;
      • 调用AssetBundle.LoadAsync()来真正加载资源;
      • 在加载的过程中,根据处理的结果调用:AssetCache.CacheRequest的OnLoadFailed()、OnLoadSucceeded()、OnProgressUpdate()等函数;
    • 在AssetCache查找此资源,如果找到了,则加载成功,调用回调函数;

      调用AssetCache.StopLoading();

我们都知道在开发过程中,不能使用AssetBundle(每次启动都要打包,肯定收不了)。怀疑它的Editor模式相关的代码是用预编译宏处理来实现的,所以未出现在发布出来的程序集当中,类似这样:

#if UNITY_EDITOR
        Obj = Resources.LoadAssetAtPath(assetPath, typeof(T));
        if (Obj == null)
            Debug.LogError ("Asset not found at path: " + assetPath);
        yield break;
#else

class AssetCache :资源的Cache机制

前面在AssetLoader一节我们已经讲到了AssetCache机制,这里再做一个详细的阐述。

前面我们已经讲到:

  • AssetCache中的资源项的时间戳,由AssetLoader在资源加载请求时维护;
  • AssetCache主要负责管理Cache数据,而真正的资源加载动作还是在AssetLoader中执行;

AssetCache的资源淘汰主要由外部的各个模块根据自己认为需要的时机去调用,例如:

  • SceneMgr.ClearCachesAndFreeMemory()
  • LoadingScreen.ClearAssets()
  • SoundMgr.UnloadSoundBundle()
  • 等等

另外,程序启动时会自动更新资源包(在Login.OnAssetsVersion()中启动),主要是通过UpdateManager和Downloader两个类来处理。

OK,总结一下炉石的资源管理机制:

  • 对游戏资源按照类型分包,每一类资源包可以有多个;
  • 在游戏运行时使用Cache机制;
时间: 2024-10-12 22:30:28

《炉石传说》架构设计赏析(4):Asset管理的相关文章

炉石传说 C# 设计文档(序)

经过3个月的开发,有很多感触. 以前一直以为技术是开发成败的第一因素,现在发现,等到你代码写的时间够长,经验够丰富,什么功能都能随手完成,对于业务的分析能力变成了第一位. 炉石山寨版的BS版本用到的HTML5的SVG,我看了一个下午的教程,借鉴以前GUI+和HTML的经验,很快就能写点东西出来了. WebSocket,Github上找了一个开源的C#项目,通讯这块也是几个小时就搞定了.Javascript不是很熟悉,当时闭包这样的一些概念也算听说过,Js也是无障碍就写成了. 整个项目的技术壁垒其

《炉石传说》架构设计赏析(1):游戏启动流程

前些天看新闻,Unity Awards两项大奖颁给了暴雪的<炉石传说>,这真是对Unity一个再好不过的宣传了--你看,暴雪都开始用Unity了.大家都知道,目前Unity发布的游戏大多都没有对程序集进行混淆.加密,所以作为一个炉石的玩家&Unity的初学者,自然不能错过这个机会.让我们好好看一下暴雪的代码吧. 炉石传说的游戏内容的非常丰富多彩,所以我花了一些时间分析了其程序集,将一些设计思路记录下来,与大家分析.欢迎各路高手拍砖,欢迎转载,请注明出处:燕良@游戏开发,http://b

《炉石传说》架构设计赏析(6):卡牌&amp;技能数据的运行时组织

前一篇文章我们看到了<炉石传说>的核心卡牌数据的存储,今天我们继续探索卡牌&技能. 主要的类 通过之前的分析,卡牌&技能涉及到几个类体系:Entity,Actor,Card,Spell,令人十分困惑,特别是前两者.在这里先略带武断的说一下这几个类的基本定位: Entity主要用来做网络数据同步用的: Actor主要处理客户端的渲染对象的控制,作为Component挂载在资源对象上: Spell是技能Prefab挂载的脚本: Card是卡牌Prefab挂载的脚本,在运行时处于中心

《炉石传说》架构设计赏析(2):Scene管理

上篇文章我们分析到SceneMgr处理了Scene的加载工作,今天我们主要分析一下炉石这款游戏中一共有哪些Scene,他们各自负责什么,以及它内部的逻辑.UI的处理方式. 在正式开始之前,我来对前文中提到的Scene切换再做一些补充分析.前文中我们看到SceneMgr是调用了" Application.LoadLevelAdditiveAsync(this.sceneName);",那内存中的东西岂不是越搞越多吗?我们再仔细看一下SceneMgr:SwitchMode()函数,它是一个

《炉石传说》架构设计赏析(3):Gameplay初探

经过前面两篇文章的分析,我们对炉石的代码已经不陌生了,接下来我初步尝试分析其游戏逻辑代码. 欢迎转载,请注明作者[燕良@游戏开发]及原文地址:http://blog.csdn.net/neil3d/article/details/39453291 经过前面的分析,我们已经找到了两个关键的类Gameplay和GameState(当然还有我最感兴趣的Spell和SpellController,这两个还要在后面分析). 首先我们看一下Gameplay这个类的Awake方法,它完成的主要工作是: 调用"

《炉石传说》架构设计赏析(7):使用Google.ProtocolBuffers处理网络消息

这段时间琢磨了一下Unity3D网络游戏开发中的网络消息处理.网络游戏的服务端一般都是自主开发的,所以对应的网络消息处理也要自己开发.客户端/服务端之间的消息传到目前使用JSON和Google.ProtocolBuffers是两种常见的做法.打开炉石的代码看了看它的处理方式,感觉代码写的还是很好的,把它的思路分析一下,与大家分享. 整体机制描述 我们想要达到的目标大概是这样的: 有N个网络消息,每个消息对应一个Proto中的message描述: 每个消息对应一个数字ID: 底层在收到消息是,将其

《炉石传说》架构设计赏析(5):卡牌&amp;技能的静态数据组织

经过前面几次的尝试,我们对炉石的代码已经不陌生了.除了网络机制还没有了解以外,本机的逻辑已经比较熟悉了. 接下来继续向暴雪最NB的技能系统进发,我们的目标是: 分析技能的静态数据描述: 分析技能的运行时数据.逻辑组织: 这篇笔记主要记录对其分析静态数据. 静态数据组织 卡牌数据 卡牌的基本数据对于的AssetFamily为:AssetFamily.CardXML: 数据对于的资源包为"cardxml0.unity3d": 资源包中的资源类型为:TextAsset: 资源加载使用的接口为

tornado项目之基于领域驱动模型架构设计的京东用户管理后台

本博文将一步步揭秘京东等大型网站的领域驱动模型,致力于让读者完全掌握这种网络架构中的“高富帅”. 一.预备知识: 1.接口: python中并没有类似java等其它语言中的接口类型,但是python中有抽象类和抽象方法.如果一个抽象类有抽象方法,那么继承它的子类必须实现抽象类的所有方法,因此,我们基于python的抽象类和抽象方法实现接口功能. 示例代码: from abc import ABCMeta from abc import abstractmethod #导入抽象方法 class F

架构设计:系统间通信(9)——通信管理与RMI 下篇

接上文<架构设计:系统间通信(8)--通信管理与RMI 上篇>.之前说过,JDK中的RMI框架在JDK1.1.JDK1.2.JDK1.5.JDK1.6+几个版本中做了较大的调整.以下我们讨论的RMI工作原理都是基于JDK1.6+版本的. 3.JAVA RMI 工作原理 通过上面的两组代码,我们大概知道了RMI框架是如何使用的.下面我们来讲解一下RMI的基本原理.本人翻阅网上的众多RMI资料基本上代码都是一大抄(甚至变量名.语法错误都一样),还有很多资料存在误导读者的情况.下图描述了整个RMI框