游戏系统开发笔记(三)——通用代码库

 墨水比较有限,工作时基本也都是着眼小处,除了工作内容涉及过的几个模块,其余的暂时并未多作关注,所以基本上还只是停留在感性认识上。不过我倒觉得这是难免,毕竟游戏产品放到整个软件行业来说也是个较复杂的东西,需要花很多时间才能把其间各种技术融会贯通为我所用,但等到了那个时候恐怕已经忙得未必会有时间详细的写东西了吧。所以这篇文章也是基本上限制在自己的这层认识水平上去写,还是那句话,过段时间回顾的时候再来修正之前写的一些问题,再去完善一些当时没能说的清楚的东西。虽然废话写多了点,还是觉得万一有人对这些日志抱有兴趣,希望他能了解上述事实,不要浪费时间才好 :-)

-------------------------------------------------------------------------------------------

通用代码库

        这个时代“站在巨人的肩膀上”已经是一件无限趋近于迫不得已的事情了,尤其是想做一些比较“大”的事情的时候。拿游戏来说,这已经是不再是过去那个靠程序猿绘制简单点线面做一个tic tac toe那样的游戏就能赢得玩家欢心的时代了,就算是学生习作,完成像俄罗斯方块、贪吃蛇那样的游戏也只能为自己带来...一点点成就感。人们对虚拟现实的要求越来越高,对交互性要求越来越强,对应的,程序也就要做的越来越复杂。

        过去我总想完全的把握一件事情,知道它的前因后果,了解所有的枝节脉络。但这种求知欲失控后却完全起到了反效果,导致迟迟不能真正的开始做自己想做的事情,而是不停的在一个又一个的概念上、技术上打转,最后也就“知难而退”,没有下文了。

        所以这个部分放到最前面来讲,是因为我觉得它是进行商业游戏开发的一个重要前提,准备一套代码库等于有了虚拟意义上的工作室——就跟现实世界的工作室一样,我们需要这样一个环境,通常情况下不能指望一个人能够包揽所有的东西。而且要像写大型程序一样,分层次的去考虑现实中的问题,专注的着眼于自己的目标去工作。比如你想做一个游戏,那就先不要过度纠结于游戏引擎的实现。

 

        通用代码库需要提供几个东西,一是功能上的便利,能够直接提供高级接口供开发者使用,防止前期花费过多精力造轮子;二是稳定性,因为拿来作为项目的基石,所以尽量找具有工业强度的代码工具来,否则以后要做好三天两头打点滴住院的心理准备;三是较好的通用性,可以应用到不同的需求的工作中。

通常来说,一个游戏项目(包括客户端、服务端以及外围工具)可能要用到以下工具:

1、日志模块

2、网络模块

3、进程通信模块

4、内存分配器

5、数据库模块

6、常用数据结构和算法库

7、常用设计模式模板

8、线程库

9、脚本库

10、常用工具函数库

11、堆栈信息抓取工具

12、内存泄漏检查工具

13、高效计时器

        暂时能想到的是以上这些,其中有一些有比较知名的库,有些我则完全没有听说过有类似的成熟代码库,不过功能上论,大致是要包含上述这些东西。有比较成熟的代码库尽量拿来用(使用开源项目注意开源协议,一些开源协议的项目直接拖进商业项目中来是不道德的,还可能会有法律风险),因为通常不管是效率还是稳定性都会优于自己去实现一个。一些工具虽然可能没有现成的代码库,但是建议自己先去实现出来,尤其是内存泄漏、抓堆栈这种,要在早期做出来,可以为以后堆代码清除不少障碍。

        还有些东西没列比如性能分析工具,这种可能就得根据具体项目自己来实现监测手段了,性能分析基准也是要由自己来定,比较大的项目分析性能通常不是用个第三方分析软件就能搞定的(因为和操作行为有关)。另外就是具体一些工具就不列举,因为自己工作没插手这块,私底下也没亲自尝试过,都是道听途说一点,也不好瞎推荐。

        各个模块的具体介绍以后再陆续展开。

本文转自:http://blog.csdn.net/mooke/article/details/8680020


游戏系统开发笔记(五)——服务端系统分层

本来很想按顺序写下来,到了第五篇是打算先写架构的,无奈这东西暂时没办法弄的比较通透,拖了很久也还是觉得写起来有困难。有一个客观因素是这阵子有点忙,很多东西要做,也没办法留出许多时间用来学习。

        还是先写已经有点概念的东西吧....

 

关于架构:

        一个复杂系统开始施工前首先要设计其架构,但这个暂时没能力说太细,所以只简单一提。

 

        什么是架构呢?

        我理解的架构就是一组能够描述服务端功能的概念模型,它能把复杂的问题使用相同统一的方式进行描述,或者说可以把一个复杂的现实问题转化为相对可控的工程问题。它甚至不需要任何代码,不需要复杂的UML图,不管通过任何方式只要能够简化并描述问题。而且并不是只有最上层的架构才叫架构,我觉得它应该是一棵决策树,每一个节点的架构设计都可以叫做解决该问题的架构。

        对于一套完整的服务端架构来说,应该包含软件架构和硬件架构。有时候一些软件上极难解决的问题却可以通过硬件方案轻松解决,所以对于网络游戏这种相对大型的软件来说,硬件方案设计也是不可缺少的。

        对架构的要求包含两个方面,第一个是硬性要求,要求能够在这套架构的基础上完成预期的游戏功能并具有一定程度的可用性;第二个是优化要求,条件允许的情况下尽可能的使得架构基础上的功能开发清晰简单,尽可能做到对未知需求具有良好的伸缩性,尽可能的对运行期效率有所保障等等(你所能想到的好的方面)。

 

系统分层:

        系统分层是软件架构设计的一个重要方面,分层的思想在《代码大全》里有相对详细的描述,应该是比较早就提出了,是降低系统复杂度的一种比较常见的做法。

 

        分层的粒度是根据问题的复杂程度以及具体的业务内容来决定的,对于简单问题使用过细的分层不只是杀鸡用牛刀的问题,实现起来也会变得啰嗦冗长,简单的问题会被搞复杂。这可以理解为对简单问题赋予了太多的概念,概念这种东西最好保持精简,这是设计范畴的问题了。 

        游戏的服务端根据游戏的类型和公司的实际情况会有许多差异,限于个人知识没办法一一比较说明,我只写我相对熟悉的MMOARPG的服务端设计。

一般也是类似软件业比较经典的三层划分(没记错的话应该是 系统层、业务逻辑层、表现层),首先是系统层,然后是引擎层,最后是逻辑层。下面分别介绍:

 

· 系统层:

        之所以需要有这么一层主要是因为程序语言层面能够提供的功能十分有限,更强力的功能常常要通过操作系统获得(比如线程和线程的各种锁)。当然您也可以试试避免直接从操作系统获得这些功能,而全部改用第三方库来解决问题,但他们的底层特性肯定也只能是来自操作系统,区别只是是不是你自己来做这些事情而已。

        该层设计的目标是要把所有依赖特定系统的逻辑全部隔离在这一层内,防止上层逻辑对操作系统产生直接依赖。同时,根据上层需求向上层提供足够多的底层功能。大概不会涉及比较复杂的程序编码,但这层实现起来也是相当不轻松的,因为现在基本都会有跨平台的需求,所以一定要在系统层考虑好程序移植问题,把程序规划好,尽量在移植的时候只需要按定义好的接口用最少量的代码再实现一个该系统的版本。同时,开发系统层也是需要对主流操作系统编程有一定经验(因为这个地方常常许多坑),防止系统API误用导致后期BUG追查起来十分困难。

        一定要处理的干净稳定,这样才称得上是为上层开发打下一个良好基石了。

 

· 引擎层:

        游戏的逻辑层有着海量的游戏逻辑,所以为了简化问题,还是把其中一些和游戏内容关联不大但必不可少的功能抽取出来单独作为一层。即去掉具体游戏逻辑的血肉,剩下来的其实都可以囊括到引擎中去。除此之外,设计引擎层也是比较经济的做法,因为和具体游戏逻辑不关联,又包罗万象集合了许多公共组件,它日做其他项目的时候可以完完全全的复用起来。引擎层铺设在系统层的基础上,与操作系统相隔离。

        一般而言引擎层会涉及的问题:通信、日志、数据库、场景管理、配置管理、主循环、脚本系统、计时器、内存管理、线程管理、容错机制、基础游戏对象、视野管理等。具体项目不一定这么划分,但一般认为这套功能是MMOARPG服务端标配了。各个模块意思也很清晰,所以我想不用一一介绍也不影响理解引擎层是怎样的东西。

 

· 逻辑层:

        逻辑层建立在引擎层的基础上,一般不需要操心除游戏功能以外的东西(如果不是的话通常暗示结构设计不合理)。到了逻辑层当然就是指那些与游戏内容密切关联的程序了,项目对游戏功能的要求越高,逻辑层的代码就越多。多是一定的,但具体多多少,增长的快不快还是一定程度上取决于开发者对游戏的理解,看看是否能对一些功能进行简单合理的抽象。

        逻辑层的主要就堆堆逻辑(十分琐碎),一般对编码能力要求不高,但实际上对设计的要求颇高,代码多则BUG多,太多功能写起来要命维护起来就更要命了...但据观察写逻辑的一般是程序部门人数比重最大的,而且对编码能力又没那么高的要求,所以一般都拿来控制成本了,所以良好设计什么的是不能有太多期待。要么就是先派一俩强力党先把核心功能写好,避免让后期纯粹来写逻辑的人来设计模块,这样可能还比较稳妥些。

        逻辑层一般包括:具体游戏对象(玩家、NPC)、基本游戏行为(聊天、战斗,移动常常会被抽到引擎层去)、帮会、任务、道具、技能、副本、宠物、好友、PK等。

        关于逻辑层的设计有许多东西可以讲,这里先不展开。

游戏系统开发笔记(六)——服务端架构设计

 上回写了写服务端的分层结构,分层是比较宏观上的东西,至于层次间具体的交互方式还得通过各个模块的交互方式来体现,姑且把这种模块划分以及其间的交互关系称之为架构吧,下面就来谈谈MMORPG的服务端架构

        对于制作一款游戏而言,首先要考虑的是做什么样的游戏?要实现哪些功能?而为了进一步简化问题,我们先考虑一款游戏的核心玩法——即少了这一部分便会使得整个游戏失去骨骼,乃至于产生一种“皮之不存毛将焉附”的感觉的部分。

 

        首先,对于一款MMORPG而言,角色和战斗是RPG游戏中比较强调的一个部分,其他功能会丰富整个游戏使它增添乐趣,但不会取而代之。所以我们的系统至少应当包含有NPC模块、玩家角色模块,需要有活动的场所因此——场景模块,需要有活动的内容因此——战斗模块。

        其次,为了使玩家间能够相互通信,我们还需要网络模块;为了记录玩家数据,我们需要数据库模块;为了监控和记录游戏和程序状态,我们需要一个日志模块。好了,再说缺少什么的话,我觉得是有了更好,但若没有我们也能好歹应付下来。

        最后,罗列一下:NPC、玩家、场景、战斗、网络、数据库、日志——完整MMORPG的最小系统版图~

 

接下来考虑这些逻辑如何在程序中体现的问题——

《游戏系统开发笔记(四)——游戏程序简介》中曾简单介绍过服务端的程序结构,那么这里再来细化一下,先考虑最简单的设计

直接上贾代码:

  1. int main(int argc, char* argv[])  
  2. {  
  3.    Init(); //初始化各种资源、环境、数据  
  4.     while(g_State != GAME_EXIT)  
  5.    {  
  6.        CheckToSleep(); // 定一个循环间隔,没跑满的时间咱们让CPU放松下  
  7.         //GameLoop  
  8.        NetMessLoop();  // 收发网络消息,并将收到的消息分派给各个模块(比如加到各自的消息队列中)  
  9.         NpcAILoop();    // Npc的AI  
  10.        PlayerLoop();   // 处理玩家行为  
  11.         //OtherLoop     // 比如帮会、副本、任务等等等  
  12.         DBLoop();       // 数据库操作  
  13.         TimeEvent();    // 定时任务  
  14.     }  
  15.     Release(); // 清理和记录工作  
  16.     return 0;  
  17. }  


        这里我们把预期游戏的功能拆分成网络消息、AI、玩家行为、定时任务几块,通过一个循环不断的去刷新各种状态、执行各种请求,从而达到使游戏运转起来的目的,循环的间隔时间主要影响了游戏的最小响应时间。其中日志模块、数据库一般会设计成一个全局范围的功能,各个模块直接对其进行操作。

        这个例子把各个模块功能完成理论上就可以完成任务了,而且看上去是十分清晰简洁的。按这种方式写下去,通常就是对每个在线玩家和NPC设置一个唯一ID,放到各个模块的表中,各个Loop维护一个消息队列,消息格式可能设计成消息ID+参数的形式。接下来实现各种消息对应的处理函数,然后处理消息的过程中从参数解析到对象ID,通过ID和一组操作角色行为的函数来进行控制。

        ——这是比较典型的C风格的游戏服务端做法,过程式的风格使得结构看起来比较扁平化,相对容易快速把握住整体结构。

 

下面是这个简单例子的逻辑结构:

--------------------------------------------------------------------------------------------------

 

        可以从中看出,我们把大部分任务都归到了逻辑模块,这会带来两个显而易见的问题:1、过于复杂 2、项目代码难以重用

        另外,在这个例子中我们也没有考虑多线程,所有操作都在主线程内完成,好处当然就是简单清晰。带来的问题,首要的倒谈不上硬件资源浪费,因为多核的服务器其实大不了多开几个游戏服务端,一个核跑一个服也就没有浪费不浪费了。更加重要的问题是现在单核的主频因为技术制约已经很难再有所提高了,依靠单核的计算能力很可能会使得一个游戏只能承载寥寥几百人同时在线——多么杯具!

 

        所以总的来说,对于现在的商业游戏项目来说,项目代码难以重用和承载人数太少是太难以接受的,一般来说也不愿意投入许多精力去做这样的东西吧。

 

        所以我们需要首先针对这两个问题对项目进行优化——

        代码重用问题一般而言首先会考虑根据“一般性”和“特殊性”对功能进行分离,设计一组划分更加合理的模块。而承载人数的问题,虽然优化程序是很重要一方面,但既然结构上具有不合理性,当然先考虑从结构上解决问题——我们需要使程序能够更加充分的利用硬件资源。具体的措施我会先从第一个问题着手,因为模块设计好了通常也就为多线程做好了必要准备,剩下的可能也就是把它们分别装配到其他线程的事情而已了。

        因为网络、数据库和日志三个模块功能上非常独立,为它们各自设计一组逻辑无关的操作接口后就已经具备了重用的条件,这个例子中主要的问题在逻辑模块。

 

        经过考虑,对于MMORPG来说逻辑模块的只有移动、场景相关的功能是具有比较广泛的一般性的,其它功能都或多或少的和具体游戏产生不可分割的联系。但因为移动相关的功能比较复杂、执行频率也非常高,所以可以单独抽出来做一个模块。说它复杂因为移动不只是简单的从一点到另一点,还会涉及碰撞、移动路径、同步等问题。我们把可视、可移动的这种能力——很基本的能力设计成一个独立对象(实体对象),组合到需要这种能力的其它对象中。

        实体对象的设计包含两个部分,其一是执行具体移动相关业务的部分,其二是逻辑模块和该模块交互的接口层,上面说的组合对象实际上是指把这里的作为接口的对象组合进来,这样我们才能把实体对象需要做的工作给完整的抽取出来。给这个模块赋予一个好听点的名字,就叫引擎模块吧。

        我们的逻辑模块剩余的还有角色、NPC、战斗、场景以及其它游戏功能,它们相互之间会有比较频繁和复杂的相互操作,最多只能做到把各个子模块划分的再清晰些,但若是把任何一个部分放其它线程去就麻烦啦!复杂的游戏逻辑里面还要夹杂各种异步操作简直要让人发疯。

 

所以最后关于线程划分的设计也差不多出来啦:

-------------------------------

        这种结构说起来算是一个拆分多线程的雏形吧,具体操作过程中可能还要有许多调整。根据性能热点和功能类型可以考虑进一步拆分细项,但拆分的过程中要考虑好多线程带来的利弊得失,对于游戏服务器来说,多线程间过多的交互会带来较大的消息处理延迟,使得一些细节的游戏表现很难做到位。另外也要考虑到开发的难度,像逻辑这边只能说还是更多的要为开发难度着想,异步的游戏逻辑下实在是很容易出各种BUG,有的不好查,有的查出来又不好解决 等等等~~

        总体来说,负载问题能得到或多或少的缓解,而代码复用的问题,一般来说引擎层的东西写稳定了就不需要再改了。逻辑上的东西虽然大多都很有个性,但考虑到MMORPG类游戏总体上都长的差不多,直接拿到一个项目来修改和扩展也无妨,而且可以相对节约学习使用引擎的成本。考虑到扩展和节约代码的问题,我觉得可能(仅仅是可能,这方面毫无经验)用C++来实现会更适合些。

 

        结合上一章的分层结构来看,系统层实际上只提供了一些底层操作供上层调用,相当于工具库,没有结构上的意义,所以它对于这套架构来说是不可见的。而引擎层在上面第二张图里也已经有雏形了——有个引擎线程,但要注意的是引擎线程和引擎层是不等价的,上面也说了,这里只是“比较随便的”称之为引擎模块而已,其实你爱叫什么“移动层”的也完全随你。引擎层所指的范围要大些,包括这里的“引擎线程”在内,还有网络、数据库、日志模块都可以归入引擎线程。而作为大头的逻辑线程(主线程),同时也可以认为是分层意义上的逻辑层

        

        很晚了,先写到这里。


原文地址:https://www.cnblogs.com/daichangya/p/12959035.html