【解决方案之道(二)】场景资源管理
一、项目需求
通常来说,场景资源的项目需求本身就来自于程序部门,并且资源管理也是项目开发最基础的活路,所以我们这个系列,就从场景资源管理开始。
首先,我们会面临一些常规的技术需求,以我自己使用Unity为例,通常会有下面几个技术点:
1、资源导入,资源分类与目录结构
2、资源使用,供其他系统调用
有了这两个技术点后,我们就可以给出一个简单的资源管理器,但此时,富有经验的老司机还会提出一些其他需求。
3、资源打包,压缩加密
4、资源更新,线上扩展内容
5、场景资源预加载,资源合理投放
6、场景资源静态与动态释放,内存管理
这些需求通常是一步一步去实现出来的,但一个老司机如果能在项目一开始就把这些问题考虑进去,那么项目就会因为这样的壮举得到很好的成本节约。
二、初步技术方案
老船长此时出差了,暂时由老司机来执行这个过程,于是,它架构出的了这么一个方案。
A、资源导入与资源使用
【技术点1:资源表的生成与使用】(详见篇尾)
资源的导入与使用,并没有什么复杂的技术点,当我们考虑清楚流程后,就可以马上着手开发。但如果我们使用解决方案的思维来看待这个问题,就会发现这样的设计会给我们的实际工作造成很多的麻烦。
在上面流程图所示范的过程中,资源表的生成是在资源已经放入到目录后才进行的。这样做的好处就是节省了资源表配置的工作量,属于典型的“程序自动化处理”工作,我们通常喜欢做这样的工作。
但从整个流程来看,资源的存放是人为发起的,什么资源起什么名字,放到什么目录,实际上是有约定后,才会进行的。也就是说,还有另外一张资源表,是资源开发人员去肉眼观看的。之后,又会又开发人员人工地把资源放入到项目目录中。
漫长的开发过程中,要保证这个人工过程不出错,几乎不可能,甚至经常因为这些错误,会引发效率降低。这就是解决方案中的预估问题(一):如何避免流程中的人工错误。
要解决这个问题也是非常地简单,我们只需把“资源表自动生成”这个步骤,变为较为麻烦的人工生成就好。因为资源的生产流程中,本身就已经有了资源表起名,目录,类型的分配过程,让大家遵循着我们的规范来走一遍流程,既不会给别人增加更多的工作,也能够在资源导入阶段让资源导入的人自己监测出一些常规错误。
下面这张图是优化后的结构:
我们可以看到,原本需要2次填写的资源表在现在的流程中只需创建一次了,而很容易出错的人工行为“资源导入”环节也因为有了资源表来检测,在环节内部资源开发者就能够得出自己的失误并修正。
相比起之前的方案,我们预估了错误环节,并修改了设计,这就是解决方案的思维下的一小个环节。
详细的资源导入与使用我们会在【技术点】文章中讲解,现在让我们继续其他部分的解决方案设计。
B、资源打包与资源更新
通常情况下,我们会直接的得到这么一个简单的流程:
【技术点2:资源打包与更新】(详见篇尾)
这是一个最初步的方案,如果此时老司机出马,根据以往项目经验,他就会有一些自己的想法:1、如何划分资源表,多少资源打成一个包。
2、每次更新的打包资源,是人工打包,还是自动打包。
3、打包后,客户端是被迫更新,还是下一次运行时更新,或是在线更新。
当老司机对上面进行了思考并给出相应的对策后,从【技术点】的角度考虑的事情也就差不多了。
此时,老船长回来了,因为老船长不仅拥有丰富的经验,还有预估问题的能力,于是它针对资源更新这一块,提出了自己眼中所看到的问题:
1、 玩家更新时切换到后台了,玩家更新时断网了。
2、 某个服务器作为活动服,需要单独更新某些特定资源,但其他服务器不更新。
3、 某配置资源有严重错误,需要客户端强制退出下载最新的配置资源。
现在,我们的架构中不仅要解决老司机的技术问题,还要解决老船长的人性化问题。所以,我们就会得到一个相对复杂的设计图。
这看起来复杂了许多,里面有几个关键点,我都将它们做了记号,接下来我对它们进行一番介绍:
1、这里的资源表是最初的资源表,在资源导入阶段它由人工填写一部分内容,作为检验依据,而在资源打包节点,我们会在打包时加入每份资源的MD5值,这样就可以作为之后更新检测的凭据。
2、通用更新表,将1上传至服务器后,就成了通用更新表,当客户端进入游戏后,就通过服务器下发的通用更新表检索当前是否需要更新。
3、特殊更新表,则是针对老船长提出特殊服特殊资源的问题,当服务器检测到是特殊服在申请资源表,就传给特殊服的客户端属于它自己的更新表。因为服务器是在游戏加载完毕之后才选择服务器的,所以特殊更新表只负责少量的配置信息,在每次进入游戏时才更新。
4、本地资源表就是1表,不过是存在客户端内的,并且每更新完一个资源后,就会改变这个表内的MD5值,保证玩家在断线后,依然可以通过对比本地资源表实现断点重连。
5、这是一个人性化的服务,我们需要给游戏的其他系统提供此时我们的网络状况。当玩家更新时,如果他知道他的网络状况,下载速度,剩余进度,他就不会显得不耐烦。而当玩家处于非Wifi网络时,我们也需要提醒他是否继续下载,所以需要提供这些功能给其他系统。
有了这一个过程后,我们开始先资源打包与更新的【技术点】设计,虽然Gad里有一些相关的文章,但在文章编写的时候看起来它们并不能满足我们的设计需求,所以这一部分的【技术点】也会我们自己来写。
C、资源预加载
通常在游戏中,每一个关卡都有许多的资源,一份资源要从硬盘里读到内存,是需要消耗IO的,为了避免一个资源投放到场景中时候“卡一下”,所以大家都会选择资源预加载。
资源预加载本身并没有太多技术点,我们依然通过流程图将它构建出来。
通常当我们需要做资源预加载的功能时,我们本能的做出这样的流程图,简明的告诉我们自己需要做一些什么功能。
但对于一名有经验的老司机来说,项目中很多的细节问题,都可以让这张图变得更加复杂。以我使用的Unity 4 为例子。
1、假设某个资源总共有10份,但每次只出现N份。如果我们预加载10份进来,那么占用的内存就会很多。
2、 进入场景的时候马上加载所有资源,玩家就需要等待很久。
3、 一个场景如果很长很大,不可能一直加载资源部释放,需要分批加载分批释放。
当想出这些问题,就算老司机也哇一下就哭出来了,不过再怎么苦恼,也得含着泪把它们定。
要满足上面功能,我们就需要从资源表里获取一些信息。
1、每份资源最大同时出现数量。
2、 每份资源的开始节点与结束节点。
在我自己的项目中,虽然所有的怪物与事件都有它们的唯一ID,但在关卡中它们都是离散型的,所以无法直接通过分析关卡配置文件得出这些信息。
于是我想到了一个办法,第一次进入场景时候,会加载所有的游戏资源,当关卡被运行后,就会实时的统计我所需要的信息,当关卡结束后,这些信息也就能够满足资源表对信息的需求。
而对于玩家来说,他们所拿到的客户端一定是被我们“训练过”的,所以自然就感受不到第一次“漫长”的加载。
当资源表能够提供这两个信息后,我们也就能够画出老司机眼中的流程图。
【技术点3:资源加载、释放与池】(详细见篇尾)
当老司机画完这样的架构图后,心里总算松了一口气,但多看两眼后,他又会心里一紧,毕竟,看图都那么长的程序,实现起来,也需要一定的时间。
不过还好,总算有清晰的流程图,老司机使用照着实现功能就好。
三、结束语
整个场景资源管理,是游戏开发的重中之重,把资源的问题处理好,可以让游戏开发的整个过程省不少心,而预加载与池的问题处理好,也能让项目在后期优化时轻松不少。
在下一篇解决方案开始之前,我会先针对该篇的每一个技术点进行补充,如果有同学又兴趣补充,也可以发帖参与,顺便告诉我一下,避免重复造轮子。
当经历一段时间,大部分游戏核心模块都有了解决方案的文章之后,我们也就完成了一次游戏的架构过程。
另外,如果对本篇有什么想法的,都可以留言,解决方案是大师之路,脱离了实际项目的后期维护必然会有许多的问题所在,大家发现后积极留言,可以避免文章内容误导后人,也算功能无量。
四、技术点
【技术点1】【解决方案之道(三)】资源导入与审查
【技术点】Unity的热更新sLua
【技术点2】资源打包与更新:Gad暂无技术点文章,作者后续补上。
【技术点】Unity资源池与动态加载释放