.hd-box .hd-fr

战棋类游戏核心玩法框架设计

2022-12-12 12:42千猴马的游戏设计之道(猴与花果山)20评

战棋类 SLG 是一个在游戏领域历史比较悠久的游戏类型,著名的有《火焰之纹章》、《梦幻模拟战》、《机器人大战》、《皇家骑士团》、《炎龙骑士团》、《高级大战争》、《风色幻想》等等系列,30 多年来战棋类游戏的佳作层出不穷。相信有很多游戏开发者入行时的梦想之一,也是开发一款好玩的战棋游戏。那么一款战棋游戏到底要怎么设计和开发呢?游戏策划和游戏程序员在开发一款战棋游戏的时候都要做一些什么事情呢?有没有好一些的战棋游戏的框架呢?

本文就将向大家介绍一套战棋类游戏的开发和设计思路,以设计一个灵活好用的战棋类游戏框架为基础展开,详细说明战棋类游戏的核心玩法开发过程中的内容。

游戏中的数据设计

首先我们还是要定义游戏中的数据结构,正如我经常说的“数据结构设计出来,游戏就设计完了一半”,因为数据结构来源于游戏的所有元素。当然我们知道不同的战棋游戏,在很多的细节上是有所不同的。比如火纹系列中,在后期作品中强调了邻接单位支援的特色,而在梦幻模拟战(非国产手游)系列中则强调了每个单位只有 10 滴血和指挥官带领士兵的感觉。但实际上他们的区别,仅仅在于策划在很多细节环节上的设计。

战棋类游戏的核心数据

在战棋类游戏的核心玩法,也就是战棋模式是由 4 大类数据支撑起来的,他们分别是关卡数据、地图数据、角色数据和动画数据。

动画信息

之所以会把动画信息放在第一位,是因为游戏的表现也是一个非常重要的环节,而我们在游戏中的任何设计,最终都应该返回出一段动画,供游戏表现用。比如每个回合开始的时候,我们就会运算一下,如果这个回合需要开始下雨了,那么就应该有一个开始下雨的动画表现,这就产生出一条动画信息,动画播放系统凭借这条信息开始播放下雨,播放完下雨之后才进入真正的回合开始状态。

动画信息的本质

动画信息在回合制游戏中,本质上都是一个 Timeline,即一个时间轴,这个 Timeline 是由若干时间轴上的节点组成的,开始播放 Timeline 的时候,时间推进,当时间推进到某个节点的时间点的时候,就触发这个节点的事件,如果这个节点是一个结束节点,那么 Timeline 就算完成了,进入下一步流程。

不论什么回合制游戏,Timeline 节点所必需的数据有:

在游戏程序运行的过程中,许多的计算结果,最后都应该会产生一个 Timeline,然后根据 Timeline,播放一遍动画,将结果展现给玩家看。正如我们上面说的那样,玩家肉眼看到的是一个火球被发射出来打中了目标,玩家就会联想到火球是一个实际存在的单位,他被一个角色发了出来,最后打中了目标产生了效果,但实际上这个火球和逻辑层运行的火球并不是一个东西,这只是做游戏最基础的视觉欺骗罢了,在动画播放之前,“火球”早已击中了目标,不然就不会有这段动画 —— 这就是回合制游戏的表现做法。

地图动画与对战动画

之所以要区分地图动画和对战动画,实际上仅仅是因为 Timeline 的事件取值以及处理对象不同。因为在一些游戏中,战斗都有一个特殊的表现模式,他更像是我们的国产手游中的自动战斗的战斗系统,只是表现一系列画面,将结果呈现给玩家,但是这个呈现模式却和战棋核心模式的呈现方式完全不同。当然也有一些经典的战棋游戏系列还支持地图上直接战斗,比如火纹系列、机器人大战系列、《七英雄物语》等等,如果一个战棋游戏的角色对战没有特别的表现模块,就不需要对战动画了。

(梦幻模拟战 2 中,进入对战后会进入另外一个“小游戏”的状态)

关卡数据

战棋游戏的核心玩法,通常描述的是一场战斗的过程,而一场战斗的完整过程,也通常是很多战棋游戏的一个关卡。在游戏运行过程中,我们需要一些数据来作为这场战斗的信息:

回合数

我们知道战棋类游戏必定是回合制的,所以会有一个回合数的概念,但是回合数这个概念也并非战棋游戏所通用。在传统的战棋游戏中,通常是一方阵营行动完毕之后轮到另一方阵营行动,比如在火纹、高战、机器人大战、梦幻模拟战系列中,都是如此 —— 玩家所有的角色行动完毕之后结束回合,轮到敌人行动,敌人行动完毕之后可能还有盟军行动等等。这样一圈下来轮到玩家再次行动的时候,就是回合数 + 1 的时候。

但是也有另外一些战棋类游戏,比如《金庸群侠传 OL》、轨迹系列的战斗模式、《梦幻模拟战 4》等都采用了 ATB 模式,即取消了传统的回合概念,根据角色的敏捷决定行动次序,一些速度快的角色可能行动了 3 次,速度慢的角色才行动 2 次,由此打破了传统的回合概念,因此在这里“回合数”这个属性就不存在了。取而代之的是 ATB(Action Time Bar)值,ATB 值也相当于一个“回合数”,只是这个数值会比回合数大很多,而且推进规则未必是每回合 + 1 的。

目标条件

即胜利条件和失败条件,尽管大多的战棋类游戏的大多的关卡胜利条件都是“敌全灭”,失败条件都是“我方全灭”或者“主角挂了”,但是依然会出现把所有人移动到某个单元格就能胜利、坚守多少回合就是胜利等等其他条件,一个关卡的胜负条件,是一条决定性的数据,因为我们要在每个角色行动完毕之后和每个回合结束(也可以是开始)的时候去检查这场战斗是否结束了(玩家胜利或者失败),也就是达成了目标条件。

(战棋游戏每一个关卡都会有自己独特的胜负条件)

当前行动阵营

通俗的说就是“现在轮到谁走”,通常战棋类游戏都是单机的,也有类似《英雄无敌》系列和《文明》系列有多玩家玩,这时候是轮流行动的。因此我们需要一个当前行动阵营,如 Player1,Player2 等来明确当前行动方。

关卡剧本

关卡剧本是一个比较容易被忽视的存在,但事实上绝大多数带有剧情的战棋类游戏都是有关卡剧本的。关卡剧本就是当游戏符合一定条件之后,会触发一些事件,这通常和整个游戏的数据都有关系,比如“在第 20 个回合,如果鲁大师还没被击败,则敌方骑兵部队将全体撤退”等,都是关卡剧本的内容。关卡剧本所必要的数据主要包括:

(战棋游戏每一定回合都会触发一些剧本,当然剧本不仅包括对话等剧情,还包括增员、移除部队、产生 AoE 等各种事件)

天气等一些游戏特有数据

除了上面这些主要信息之外,不同的游戏也可以在游戏中扩展一些其他的信息给关卡,比如《三国志英杰传》《三国志孔明传》《三国志曹操传》等中有天气系统,下雨天的时候不能施展火计,那么我们就需要一个天气属性来支持这个设计。

(三国志 11 中,火计的效果会受到天气和风力的影响)

地图数据

地图相对于战棋类游戏,就像棋盘相对于《中国象棋》,是一个非常核心的存在。通常在战棋游戏中,地图都是一个二维数组组成,即地图的 (x,y) 坐标对应的单元格是什么地图块,这样做的好处是通过数组下标就能找到对应的单元格。尽管一些游戏从视觉上来看是立体的,比如《皇家骑士团》系列《最终幻想战略篇》等模仿《皇家骑士团》的作品,但实际上他们的地图还是二维的,所以用不到三维数组,它们仅仅只是在在地图块数据中加入了一个“高度”的数值,这个数值会被寻路等系统所关心。

地图块数据

地图的二维数组的每一个元素,都是一个地图块(Tile)数据,地图块数据在通常的战棋游戏中包含了这些信息:

地图块数据是策划需要填表详细设计的数据,就如地图的数据一样,但是实际上在游戏运行时的地图数据并不总是一成不变的,根据游戏的进行,可能会触发一些变化 —— 比如村庄被山贼摧毁了(从一个好的村庄地形块变成了一个被摧毁的村庄地形块),宝箱被玩家拿走了(从一个关闭的宝箱地形块变成了一个开启的宝箱的地形块)等,这些信息都不应该被放在地图块里,而是放在地图信息里,其行为结果是改变了地形块数据。

(左下角的“平地”正是当前光标所在地图块的效果显示 UI)

AoE

AoE 即 Area of Effect 的简称,其概念来源于魔兽世界的玩家称呼。它表达的是在某个范围内的事件,比如陨石撞击地面,会产生一个十字单元格(中心格 + 上下左右一共 5 格)的火海,首先地图块将会变成被砸过的地面,然后火海的火焰就是 AoE,对于 AoE 范围内的所有角色造成伤害。因此在一个战棋游戏中,是有 AoE 存在的必要的,不光是因为技能,还会因为关卡设计,比如这个关卡会有火山喷发等,都会需要产生 AoE 来丰富游戏的内容。AoE 在战棋类游戏所必要的属性包括:

AoE 是一个非常核心的设计内容,如果使用得当会让游戏的内容极大程度的得到丰富。当然完全不用 AoE 也不是不行,经典的战棋游戏中的确有很多是没有用到 AoE 的。

角色数据

角色相对于战棋类游戏,就如棋子相对于中国象棋。角色是非常核心的元素,不论是敌人还是玩家的角色或者是 npc 什么的,但凡是可以参与游戏中的对战等角色规则的元素,都是角色。

(角色是战棋类游戏的核心元素之一)

阵营

阵营往往是被策划所忽略的一个设计,但实际上在战旗类游戏的每一个关卡中,这又是一个不得不设计的元素,尽管它本身非常简单。所谓阵营,直白的说就是“哪些人是一伙的”,比如玩家所在的就是一个阵营,而敌人所在的也是一个阵营。但是一个战棋游戏,往往并不是只有两方,可能还会有类似“我方援军”的存在,这样就形成了至少 3 方对战的可能性,但是作为我方援军,为什么不会攻击玩家而只会攻击敌人呢?这就是由阵营关系决定的,阵营关系决定了跨阵营角色之间的一些权限,比如是否可以攻击等,常见的阵营关系(权限)包括:

阵营关系是双向的,即 A 阵营可以攻击 B 阵营的时候,B 阵营未必有权攻击 A 阵营。当阵营关系确定的时候,通过角色属性的“阵营”值,就能确定角色之间的关系了。

(用不同颜色表达不同阵营已经是玩家非常熟悉的表达手法了)

角色

角色的数据根据不同的游戏会有所不同,但是基本只要是战棋游戏,都应该有以下几个类型的属性:

最终我们把这些属性传给一个脚本函数,由这个脚本函数返回一个角色属性 struct 回来,作为游戏中角色所使用的“当前属性值”,而这个脚本函数,也正是数值策划要设计的角色属性关系。

根据游戏的具体设计不同,还应该扩展出很多很多属性,在这里就不一一列举了。

(当然,除了上面提到的属性,根据不同游戏的细节设计,还能增加出很多角色属性来)

Buff

通常在玩家的理解中,buff 是指一个角色的增益或者减益状态,甚至可以简单到只有角色属性的变化。但是对于游戏逻辑本身来说,buff 更像是一种特殊的标志,在角色身上存了的这些特殊标志,将在一些流程中起到一些逻辑作用。比如最简单的中毒状态,就是在每个回合开始时这个时间点,对于 buff 的携带者这个角色,造成某个公式算出来的一个伤害值。通常初级游戏策划和玩家所“设计”的一些技能中,常带有“暴雪更新文档”式的描述,比如“每回合对角色造成 50 点伤害”,但是作为专业的设计师,我们的概念里不应该是“暴雪更新文档”一样的东西,而应该把它们转化为可以用来实现的“buff 机制”。就比如“烈焰对于燃烧中的角色造成双倍伤害”,这是标准的“暴雪更新文档”范式,而我们设计师的大脑中应该是怎样的?是“有一个 buff,这个 buff 是一个标志,这个标志的作用是,在被击(BeHurt)的触发中,如果触发源(详见伤害信息)是烈焰,那么就将伤害翻倍”。

在战棋类游戏中,buff 通常需要的属性包括:

在这里,我列举了 3 个最常用的触发点,这不代表 buff 只有这个 3 个触发点,根据游戏设计的具体需要,设计者应该在流程中去定义清楚触发点,比如我要免死金牌,就得有角色被击败前(BeDefeated)的触发点,如果想要击败敌人经验翻倍,就要有 (OnDefeat) 等等。但是值得注意的是,千万不要定义太多,想清楚了再要(比如需要某个触发点,先想 3 个用法在增加),因为触发点越多,尽管设计越灵活,但是游戏运行效率也就越差。设计好触发点,是游戏设计早期的核心工作之一。

伤害信息

伤害信息既是一次即将发起的伤害(通常来自于攻击)的详细信息,通过这个信息,伤害处理系统走一次伤害流程,产生伤害和结果。这通常是一个被完全忽略的存在,即便是很多老的经典游戏,都会直接运算伤害而不需要这么一个“多余的结构”。但是随着游戏开发经验的积累,这个伤害信息会变得越来越必要。它的必要性在于游戏中拥有这么多的变数(由策划设计产生),而当这些变数组合在一起形成“build”的时候,会非常可怕,比如策划给一个角色设计了“风怒”,攻击的时候有 30% 的几率产生额外 2 次攻击,而他攻击的目标也被添加了一个 buff,受到伤害的时候,会把这个伤害分散在“灵魂链接”的角色身上,而他“灵魂连接”的角色有一个 buff,是受到伤害时,攻击发起者也会受到相当于这个伤害 30% 的伤害…… 类似这样一环扣一环的设计,玩法上是存在乐趣的,所以策划的想法应该被支持,因此我们需要一个很好的管理机制。

伤害信息 (DamageInfo) 中的主要属性包括:

除了这些必要的信息之外,根据游戏具体设计,还可以扩展一些其他需要的信息,这里就不展开说明了。

值得一提的是:游戏中任何一次“正常的”伤害请求,都应该产生一个“伤害信息”,而非直接产生伤害,因为这样可以避免一个错误的递归。我们举个例子:一个目标身上有 6 个 buff,其中第三个“受击时”效果是可能遭受额外一次攻击,伤害力为本次的 40%,这时候这个“受击时”要做的并不是直接“扣血”,更不是直接把伤害信息的伤害值提高 40%,这都不是策划所想,策划想的时候再产生一个伤害,这个伤害也走一次所有的 buff 的流程,而更重要的是,这次伤害应该发身在另外 3 个 buff 执行“受击时”之后,以及其他已经注册的伤害信息之后,这是一个流程问题,所以此时应该是产生出一个新的伤害信息。

(角色每一次攻击都能产生一个伤害信息,无论命中与否)

游戏的状态和流程

当我们定义完游戏的数据之后,游戏所有的元素就有了,思路都清晰了,接下来我们就要把他们串联起来。在这里开始,就要进入程序去实现这个玩法的流程了,但是这并不代表这其中没有需要策划去设计的部分,也因此策划应该清楚的了解游戏是如何实现的,尽管不必去 coding。接下来我们就从状态到流程去详细说明一下战棋类游戏的开发细节。

在这里,我们以传统的阵营轮流的战棋游戏为主展开,ATB 类将不做完整说明。

战棋类游戏的主要状态

回合开始状态

在每个回合开始时候的状态,在这个状态主要的工作有几项:

我们可以看到上面这些事情都是在每个回合开始的状态下要去做的,而这些事情的先后顺序是有讲究的,策划应该设计好事情执行的先后顺序,因为不同先后顺序会带来不同的执行效果。最常见的例子是一个在“恢复点”上的中毒的角色 —— 他需要受到治疗和损失生命,那么先损失和后损失,就取决于这个顺序。“恢复点”的本质,是因为这一格坐标上有一个范围为 1 格的 AoE,他每回合都会治疗范围内的角色,而中毒则是角色身上的 buff,在每个回合对角色进行伤害,因此先执行 AoE 还是先执行 Buff 将会带来完全不同的结果。同样的如果角色身上有着火 Buff,每回合要受到火焰伤害,而游戏有个天气系统,如果下暴雨就会灭掉所有角色身上的着火 Buff,那么究竟这个回合着火会不会伤害角色,取决于策划设计的执行顺序,是天气先执行(不会伤害)还是 Buff 先执行(会伤害)。

当回合开始的所有事情完成之后,会产生若干个 Timeline(可能会很多),这时候需要一个 Timeline 的合并 (merge),即得有一个规则把这些 Timeline 串联起来,是一个接着一个,还是并行,或者符合一些条件的串联,符合另一些条件的并联 —— 这些也都是策划需要和美术共同精心设计的。

最终,程序这里会播放合并出来的 Timeline,播放完毕之后,进入下一个状态 —— 选择角色状态。

选择角色状态

选择角色状态实际上有 2 种情况:一是玩家行动,则是玩家开始下棋,观看地图选择角色的状态,是一个等待玩家发令的状态;另一个则是非玩家行动,那么在这里将会遍历地图的角色,找到第一个符合这个阵营尚未行动的角色,执行 AI,然后切换到后续状态,如果找不到这样条件的角色,就会进入回合结束状态。

这里需要进一步说明的是,玩家行动状态的显示(display),并非一尘不变的,这根据游戏的设计需求,会存在很多需要修改显示的细节,比如“显示当前选中角色的移动范围”,再比如“显示所有敌人攻击覆盖的范围”等,这些都只是显示的东西不同,并不需要一个特别的状态。

如果是玩家的行动,当玩家点选一个具体的角色的时候,就会进入角色详细信息的 UI,此时就需要切换到“角色详细信息状态”,这也仅仅只是一个“操作锁”的作用,具体需不需要“角色详细信息状态”可以根据实际的 UI 设计来决定。

当玩家行动并且点击一个自己的角色的时候,如果这个角色尚未行动,就会进入“选择移动范围状态”。而如果是 AI 脚本,他是不需要“选择移动状态”这个状态的,可以直接切入到角色移动状态。

(火纹 if 中的选择角色状态)

选择移动范围状态

在选择角色移动范围的时候,我们就需要依赖地图、角色和阵营 3 大块数据,来获取一个角色可以移动的范围(事实上大多 UI 设计中,这在“选择角色状态”也要用到,这里只是具体介绍一下这个方法)。角色的移动范围是一个标准的 Dijkstra 算法,但是也有一些与常见的 Dijkstra 算法在游戏领域运用的文章中描述不同的地方:

选择移动范围不光是一个显示移动范围的问题,除此以及确定能否移动以外,还有一个轨迹问题,可能存在一些“地雷”情况,因此角色走过的路径非常重要,如果碰触“地雷”,比如火纹、高战等系列中阴雨天看不见敌人单位,如果下一步会走到敌人单位所在地图块就会被强制停下,因此如果有这样的设计需求,就要记录玩家输入的走路过程。如果没有特别需求,则可以和 AI 一样,使用 AStar 寻路来行动。

AI 选择路线,则是根据一个类似行为树的 AI 脚本产生出移动数据。之所以说是“类似行为树”,因为他只是看起来像行为树,本质上完全不同。AI 脚本是由若干片段组合而成,每一个片段的内容都有:

最终会有一个 AI 脚本片段在最后,就是“无条件待机否则待机”。而“待机”究竟是怎么回事儿呢?从数据上来看,这个返回的移动数据(实际上玩家操作也是得出这个信息)包含的数据为:

当行动路径产生之后(玩家选择移动方案、AI 运算出结果),就会根据行动路径和角色,产生一个 Timeline,并且进入角色移动状态。

(火纹 if 中的选择移动范围状态)

角色移动状态

角色移动状态,实际上是一个“操作锁”,在这个状态只是改变一些操作权限,然后播放一个角色移动的 Timeline。

选择目标状态

这个状态的有无,取决于游戏的 UI 设计。在一部分战旗类游戏中,角色行动完毕后选择“攻击”等类似指令之后,才能选择目标,因此要有一个状态让玩家选择。

(曹操传中选择策略目标)

信息确认状态

这个状态也取决于 UI 设计,但是大多战棋类游戏,在单位进行对战之前,都应该显示一下对战细节和一些简单的数据分析,提供给玩家情报,以让玩家来决策是否真的开始对战。

(给玩家一些信息以预判对战的结果,从而决策是否开始对战)

对战状态

通常来说对战状态是攻击者向目标发动攻击,然后进入一个对战画面或者在地图上直接表演战斗的过程。但是随着游戏设计的发展,许多战棋类游戏在对战环节也出现了很多新的花头,比如火纹系列的支援系统等,使得对战已经不再是简单的“单挑”模式了。在今天,战棋游戏的对战模式的复杂程度已经不亚于很多国内的手游产品的核心战斗系统了。那么一个对战系统需要做些什么事情呢?

当对战结束后,会返回到选择角色移动状态,根据游戏的规则,决定这个发起攻击的角色是行动完毕(本回合行动完毕设置为 True),或者一些特殊条件角色可以继续选择移动(比如火纹系列一些作品中骑兵打完可以继续移动)。只有当玩家确定(或者自动帮玩家确定)这个角色行动完毕的时候,才会执行一次“检查关卡条件”的工作,来判断游戏是否分出了胜负。

(火纹的最近几座,都非常强调支援系统,甚至在对战中都已经跳出了 1 对 1 单挑的形式)

伤害流程

伤害流程是对战的核心所在,同时伤害流程也是本文中的一个关键点。在绝大多游戏中,伤害流程总是开始被忽略的,到后面会被改的越来越复杂的,而在本文中归纳的这套被称为“buff 机制”的做法中,伤害流程会是一个非常简单而明确的流程:

在这个流程中:

整个伤害流程是这样的,我们将一些“额外特殊处理”,比如“攻击后吸血”之类的丢出伤害流程,丢给 buff 的脚本去完成。原本我们是判断 if (吸血标志)else if(其他标志),现在全都由这些 buff(即可以被理解为标志的数据 struct)来承担了,由此也跳过了很多不必要的遍历(比如不会吸血的人伤害流程里就走不到 if 吸血标志),所以性能上只高不低。而在这个流程中扩展出更多的 buff 触发点,也是丰富战斗的关键,比如在角色被击败之前再次执行一些 buff 来看是否能成为“免死金牌”等,这些都应该是策划在设计游戏开始时候去想清楚的。

(我们能看到的简单动画背后,藏着复杂的对战逻辑,也正是因为这些“复杂”,为游戏带来了更多有趣和多样的内容细节)

回合结束状态

当玩家主动结束回合,或者根据算法(包括 AI)得到结束回合请求的时候,就会进入结束回合的状态,结束回合的状态主要做几件事情:

回合结束状态是一瞬间的,玩家几乎无感,但逻辑上他是实实在在存在的。

结尾

由此,一个战棋类游戏的核心玩法框架设计就完成了,基于这个框架,可以扩展出许多更精细的玩法,这就是游戏要进一步涉及的地方,但是框架到此为止了,框架的意义在于后面的扩展不会对代码和逻辑结构产生破坏性的影响,而不是约束设计者创作。而到此我们也差不多该清晰的定义一下分工了:

程序在这个模块的工作

策划在这个模块的工作

到这里,开发就可以顺利展开了,也真正的做到了“流程不通找程序,效果不对找策划”。

本文来自微信公众号:千猴马的游戏设计之道 (ID:baima21th),作者:猴与花果山

广告声明:文内含有的对外跳转链接(包括不限于超链接、二维码、口令等形式),用于传递更多信息,节省甄选时间,结果仅供参考,IT之家所有文章均包含本声明。

下载IT之家APP,分享赚金币换豪礼
相关文章
大家都在买广告
热门评论
查看更多评论