http://www.gameres.com/677342.html

文/AI分享站Finney

  首先先来定义一下什么是我这里说的核心游戏系统,一般来说,游戏可以大致分为两个部分,一个部分是我这里指的核心游戏部分,比如FPS里的射击战斗部分,或者如LOL里的战斗对抗部分,又或者是体育类游戏里的比赛部分等等。

<ignore_js_op>
<ignore_js_op>
<ignore_js_op>

  这些是游戏里的主要玩的点,核心游戏部分可以很重,占到玩家80%以上的游戏时间,也可以很轻,甚至没有,像现在很火的列王的纷争(COK),几乎就是没有什么核心游戏部分。另一部分就是外围的辅助系统,比如装备,任务,社交等等,这部分也有玩点和设计用意,这两个部分相辅相成组成了大部分游戏的主体框架。而今天要聊的就是第一部分的核心游戏系统。

  从开始做游戏到现在,我大部分的工作是专注在引擎以及核心游戏系统部分,所以今天就想来聊聊如何来设计核心游戏系统。当然这个设计不一定适用于所有的游戏,仅仅是我个人的经验之谈,希望能给大家一些参考的价值。

  大多数情况下,核心游戏系统都比较复杂,牵涉到很多系统之间的协作,也和策划的需求有相当紧密的联系,国外公司一般称这类程序员为Gameplay programmer,国内公司这种职位相对较少,一般就以泛指的客户端程序员代替了,但和做外围系统的程序员不同,真正的Gameplay programmer需要对于AI系统,动画系统,物理系统都有一定的了解,因为这是核心游戏部分都会涉及的领域。正因为核心游戏系统的复杂性,所以必须要有一个适合的,灵活的架构来支持,抛开一些基本的优点,诸如可扩展性,低耦合等等要求不说,我最直观的感受,就是以下两点好处:

  • 多人合作:一个好的架构可以将系统进行合理的拆分,这样的话就便于多人协作,对于核心游戏系统来说,一般是不可能一个人单枪匹马的去完成的,所以如何去拆分任务让更多的人参与,对于项目而言是相当有利的。对于现在的AAA的游戏来说,单单一个主角,可能就会有将近10个程序员在一起制作,包括AI,行为,动画等等,所以好的架构可以保证工作效率随着人数的增加而得到提升;
  • 留有“挥霍”的空间:架构是很难完美的,因为在开始设计的时候,所有的需求并不明确,特别是核心游戏系统,可能会推倒重来,重构很多次。而当游戏方向定下来了之后,一些策划的改动或者扩展,也会使得以前的架构在某些情况下变得不是很适用,这个时候就会需要一些对于特殊情况的处理,也就是所谓的“hack”,好的架构会让我们在开发后期,在不重构的情况下,有余地进行适当的“hack”,而不是在一开始就“hack”到底,导致bug满天飞。

  好,接下来开始说说设计思路。

  解构一个复杂的系统,有一个很好(不是唯一)的办法,就是“分层结构”(Layered structure),也就是把一个复杂系统,分成一层一层的结构,每一层都做每一层自己的事情,并且每一层都是单向依赖。这样可以把一个网状的,如同乱线团一样的复杂系统,梳理的非常的清楚。这样的例子其实很多,比如学计算机的人都很熟悉的OSI网络七层架构,这就是一个非常好的,把复杂问题层次化的典型例子,它使得每一层都可以独立设计,而且可以有明确的设计目标,层与层之间的接口也变得非常清晰。

<ignore_js_op>

  还有一个游戏的例子,就是游戏的架构,游戏其实也是遵照这层次化的设计思路来设计的,虽然不像OSI那样有一个标准化的结构,但是大部分游戏可以分为核心层(Core),引擎层(Engine),游戏类型层(Game Genre),游戏层(Game),像现在一般的商用的游戏引擎,基本就做到核心层和引擎层,再往上就是使用引擎的人自己设计和实现了,像一些大公司可能会有一些积累,就会根据不同的游戏类型在引擎层的之上抽象出游戏类型层,比如体育类游戏,射击类游戏等等,然后再开始开发实际的游戏产品。这种分层的架构设计就可以帮助我们把复杂的系统进行解构,从而实现每个子系统或者模块的功能单一化。

<ignore_js_op>

  核心游戏系统架构也可以用这样的思路来设计,这样每一层都可以由不同的人来负责,如果实现的话,在一层当中也可以进行任务分工,那下面我就根据执行顺序,自上而下一层一层来描述。

  总共的架构分为五层

<ignore_js_op>

  第一层:更新/收集世界信息

  这部分主要是要设计两个部分,一个是知识池(Knowledge Pool),另一个就是感知器(Sensor)。听上去很高大上,其实概念上很简单,知识池可以理解为就是世界信息的数据存储,比如某一个智能体需要一个这样的数据,“谁是离我最近的人”,这个数据就可以存下来,方便获取,写成代码的话,就类似于这样:

  1. KnowledgePool().Get(WHO_IS_NEAREST_TO_ME, meEntity)

复制代码

  这种数据存储的数据结构,可以根据不同的情况去选择,用key-value的黑板格式,或者自定义的数据类型都可以,也可以分成多个知识池来管理不同类型的数据,设计的关键就是要有清晰的世界信息获取方式。

  感知器的话,就可以理解为具体的获取数据的方法,可以定义一个感知器的接口类:

  1. interface ISensor {
  2. Update()
  3. }

复制代码

  这样就可以把这个感知器注册到一个感知器的管理器中,当收集所有世界信息的时候,只要遍历一遍这些感知器,就可以完成对于世界信息的收集工作:

  1. foreach(s in sensers){
  2. s.Update()
  3. }

复制代码

  感知器也可以分为两种,一种是全局的感知器,这种可以看成是对于游戏整个世界,或者关卡的抽象,比如势力图

<ignore_js_op>

  还有一种是个体感知器,比如听力,视野等等

<ignore_js_op>

  当然,这只是一种思路,也可以不定义接口,而是写成不同的数据更新方法。这就是第一层,主要的功能就是为下层预备数据。

  第二层往下,都是针对单个智能体的更新,也就是说需要对每一个智能体执行更新操作。关于智能体的行为,很多时候容易写的一团糟,又要决策,又要运动,又有动画,还要处理物理,有些系统呢,需要每帧更新,比如位置,有些呢,又不需要更新的这么勤快,比如决策,所以在设计上我把它分成几个层次,决策层,请求层,行为层,运动层。

  第二层:更新决策层(做什么)- What to do

  决策层就是负责来决策此时该智能体应该要做什么,比如我要走到某个位置,我要攻击,放技能等等,可以说,这就是传统所说的人工智能AI部分,这部分只根据当前所有的世界信息,产生“做什么”的决策,决策的内容会封装在一个“请求”(Request)的结构中,继续向下传递:

  这部分的结构可以有多种选择,状态机,行为树,甚至神经网络都可以,但是有两个要点

决策的时机:也就是什么时候进行决策,这里就可以用来控制决策的频率,比如离玩家很远的人可以降低决策频度,离玩家近的人,可以提高决策频度等等,类似与这种的控制都应该在这一层中得到支持和实现。
决策请求的类型:这一层的输出可以看成是所有该智能体可以做的决策的总和,所以千万不要把一些下层的行为放在这里,比如寻路,接下来会说到,寻路并不是决策层的决策行为,它只是来处理“移动”这个决策的一个方法。还有比如选择动画,也不应该是决策层所要关心的内容。

  还有一种特殊的模块是属于这一层,那就是玩家输入,玩家的输入说起来,其实也是一种决策,只是这个决策是通过玩家来做出的。行为树就很容易处理这个情况,将玩家输入和AI决策可以融合成一体让所有的智能体共用:

<ignore_js_op>

  第三层:更新请求层

  上面说到,决策层产生的输出是“请求”,请求是一种自定义的数据结构,包含所以该决策所需要传递的决策信息。那为什么要更新请求层呢?直接把当前请求传递给下一层不就好了吗?这一层的功能抽象,也是我在实践中的经验,总结一句话,“请求层”就类似于一个“防火墙”,由它来“过滤”,那些请求会被继续往下传递到行为层。

  我们可以先来看一下请求层的设计,请求层的设计,借鉴了渲染中的“双缓冲”结构,把请求分为,前端请求(Foreground Request),后端请求(Background Request),前端请求就是当前智能体正在执行的请求,因为一个决策请求可能需要多帧才能完成(想象一下,移动到某一个点这个请求,就需要一段时间才能完成),后端请求就是准备执行决策请求,当一定条件满足后,就可以做了一个“Flip”的操作(前后互换),把后端请求变成前端请求,这样的话,后一层就会执行这个请求,从而改变行为了。

<ignore_js_op>

  细心的同学会发现,在上面的描述中,有一个地方值得回味,就是Flip操作是,“当一定条件满足后”,而这个条件的监测,就是这里更新请求层的时候需要做的事情了,其实每一个决策是存在潜在的优先级的,这个优先级和策划的设计有关,比如我当前正在执行一个攻击的请求,这个时候,新的请求是一个释放技能,此时策划要求,技能释放能够打断当前的攻击行为,那这个判断逻辑就可以写在这一层中,使得当这个条件满足时,可以立即切换请求。这里的设计一般采用配优先级表,和基于规则的(Rule-based)的实现方式。

  有了这一层的逻辑抽象,就可以保证它的上层和下层都不需要关心决策能否被执行,而只要关心自身的决策/行为逻辑就可以了,大大降低了上下层的实现复杂度。由于这部分逻辑相对比较繁琐,所以把这些繁琐的逻辑集中在一起,也是一种理想的设计思路。

  第四层:更新行为层(怎么做)- How to do

  就像补充里所描述的,行为层的职责就是怎么做,也就是如何去完成上层经过决策,经过规则的逻辑判定,最终“胜出”的那个前端请求。像前面提到的寻路,或者选择需要播放的动画,都是在这一层所完成的工作,这层的实现同样可以用行为树,或者状态机,不过我还是推荐用行为树,因为行为树可以扩展和处理更复杂的行为逻辑,比如随机,序列,并行等等。某些请求可能不是用单个行为可以完成的,需要多个行为的配合,比如完成一个技能释放,需要先集气,然后再释放,类似这样的行为,就可以用行为树来实现了,更棒的是,集气这个行为还能被共用。

<ignore_js_op>

  在这一层中,会产生一系列的输出,有特效,有动画,可能还有声音等等,有一个很重要,也是必不可少的,那就是运动信息(Kinematic Information),这也是智能体最终呈现的样子,这部分内容包括空间信息(位移,旋转,缩放)和动画信息,某些情况下,智能体的空间信息可以通过物理计算在这一层直接更新,动画信息直接调用引擎的播放接口即可,但有时候这种处理还不够,那就需要第五层,运动层的参与。

  第五层:更新运动层

  游戏中物体的移动有两种方式,一种是动画跟随物理,比如为了解决移动中的滑步问题,我们可以做多个移动的动画,然后根据速度做融合,这样可以调出一个滑步不明显的表现,还有一种就是root motion,也就是物理跟随动画,就是物体的移动和旋转完全跟随动画中的效果,这样可以解决一些物理没有办法模拟的复杂运动。

  有些时候,我们需要混合使用这两种方式,并且在位移过程中需要加上一些修正(比如为了解决同步问题),这个时候,就需要用运动层来实现。这一层的输入,就是行为层产生的运动信息,输出自然就是智能体最终的空间信息和动画了。

  在实现上,建议对这两种方式进行封装,这样可以对于上层来说,接口就相对统一了。

  有了这五层的设计,整个核心游戏系统的更新循环就完成了,并且每一层的功能职责和输入输出都有了明确的定义,如下图:

<ignore_js_op>

  每一层具体的设计可以仁者见仁,智者见智,并且也和具体的游戏有关,但是整体的架构基本就可以参考这样的思路,至少以我的实践来看,不会导致结构混乱,也可以更好的进行分工和合作。

  最后再聊一个关于核心游戏部分网络同步和回放系统的问题(同步和回放是差不多的东西,回放只是把同步的东西存下来而已)。

  其实如果有了上面的架构设计,理解网络同步就很简单了。

  如果同步放在第二层,那就是采用的“同步输入”(Input synchronization)的方式

<ignore_js_op>

  如果在第三层,那就是“同步命令”(Command synchronization)的方式

<ignore_js_op>

  如果放在第四层/第五层,那就是“同步状态”(State synchronization)的方式

<ignore_js_op>

  这三种方式是越往下传输的数据越多,但是“失同步”(Out of synchronization)的风险就越小。当然不同的方式在具体实现上,还是有很多值得讨论的地方,这里就不多说了。如果客户端和游戏服务器采用相同的语言,那就可以很方便的在单机游戏和网络游戏间切换,在单机模式下,只是本地和本地通信罢了,FPS游戏很多都是这样去实现的,其实在单机模式下,内部也是一个CS的架构,而如果需要一个服务器的版本,只是加一个宏去编译而已。

  好了,就说这么多,欢迎讨论,也可以下载那个僵尸的 <ignore_js_op>TsiU_AIToolkit_CSharp.zip (5.97 MB, 下载次数: 12823) ,里面也采用了类似的层次结构,可以参考一下。

转载于:https://www.cnblogs.com/nafio/p/9187012.html

核心游戏系统架构设计相关推荐

  1. 十全干货:核心游戏系统架构设计

    http://www.gameres.com/677342.html 文/AI分享站Finney 首先先来定义一下什么是我这里说的核心游戏系统,一般来说,游戏可以大致分为两个部分,一个部分是我这里指的 ...

  2. Unity教程之-Unity游戏技能Skill系统架构设计

    我想把技能做的比较牛逼,所以项目一开始我就在思考,是否需要一个灵活自由的技能系统架构设计,传统的技能设计,做法都是填excel表,技能需要什么,都填表里,很死板,比如有的技能只需要1个特效,有的要10 ...

  3. 分布式实时处理系统架构设计与机器学习实践

    编者按:在2017年的1月11日,CSDN高级架构师金牌授课群为群友们带来了第一次的分享,讲师和主题参见这里,本文为课程后续的文字整理,第一时间发出来分享给读者,课件下载点击这里. 大家好,我们今天主 ...

  4. 网购秒杀系统架构设计案例分析

    大型网站技术架构-核心原理与案例分析 作者:李智慧 申明:文章版权归作者所有,若有侵权,请联系删除 秒杀是电子商务网站常见的一种营销手段:将少量商品(通常只有一件)以极低的价格,在特定的时间点开始出售 ...

  5. 网购秒杀系统架构设计案例分析——《大型网站技术架构》笔记

    一.核心思想: 网站秒杀时的并发比正常运营时多的多,所以网站的秒杀业务不能使用正常的网站业务流程,也不能和正常的网站交易业务共用服务器(否则造成巨大浪费),必须设计部署专门的秒杀系统,进行专门应对 二 ...

  6. 海量并发低延时 RTC-CDN 系统架构设计(下)

    上半部分内容:海量并发低延时 RTC-CDN 系统架构设计(上) 低延时 RTC-CDN 系统的架构 传统 CDN 直播发展多年,为了优化延时,业界基本上朝两大优化方向:优化传输层协议和在传输层协议的 ...

  7. petshop4.0 具体解释之中的一个(系统架构设计)

    前言:PetShop是一个范例,微软用它来展示.Net企业系统开发的能力.业界有很多.Net与J2EE之争,很多数据是从微软的PetShop和Sun的PetStore而来.这样的争论不可避免带有浓厚的 ...

  8. 亿万级图数据库 Nebula Graph 的数据模型和系统架构设计

    Nebula Graph:一个开源的分布式图数据库.作为唯一能够存储万亿个带属性的节点和边的在线图数据库,Nebula Graph 不仅能够在高并发场景下满足毫秒级的低时延查询要求,还能够实现服务高可 ...

  9. petshop4.0 详解之一(系统架构设计)

    前言:PetShop是一个范例,微软用它来展示.Net企业系统开发的能力.业界有许多.Net与J2EE之争,许多数据是从微软的PetShop和Sun的PetStore而来.这种争论不可避免带有浓厚的商 ...

  10. PetShop的系统架构设计[转]

    <解剖PetShop>系列之一 前言:PetShop是一个范例,微软用它来展示.Net企业系统开发的能力.业界有许多.Net与J2EE之争,许多数据是从微软的PetShop和Sun的Pet ...

最新文章

  1. 又一个不错的FCKeditor 2.2的安装、修改和调用方法
  2. Codeforces Beta Round #16 (Div. 2 Only)【未完结】
  3. context:annotation-config/,context:component-scan/,mvc:annotation-driven/区分
  4. mac os 切换网络优先级
  5. win 7 ×××自动拨号设置
  6. SharePoint 2013 托管导航及相关配置
  7. css3-10 css3中的边框样式有哪几种
  8. 百度杀毒软件2013低调发布 仅面向泰国市场推出
  9. 报价管理解决方案丨汇信
  10. BIP语言教程(一)
  11. AutoLayout(Ⅱ):遇到的几个小问题(Keng)
  12. iText7高级教程之html2pdf——7.关于pdfHTML经常问的问题
  13. UnityWebRequest加载音频
  14. 中秋赏月地图出炉,跟随锦江之星去赏月
  15. 微信公众号的封面图怎么拿到手
  16. Mobius反演学习
  17. 影像测量—摄影测量和RTK原理
  18. 【转】PCIE 通道x4/8/16的定义
  19. Django中内置的User模型
  20. C++模板类声明和定义几种写法

热门文章

  1. 云计算成就代码之美——首届阿里云开发者大赛巡礼
  2. 虚拟机运行出现蓝屏解决 win11
  3. 6种继承的优点和缺点
  4. 计算机辅助电话访问优势,电话访问的优点缺点、优势不足、局限性
  5. 【历史上的今天】9 月 20 日:中国正式接触互联网;抖音上线;中科大成立
  6. 如何将PDF转换成word文档
  7. U-Net网络模型(添加通道与空间注意力机制)代码---亲测提高精度
  8. html纵向时间轴代码,垂直时间轴HTML
  9. virtualbox硬件加速_虚拟机安装后必做的3项设置,解决运行卡慢问题,提速十倍!...
  10. 嵌入式软件工程师是前端还是后端_软件开发工程师与嵌入式软件工程师有区别吗...