可复用框架的必要性与可行性

在现代游戏产品的开发中,游戏服务器端程序已经几乎成为了标配。从最简单的正版保护功能,到玩家档案、成就的存储功能,到复杂的主要游戏逻辑运算,游戏服务器端系统都是必不可少的。但是和客户端丰富的游戏引擎不同,服务器端比较少这类可复用的软件产品出现。其原因可能有以下几个:一是欧美、日本的服务器端逻辑一般比较少,所以这类产品的需求也比较少;二是游戏服务器端本身涉及大量不同的运行平台、环境、语言,导致较难统一产品形态;三是游戏服务器端的需求变化比客户端更加“不可预测”,所以导致了产品特性在通用性上较难聚合。但是,游戏服务器端“引擎”,却有明确而清晰的市场需求。我们可以看到大量的游戏团队都在一遍遍的重复开发着类似的功能;而因为缺乏可复用的技术,有很多游戏死于“无法修改”;游戏服务器端程序员也和客户端程序员一样,长期经受着加班的折磨。

事实上,可重用的游戏服务器端框架,是完全可以设计和实用化的。因为理由也非常充分:一是国内游戏产品的游戏服务器端逻辑一般重,提供了丰富的实践需求;二是国内的游戏服务器端运行环境分类,还是比较统一的,其中C/C++在Linux上运行,是一个经典的选择;三是游戏服务器端的需求范围,通过认真的设计,完全可以抽象出可复用的模型。如果我们要找一个可行性的例子,也是有一个的:MudOS和MudLib的例子。

可复用框架的必要性与可行性

在“古老”的MUD(文字多人在线游戏)的世界里,游戏通常由两大部分组成:MudOS和MudLib。其中MudOS负责通用而基础的一系列功能,而MudLib实现具体丰富复杂的玩法。在这样一套体系下,我们看到几乎所有的Mud游戏,都能复用MudOS的代码。而大量不同的Mud游戏,在MudLib的复用程度上,也是非常高的。我所知道有些“大神”,能仅仅用3天时间,就把一个MUD改造成另外一个完全不同感观的游戏——这证明了MUD的这套体系在代码复用上的高度可行性。从MUD的体系上,我们可以学习到它在设计上的一些特点

  • MudOS 接口简单,功能通用
  • MudOS让整个体系的部署、运行都非常简洁。整个游戏只依赖于MudOS,而对外界其他系统依赖很少。
  • MudLib本身也是分层的,以游戏系统的共性先做建模,然后再实现具体的游戏逻辑
  • MudLib是以脚本语言编写,以源代码形式开放给所有开发者,因此灵活同时强大。

可复用结构整体描述

根据我们对MUD体系的学习,以及长期游戏开发经验积累,我们发现,可复用的游戏服务器端框架,应该具有以下几个设计特征:

1-系统应该是典型的分层架构,需要同时具备灵活和强大这两个特征。

2-底层负责有限的功能:通信(请求-响应、单播、组播)、存储(对象缓存、持久化)、调度(并发、定时器)。

3-底层的关注点在非功能性需求,如可用性、扩展性、运维需求等。

4-中层建模是关键,要以游戏的业务模型来提供强大的功能,并提供足够的灵活性。因此应该是开放源代码形式,并且是以库的扩展方式提供。

5-顶层代码应该全部由具体游戏开发者编写,最好能支持脚本语言。

一般来说,我们对于“底层”的认识比较容易达成一致,也有很多业界的产品供参考,但是可惜的,一个完整功能的底层却不太容易找到开源的标杆。很多游戏团队都是自己组装或者搭建的。比如JAVA语言的团队会选择Netty+iBatis+MySQL;C语言的团队会选择ZeroMQ+Redis……。而一些游戏服务器端框架,所提供的能力也参差不齐,如SmartFoxServer主要提供的是通信中请求-响应和组播的能力,而FireFly和Pemelo则在通信功能外增加了调度能力中并发(异步)的支持。从非功能性特性来说,高可用和灵活扩展,以及丰富的运维能力,都难以找到非常好的例子。因此一个好的“底层”,应该是同时具备三大主要功能:通信、存储、调度;也能满足高可用、灵活扩展、丰富运维工具的需求。

可复用框架的必要性与可行性

由于本身服务器端的“底层”就缺乏统一的框架,所以对于中层的模块来说,更是无从获取可重用的代码,尽管很多游戏都有角色、道具、任务、商店……。同时,中层代码的建模,如果要做到高重用度,也需要认真分析各种游戏的共性和特性,并且在设计中遵循良好的接口规划原则,才能真正的实用。因此,我希望能提出几个典型的游戏系统模型,并且以此为出发点,尝试构建典型的游戏中层模型,并且辅以源码开放和库的使用形式,试图提出一个可重用的游戏中层的方案。这个方案包括5个主要部分:RPG系统;社交类系统;引导类系统;战斗系统模型;副本系统框架。

  • 在现代游戏中,RPG玩法的设计非常通用,而RPG玩法本身是一个具有非常典型的模型的:成长和搜集的体验,承载体主要是玩家的属性、技能、装备、道具。尽管玩机的属性名称、数据各有不同,技能体系千差万别,装备和道具的类型功能有很大差异,但是其共性的“获得”“成长”“消耗”却是一致的,在这个角度来说,是完全可以产生可复用代码的。
  • 社交类系统在现代游戏中,承载着玩家间交互的功能。这些功能表现好友系统、组队系统、帮派系统、聊天系统、邮件系统等,其核心模型,为玩家关系构建和玩家交互手段两类。只要我们对玩家关系管理进行建模,以及玩家交互手段(在线、离线)进行建模,基于这两个模型,就能很快速的搭建出上述的一系列社交系统。
  • 引导类系统主要包括剧情系统、商店系统、任务系统、活动系统一类功能。这些功能的底层逻辑,主要是由“引导数据源管理”-“引导数据运作”两个部分构成。实际上这个抽象模型,在几乎所有现代游戏中都具备,而且高度相似。主要的设计难点在于其引导内容的复杂性。因为引导内容是一系列的玩家行为,针对行为的建模远比数据的建模要困难,但是我们也可以在开源的前提下,提供可供修改的模板代码,从而试图减少重复代码的开发。
  • 战斗系统一直是游戏开发的重点和难点,也是游戏巨大差异性的重要部分。但是对于服务器端系统来说,战斗系统的核心一直都是一个“数值比较系统”,简单来说就是一个调用机制比较丰富的比大小函数。服务器端承载战斗系统的功能,往往是提供比较规则,运算比较结果,和玩家的战斗操作往往并不完全一致。当然,也有一些战斗服务器是完整的重现客户端的战斗逻辑,需要建立2d甚至3D的战斗模型,但是其中也一定会包含战斗规则的调用。从这个角度上来说,也是具备了一部分可重用的模块。
  • 副本系统在非MMORPG中几乎是最基本的玩法结构,比如大厅开房间类的玩法,在QQGAME上是标准模型,更不用说《英雄联盟》《使命召唤OL》这类对战游戏。这类系统主要提供玩家在线匹配的功能,有手动选择或者自动组建的能力。其特化的需求主要是自动匹配规则和房间展现数据模型,是一个非常便于构建重用代码的部分。

可复用框架的必要性与可行性

RPG系统的可复用模型

RPG系统主要负责提供游戏中提供“积累、成长”的快感,也是驱动玩家反复进行游戏操作的重要系统。RPG系统能提供这种作用的最基本逻辑,是以玩家为中心,为其赋予了一系列的可成长的数值,然后这些数值可以用在战斗系统或者RPG系统本身。

可复用框架的必要性与可行性

因此,RPG系统是由一系列子系统构成,而这些子系统,又由一个内在逻辑驱动,具备一些共性的行为和数据特征。我们使用面向对象的方法,可以比较清晰的分析出来其结构。我们从需求侧可以看到,RPG系统包含的子系统有:

  • 角色属性系统:提供玩家的等级、攻击力、防御力、敏捷、智慧等一系列游戏需要的数值属性,以及一些特殊的非数值属性,如“黑暗魔法抗性”“防穿刺物理攻击”
  • 技能天赋系统:技能和天赋本质上也是角色身上的属性,但是技能是有一定的等级的,而天赋除了等级,还可以提供玩家自行分配的操作。有些天赋的分配会直接赋予玩家新的技能。
  • 武器装备系统:武器装备主要指角色身上那些有装备栏位限制,但是可随意修改装备状态的系统。武器本身可以携带大量数值,这些数值会由其他系统计算读取。同时装备本身也有可以修改的空间,比如打孔、改名、升级,可以说是一个小型的角色系统。
  • 物品道具系统:物品道具的主要内容包含名字、数量、作用。这些道具有一些是可以消耗的,而另外一些则会作为永久性唯一道具存在于玩家身上。它和武器装备的差异在于,一般没有很复杂的附加数值,但是数量会很多,甚至成千上万,通常以“堆叠”的形式出现。

我们基于这些系统的共性,可以大概总结出一个基础共性的模型。

可复用框架的必要性与可行性

对于以上的数据模型,其行为方法也是比较明显的:

l 角色

  • 新建角色,返回ID
  • 根据ID从持久化或缓存中读取角色load
  • 把角色存储到持久化数据中save
  • 属性的get/put/list
  • 技能的get/put/list
  • 背包内容的put/get/list
  • 装备的arm/unarm/get_by_position
  • 装备栏的add/remove/list

l 属性

  • 根据ID构造
  • 对于各成员属性的getter/setter

l 技能

  • 根据ID构造
  • 对于各成员属性的getter/setter

l 物品

  • 根据ID+数量构造
  • 对于各成员属性的getter/setter
  • 使用(可能消耗)

l 装备

  • 根据ID构造
  • 对于各成员属性的getter/setter
  • 被装备armed触发的效果回调
  • 被脱下unarmed触发的效果回调

需要注意的是,这里的技能、属性、物品如果不带可修改的能力的话,可以采用单例以及享元的模式,这样可以大大减少对于内存的消耗,根据这些属性和方法,我们大概可以画出这样的类关系图:

可复用框架的必要性与可行性

对于以上设计,可能读者会问,这些系统完全没有考虑到游戏客户端和服务器通信的问题,也没有考虑登录在线的实现,仅仅是一些数据结构的列举,真的能用吗?为此,我就把相关的一些系统试着画一下类结构图。

可复用框架的必要性与可行性

这里的命令系统主要是负责网络通信的一套系统,把客户端的操作变成对“玩家对象”的方法函数的调用;而登录系统是一个负责玩家在线的缓存系统,可以让命令系统获得“玩家对象”;玩家对象则由负责通信的对象和负责数据的角色对象两者组合而成,玩家对象除了对数据的存取和读写外,还会使用通信的对象来完成诸如说话、战斗等操作。

像这种数据建模,从一开始看似乎并没什么特别的优势,但是如果你需要快速开发一个游戏的时候,你可以从一套模板代码开始扩展或者修改,会比完全从头开发要快的多。有一些通用的逻辑,比如背包大小检查,物品负重判断,天赋总数控制,都可以直接添加到这个中层MudLib的代码里面,这样就确确实实的减少了代码的编写。

社交类系统的可复用模型

在线游戏由于可以让不同的玩家在游戏中互动,所以产生了比单机游戏有趣的多的感觉。多人合作和竞争的操作,以及多种人际关系的玩法,都是现代游戏所热衷的设计,特别在国内的MMORPG中,对于玩家关系所依赖的玩法更加丰富,国战、结婚、帮派等等都是很常见的社交类系统。社交类系统包含我们常见的好友系统、公会系统、组队系统、聊天系统、邮件系统等等。而这些丰富的系统,其背后也由两个核心的逻辑系统组成:玩家关系;玩家间交互。常见的系统有:

  • 聊天系统:一般有玩家间私聊、多频道聊天等功能,为在线沟通的主要系统。聊天内容除了文字外,还有表情图,角色、物品链接等功能。
  • 邮件系统:则是离线沟通的主要系统,还承担着游戏内物品道具的寄送功能。很多任务、活动、交易系统都是用邮件系统来发物品给玩家。
  • 公会系统:属于需要玩家主动建立的玩家组织,公会主要的功能是对玩家进行分组管理,并会根据玩家组织的数据,添加更多的附属功能。家族、帮派是公会系统的不同名称。公会系统除了展示成员,设置成员等级等等界面功能外,往往还附带公会基地、仓库,成员升级机制等等。这些理论上不属于社交类核心范畴,而属于扩展功能。这些功能的开发工作量也比较大,也许这一块的代码难以抽象到中层中去,但是如果中层可以服用,则部分高层倒是可以通过修改代码来重用的。
  • 好友系统:每个玩家都有一个好友关系的列表。另外有的游戏还扩展出固定名称和人数的特异好友系统,如结拜系统、师徒系统、夫妻系统等。

可复用框架的必要性与可行性

交互系统和玩家关系是整个中层系统的核心,他们具备的数据关系可以大概如下记录:

可复用框架的必要性与可行性

以上类型的成员方法:

l 交互消息

  • 内容的getter/setter
  • 发送方/接收方的getter/setter

l 交互系统

  • 发送一条消息
  • 收取一条消息
  • 设置收取回调通知

l 玩家关系

  • 加入一个角色
  • 列出所有角色
  • 删除一个角色
  • 新建关系列表,返回ID
  • 根据ID从持久化或缓存中读取角色load
  • 把角色存储到持久化数据中save

在实现社交类系统的时候,最常见的难题是对于社交系统对象的单例操作。由于游戏服务器可能是多进程多物理机器的。要实现跨机器投递交互消息,是需要额外的处理能力的。有一些实现者会采用ActiveMQ之类的消息队列服务来承载,有些则使用数据库存储做交换。但是增加额外的服务会增加整体的运维和开发的复杂度,因此GameOS(底层)提供的跨机器数据缓存就是必不可少的部分。基于这个基础功能,实现消息队列或在线消息投递都会非常的简单。

引导类系统的可复用模型

说到游戏中的“引导类系统”,最常见的就是所谓“新手引导”,这些专门设计的游戏流程,让玩家一步步的按规定顺序去操作游戏。而“任务系统”,也是最著名的引导类系统,这个最初只是基于NPC机关的小玩法,现在已经成为几乎所有游戏的标配。并且后续还出现了“每日奖励”,“日常任务”,“活动任务”,甚至“成就系统”等各种变种。这几个系统的核心逻辑,都是策划预设了一条“任务链”,让玩家通过操作,来改变自己在“任务链”上的位置。另外一种很特别的引导类系统,就是商店。最古老的游戏中都会有商店,到现在的游戏,商店系统的形态变得更加多样化,比如专门使用某种货币的兑换系统(使用人民币的商城系统)。拍卖行作为玩家的物品互动系统,实际上也是一种商店系统,只不过其价格和商品不是系统设置,而是玩家操作的而已。

虽然从产品角度来说,都是引导玩家进行某些行为,但是以上两类系统的核心逻辑是有所不同的,因此我打算分成两个部分来描述。

任务系统族:

任务系统的基础数据模型,是一个预设的任务库,存放着大量的任务链以及具体任务。而玩家则有一个任务列表,存放着已经完成的任务、接受后但未完成的任务。具体的任务最重要的是包含着任务完成条件和完成进度的数据,这些任务条件一般来说都是必须全部完成的。因此我们可以抽象出任务系统的基本数据模型:

可复用框架的必要性与可行性

“任务项”中的“接受条件容器”和“完成条件容器”中,都应该分别对应着两类对象,即“接受条件”和“完成条件”。“接受条件”可以有多个不同的子类型,如判断前置任务是否完成的,玩家是否符合等级,是否具备某个道具等等。而“完成条件/进度”也应该有多个子类型,如“对话操作”“杀怪操作”“物品收集操作”……游戏中一切的操作都应该可以成为完成条件,具体实现则由游戏的操作中,添加钩子处理程序,对玩家身上的完成条件的检索,然后根据游戏操作的逻辑,对这些玩家身上的“完成条件/进度”做修改。这些模型的方法应该有:

l 任务项

  • 用ID从持久化中load出来并构造
  • 各属性的getter/setter
  • 返回此玩家是否能接受
  • 更新并返回此任务的完成状态

l 玩家任务集

  • 根据玩家ID,从持久化设备中save/load出玩家任务集
  • 接受任务
  • 放弃任务
  • 输入一个“完成条件”的类型,返回所有符合此类型的“完成条件和进度”对象

l 接受条件

  • 输入玩家对象,返回是否满足此条条件

l 完成条件和进度

  • 返回已完成进度
  • 返回总进度
  • 修改已完成进度

由于任务系统的变种种类繁多,所以可能会导致设计大量的“接受条件”和“完成条件进度”类型,所以这些对象的部分属性如“描述文字”等静态内容,应该采用共享数据的形式出现,而不应该反复拷贝。下图初步描述了任务系统族类型的关系:

可复用框架的必要性与可行性

商店系统族:

商店系统的数据核心模型,是一系列可供展示、销售的商品,用于购买商品的货币,以及为这些商品提供展示之地的商店模型。当然这里的商品可能不一定对应于RPG系统里面的物品道具,因为有些商品是权限、等级等不能放入背包里的东西,也有可能是某种属性、状态等等。但是我们还是推荐用RPG系统中的道具来承载,这样编程的复杂度会比较低。

可复用框架的必要性与可行性

商店系统看起来非常简单,但是最复杂的地方在于“购买”环节,因为购买得到的商品,不一定是放入背包的货品,所以商品的“被购买”应该是一个可扩展的虚方法函数。这样商品也可以扩展为背包货品类商品和其他商品类。需要注意的是,买入玩家销售的物品,这个功能并不是非常容易重用,这涉及到如何对玩家物品的估价的问题,因此这个能力应该从最基本的“商店”系统扩展,而不应该加入商店系统中。商品系统的对象方法应该有:

l 商品

  • 属性的getter/setter
  • 以id从持久化设备中构造
  • 被购买行为(虚方法)

l 商店

  • 列出商品,可能带分页接口
  • 卖出商品

商店系统的表现形式非常多样,但是核心逻辑关系却异常简单:

可复用框架的必要性与可行性

至此,已经描述完主要的引导类系统的设计。这些设计本身可能并不能完成非常复杂的游戏逻辑,但是其基础逻辑却足够简洁。这样基于其开发的上层代码,就具备了一个比较统一的实现结果,便于构造出更多能重用或修改使用的系统。

战斗系统的模型构建思考

战斗系统是一个游戏的玩法核心,也是游戏之间差别最大的地方,想要建立可复用的模型,可谓困难最大。但是,游戏的玩法本身也是有分类和传承的。需要特别注意的是,作为服务器端程序,战斗系统的职责是有多种不同层级的:

1-记录战斗结果,发放战斗奖励。很多开房间和按“局”算的游戏,如棋牌类,其游戏本身是分为很多“局”的。游戏是在一局结束后,才结算所有的奖励。另外如《地下城与勇士》(DNF),其游戏为一个个地下城“副本”,本身也是这种分局的。如果服务器端只是为了游戏完成一局后做“结算”,那么其逻辑还是比较简单的:主要就是校验游戏中的消耗与产出是否合理,有无作弊嫌疑,然后更具战斗数据,发放战斗奖励给玩家。这种模型下其实无需很复杂的模型,无论任何的玩法,实际上都可以用一个“校验与奖励”函数作为抽象即可。如果战斗过程中有消耗品使用或者奖励掉落,还要增加一个消耗品函数和奖励掉落函数即可。

2-在二维游戏场景下,对战斗过程进行模拟,且时常会结合帧同步广播来协调游戏。例子就如著名的《英雄联盟》。这一类战斗服务器,往往会作为“主广播器”,生成AI的行为,转发广播玩家的动作,同时计算每个战斗动作的“成败输赢”。这种做法其实和一个客户端计算战斗过程是差别不大的。这种战斗服务器的抽象模型是由几个部分组成:AI控制+状态广播+战斗判定。每一个战斗“帧”,服务器都会更新所有玩家、怪物的状态,然后判定战斗结果,最后广播状态和战斗情况给客户端。这种模型是相对比较复杂的,其最复杂的地方在于处理AI和判定战斗结果的时候,必须在内存中根据二维的战场状况,重现整个游戏情况。这对于一般来说不显示画面的服务器端程序来说,是比较复杂的。

3-在三维游戏场景下,对战斗过程的模拟和广播协调。很多FPS游戏都是这种,而著名的WOW也是这一类例子。其实这个模型和上面第2种情况是类似的,只不过3维空间的计算复杂程度比二维更高。

基于上面所说,一个战斗系统确实是游戏中最复杂的部分,因此我们的建模也不能很轻易简单的完成,而是应该分为几个不同的层次和阶段。第一个层次对应最简单的战斗结果校验和奖励模型:

可复用框架的必要性与可行性

在这里,我们使用“战斗关卡”这个模型来抽象战斗内容,“奖励对象”只是一个用于抽象可能存在的不同奖励内容的接口。一般来说我们的奖励是物品道具、货币,但是也有可能会有更复杂的内容,比如开通某种特殊权限,完成某个任务等等。这些复杂的“战斗奖励”也许不是一个RPG的物品基类能涵盖的。所以不如以组合的形式来耦合关卡奖励和具体的物品。由于战斗关卡的数据项目一般很少,所以就不列举数据属性的设计,而专门关注关键行为的模型:

l 战斗关卡

  • 根据ID从持久化数据中构建关卡
  • 返回战斗关卡的介绍,应该返回一个游戏自定义的介绍数据结构。
  • 输入所有进入玩家对象,开始一局关卡游戏。有可能开始一局游戏会扣减某些玩家资源和检查是否能开始游戏。
  • 输入玩家对象,计算关卡中掉落物品
  • 输入玩家对象,计算关卡中消耗物品
  • 判断关卡是否结束。根据战斗关卡的数据来判断是否应该结束关卡。
  • 输入所有当前玩家对象,进行关卡结束的计算,返回关卡的奖励。此操作会计算游戏过程中的掉落物品和消耗物品。

l 关卡奖励

  • 输入玩家对象,执行奖励(虚函数)

在第二个层次中,战斗系统的模型会变的复杂很多,其中有一块是关于AI模块的。关于AI的模型本文恐怕无法简单完成,因此可以把他看成是一个黑盒子。当然AI简单的用一个状态机实现也可以。在这种情况下,战斗的标准流程是:按帧接受玩家操作,AI产生怪物/NPC操作,计算战斗结果,更新战场状态,广播战场状况。因此模型大致如下:

可复用框架的必要性与可行性

在这个模型下,战斗主要执行逻辑对象,是由“帧同步广播器”来执行的,当然也有可能游戏不是帧同步模型的,但是无论如何都会有一个战斗驱动器来循环的调度战斗进行。如果这个战斗驱动器是帧同步或定时器的,那么游戏就会是实时游戏,如果这个战斗驱动器是由玩家命令触发(可能带超时触发)的,那么游戏可能会是回合制的。不过归根结底,战斗系统都是一个“战斗判定”循环,只是这个循环的定时器的快慢差别,以及是否能由玩家主动暂停或加速的差别。因此以上对象的主要行为应该包括:

l 战斗驱动器(此处是“帧同步广播器)

  • 驱动帧(虚函数)函数:此函数可以由各种不同的情况触发,如定时器、玩家命令等。负责执行具体的战斗计算状况。

l 战场状态集。有时候关卡的战斗状态和这个对象是同一个对象。

  • 获得战场状态:包括所有战场上角色对象及其2D、3D坐标
  • 输入目标角色与技能范围,返回是否命中。(辅助函数,根据2D/3D的差别,会有很多不同的模型,如射线命中判断用于FPS)

l AI模块。AI模块可能包含复杂的状态机模型,但只有一个驱动函数而已。

  • 输入游戏角色对象,执行其AI结果操作。(虚函数)

关于战斗驱动器的几种模型下表有一个分类:

可复用框架的必要性与可行性

关于战斗动作,在2维和3维下略有不同(同理,如果有1维的游戏也是一样的),但是仅仅是参数的区别。实际上,针对特定类型的游戏玩法,比如FPS或者MOBA&RTS类型,这里的战斗动作都具备相当高的可复用可能。这些动作的内容各有不同,但是其“动作”的主要行为是:

  • 判断——是否碰撞(命中),返回碰撞对象
  • 动作——修改发出动作对象以及碰撞对象状态

对于纯数值类型游戏,其实就相当于0维空间游戏,战斗动作的判断实际上返回的对象由动作本身确定,无需空间计算。这在很多RPG或者策略类游戏中是很常见的。

可复用框架的必要性与可行性

最后总结一下战斗系统的完整流程,在有些游戏中,关卡和战斗是配合一起操作的,但是有些游戏则没有关卡,只有战斗,不过这种游戏主要是世界地图的MMORPG,现在这类游戏已经渐渐式微了:

可复用框架的必要性与可行性

尽管战斗系统是如此的复杂和多变,但是其运作还是一个模拟状态变化的系统,所以还是有不同层次的可重用模型可以设计。核心的战斗驱动就是模拟时间因数,而战斗动作则负责对空间因数进行模拟。关卡系统是对战斗的一个时空区隔,也是各种奖励、消耗的小结,因此更常见于游戏当中。

副本系统的构建思考

在早期的联网游戏中,有两种典型的不同种类,一种是类似《传奇》的全地图,分区分服的MMORPG;另外一种则是类似QQ Game类型的“大厅-开房间”的全区全服游戏。这两种游戏在底层架构上有非常大的差异。但是,随着网络游戏的发展,在MMORPG中出现了类似开房间的“副本”系统,甚至出现了可以跨服跨区玩的“副本”;而开房间的游戏也离开了简单的UI界面,出现了《地下城勇士》(DNF)这类所有战斗都发生在关卡副本中的游戏。到后来,如《英雄联盟》《星际争霸2》这类复杂的游戏,都渐渐走上了全区全服开房间的架构。因此,本章的名字叫“副本系统”,其实也可以叫“大厅-房间”系统。

在“副本系统”中,最需要考虑的是游戏服务器的隔离性和生命周期。这时游戏服务器不再简单的代表一个静态的“虚拟世界”,而是一批动态的,不断生灭的小虚拟空间。同时我们还需要一个相对静态的“大厅”。因此在副本系统中,最典型的模型就是:大厅、房间这两个概念。大厅提供玩家的接纳,了解房间的情况,提供玩家对房间的管理、操作;有很多游戏还需要提供自动匹配建立房间的功能。而房间则应该是一个对游戏关卡的容器,能承担任何一段游戏逻辑,但是需要大厅提供一组玩家和选择关卡作为输入,最后在结束时提供一个返回给大厅,修改房间的状态和触发玩家的关卡结算。对比与那些复杂的游戏逻辑系统,副本系统本身。

可复用框架的必要性与可行性

这个模型下各类的行为:

l 大厅

  • 列出所有房间
  • 使用关卡数据建立房间
  • 角色进入、离开房间
  • 启动房间游戏
  • 自动匹配玩家进入房间

l 房间

  • 输入关卡数据构建房间
  • 房间类型setter/getter: 公开、加锁
  • 关卡数据getter/setter
  • 列出房间中所有副本角色
  • 列出房间所有者(队长)
  • 切换房间所有者(队长)
  • 启动关卡对应游戏

l 副本角色(与玩家角色类型绑定的模板类)

  • 根据玩家角色构建
  • 返回副本角色匹配数据:匹配数据为一个map类型自定义结构
  • 返回玩家角色对象
  • 准备就绪-锁定

l 关卡(与关卡数据类型绑定的模板类)

  • 关卡人数范围
  • 返回关卡对应游戏数据:游戏关卡数据为另外定义的特有类型,因此此方法应该为模板方法。
  • 启动关卡对应游戏(虚函数):输入副本角色(玩家列表),根据自身具体游戏数据启动。
  • 副本角色中断副本,离线保存(虚函数):当玩家断线或其他操作,系统调用此回调
  • 副本角色重新进入副本(虚函数):当玩家重新进入游戏,系统调用此回调

副本系统的整体调用流程:

可复用框架的必要性与可行性

总结

本文初步介绍了可重用的游戏服务器框架中的三层结构。其中底层的通用性强,技术含量高,关注非功能特性较多,事实上你可以用这个底层开发任何后台服务器,其对游戏最有意义的功能在于广播和复杂结构的本地数据缓存能力。而框架的中层结构,提供了大量的游戏业务领域的建模,这些建模也许不是非常详细,不够详细,但是如果我们坚持分层设计原则,并且以库和开放源码的方式去维护,那么这些中层模型将日渐强大,成为可重用代码的核心模块,让游戏开发效率飞速提高。游戏的高层模型更依赖于具体游戏内容,但是如果能在此框架下开发,那么其通用性将通过少量特异化修改来实现;同时这些高层模型,在一定积累和沉淀后,很容易的就能成为通用性更强的中层模块。这个转换并无任何硬性规定,而是随项目和使用团队(社区)的变化而变化的:当某个游戏设计成为“主流”,对应的实现代码就能成为可服用的模块——这正是整体框架想要表现的最重要功效。

转载请注明出处:https://stgod.com/2994/

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: