手游的回合制游戏,我就不多介绍了,最近的《秦时明月》,早些时候的《我叫MT》等。从一个单纯的策划角度来说,你可能觉得开发这样的游戏,为战斗过程中加入一些有趣的buff、有趣的玩法会更有趣,但事实上在实现过程中,因为种种原因最终都没了下文,其实核心原因很多时候是在策划对于游戏设计的规划上出现了问题。

  首先我们来畅想一下,一个秦时明月类型的游戏,或者我叫MT类型的游戏,其中有什么背景角色,无所谓,我增加一个卡牌可以吧?一个英雄——伊夫里特(我想这个够出名了吧,不知道就白度去吧),为了体现这个卡牌的价值,作为游戏设计师,我希望加入一些特殊功能,让这个卡牌或者英雄与众不同,那么如果游戏允许加入被动特技,伊夫里特的被动技能我想可以是:受到火焰伤害的时候,免疫受伤效果,将原本伤害的一半转化为治疗。这是一个很刁的效果,至于伊夫里特因此会被定义为多少星什么颜色怎么个价钱的英雄,这里不作讨论,核心是,要让这玩意儿发挥,我们还可以为游戏的战斗加入地形影响,你可以想象,当战斗进行到第3回合开始,场景发生了变化,开始着火了,每回合对所有角色造成40点火焰伤害,而此时你的伊夫里特,相当于每回合恢复20点生命,因为战场的需要,让伊夫利特再次增值。

  我相信从设计的角度来说,这样的idea是绝妙的,因为它可以让游戏增色不少:

  1,我们有了英雄的被动,让英雄除了数字以外,更加体现出独一无二性。

  2,我们有了地形、天气系统,让战斗战场更具特色,火山口和城堡内相比,不再只是背景贴图变化了,火山口会着火,就像城堡内会有乱箭射出。

  3,英雄被动配合地形,让养成更多的英雄有了意义。

  以上这种脑袋随便一拍就能想到的主意缘何无人能做出来呢?事实上我们真的在动手的时候,会发现一个核心的问题——当我们在服务器上作数据计算的时候,又如何让客户端来重演一次服务器的计算呢?

  事实上这个问题大家解决的办法都是一样的——

  1,策划归纳好战斗中会发生的事情。

  2,客户端把这些事情都写好,就犹如写一段脚本一样,然后服务器告诉客户端发生了什么事情,客户端去重现,一个(或多个)回合的战斗,都能序列化为一个1维数组。

  我们就拿MT来举例:

  第2步策划工作,策划总结出来有这样几个情况:

  1,角色发动攻击:造成单个角色受到伤害、死亡。

  2,角色发动大招:根据大招造成若干个角色受伤、死亡。

  3,角色获胜:战斗结束。

  第2步显而易见,我们做个简单的演算:

  1,A1角色攻击B1角色,造成300点伤害。

  2,B1角色攻击A1角色,造成150点伤害。

  3,A2角色攻击B1角色,造成200点伤害,B1角色死亡。

  4,战斗结束。

  很简单的1维数组做到了。的确,在这种结构下,在复杂一点增加大招也没问题,包括秦时明月也是如此。当然,我们很多策划都能用Excel做出这样的战斗模拟。那么仅仅使用这样的机制,是否能够实现我们之前说的伊夫里特的扩展呢?我相信这个没问题,因为那并不是很头疼的事情,因此我们让策划把想法更进一步的扩展一下:

  我们的策划又设计了几个英雄,他们的数值我就不管了,我只来说说他们的被动:

  1,盾牌哥哥,被动——格档:受到的伤害若小于300,则完全抵消。

  2,骑士姐姐,被动——护卫:每回合我方后排角色受到的第一下伤害,都会被骑士姐姐援护掉,骑士姐姐受到该伤害50%的损伤。

  你要知道,光是这两个英雄的被动,是无法满足大佬们的,因为大佬们知道,主流赚钱的卡牌游戏中,还有一个叫做“缘分”的东西,那么盾牌哥哥和骑士姐姐如果有缘我们怎么做才是有趣的?是他俩一起上互相增加20%防御力么?我觉得更好的设计是:

  1,当骑士姐姐护卫盾牌哥哥的时候,若最终收到的伤害小于600将被完全抵消。

  2,当盾牌哥哥在场时,骑士姐姐受到攻击后可以攻击3个目标,而不是单体,受到攻击的同时会提高盾牌哥哥防御力20。

  假如一个程序员并不了解我很早以前就说过的buff机制及其实现原理的话,在看到这个设定的时候,他足够有判断能力的话,就会想到——你是一个异想天开的设计师,和你合作的话,会有更多意想不到的效果要去实现,你有了盾牌哥哥和骑士姐姐,那一定会有想都想不到的牧师弟弟和战士妹妹,他会果断的告诉你这玩意儿做不了,最后你的游戏成了MT,只有你拍一我拍一,所有效果都和伤害挂钩。

  假如一个了解和熟悉我的Buff机制的人会去如何做这些逻辑呢?事实上很简单,回合制游戏中最佳的buff回掉点就只有这么几个:

  1,回合开始时:在每个回和开始时执行,比如HoT技能、DoT技能,对于一个回合制游戏来说,回合开始时生效是最合理的(ATB游戏不在此讨论范围)。

  2,角色攻击时:当角色攻击命中每个目标的时候回调,用于左右最后的攻击效果,如造成爆击时吸收造成伤害50%的血量恢复自己。

  3,角色受击时:当角色被攻击时的回调,用于左右最后的攻击效果,例如盾牌哥哥的,受到伤害低于300时,受到伤害=0。

  4,角色击杀前:当角色即将死亡的时候发生的事情,比如我们设计了一个技能叫手下留情,她的作用是永远不会把目标生命打到1以下(请别在这里思考为什么设计这么一个技能)。

  5,角色死亡后:当角色被击杀时发生效果,如:每杀死一个角色获得一层狂怒,增加伤害30%。

  6,Buff结束时:在Buff结束时候做出的回调,比如:3回合后召唤陨石攻击所有场上的角色。

  事实上,Buff机制合理运用,在逻辑层上,是完美无缺的,这些效果都是轻而易举就能实现的,但是这里还是有一个核心的问题,也是最难解决的问题——我如何将这个回合的战斗告诉客户端?

  我们继续演算盾牌哥哥和骑士姐姐的浪漫,他们在冒险的过程中遇到了3个怪物,史来姆ABC,史来姆ABC又都带有特技:

  史来姆A:受到伤害时有20%的几率反弹30%的伤害量。

  史来姆B:受到伤害固定为1。

  史来姆C:每当一个史来姆受到攻击的时候,叠加一层粘液,每层粘液提高自身5%行动速度,但降低5%伤害,每3层粘液可以使攻击产生一层风怒,每层风怒可以使攻击产生额外一击。

  战斗开始了,会发生什么?我们大概演算几步:

  1,回合1开始,盾牌哥哥攻击,史来姆B受到了1点伤害(582点被吸收)。史来姆C获得了1层粘液。

  2,骑士姐姐攻击,史来姆A受到了伤害447点,史来姆A发生了反击效果,骑士姐姐受到伤害134点,骑士姐姐得到了顺势劈的效果(下次攻击命中3个目标),盾牌哥哥得到了提起护盾效果(防御力提高20点)。史来姆C获得了一层粘液。

  3,史来姆A攻击,骑士姐姐护卫,受到0点伤害(177点被化解),骑士姐姐获得了顺势劈效果,盾牌哥哥得到了提起护盾效果。

  4,史来姆B攻击,盾牌哥哥受到了0点伤害(22点被化解)。

  5,史来姆C攻击,盾牌哥哥受到了742点伤害,盾牌哥哥给跪了。

  6,回合2开始,骑士姐姐攻击,史来姆A受到了491点伤害,史来姆B受到了1点伤害(442点被吸收),史来姆C受到了222点伤害。史来姆C获得3层粘液,史来姆C获得一层风怒(下次攻击2段伤害)。

  7,史来姆C攻击,骑士姐姐受到615点伤害,史来姆C风怒攻击,骑士姐姐受到了667点伤害,骑士姐姐给跪了

  10,战斗结束,玩家战败。

  我们从上面这段演算,可以看到这样一个复杂的流程:

  

手机游戏回合制游戏战斗机制归纳式设计

  这个相比一条线下来的MT模式来说,复杂了太多太多。他完全是多条线发展的,那么我们在这个过程中再看看,我们通常所采用的那种归纳方式还有意义吗?我们仍然可以把每一个方块发生的事情归纳起来,因此我们有这些事件:

  1,攻击:某角色攻击了另外一个角色。

  2,反击:某角色反击了另外一个角色。

  ………………

  你会发现这样归纳,几乎每一个方块都是一个东西,目前只有5张卡牌,如果我们有个哥不林小分队,天知道天才的设计师们能想出什么花招。最要命的是,这么多东西,我们让客户端怎么去重现呢?事实上,最困难的地方,不是将逻辑重现给客户端,而是在逻辑的基础上,我们还有很多表现要传递给客户端,这才是最头疼的,也许一个方块内的事情可以归纳为:

  一次攻击:谁攻击了谁一次,造成了伤害多少。

  但事实上从一个优秀设计师的角度来说,这应该归纳的并不是一次攻击,而应该是:

  1,攻击者发动了某个视觉特效(如果游戏规定攻击前要播放一下)

  2,攻击者做出了攻击动作

  3,受击者身上出现了受击特效,受击者同时做出了受伤动作,跳出了伤害数字。

  根据这个归纳,我们就有了这么3个事情(姑且称之为事情,Thing吧)

  1,某个角色身上播放了一个特效。

  2,某个角色作了某个动作。

  3,某个角色身上跳出了数字。

  我们看,假如你这样归纳的话,用在其它地方是不是会更合适?我的意思是,你并不需要增加很多未知的东西,比如我们可以试试看最长的第2段:

  1,骑士姐姐的攻击:播放特效-〉攻击动作-〉受击动作-〉跳数字-〉受击特效

  2,史来姆C获得Buff:播放特效-〉跳数字(确切地说是文字)

  3,史来姆A反弹:播放特效-〉反弹动作-〉受伤动作-〉跳数字-〉受击特效

  4,盾牌哥哥获得Buff:播放特效-〉跳数字

  5,骑士姐姐获得Buff:播放特效-〉跳数字

  利用这样的归纳方法,我们很容易的就把整个一段很特殊的5个事情归纳成了3个已经归纳过的动作。

  在这个基础上,我们很容易就能确定出,服务器应该如何整理这个结构用来告诉客户端(Haxe)

  首先我们需要的是一个总管,来实现上面的树结构:

  class BattleAction

  {

  public function new(_thing:Dynamic)

  {

  thing = _thing;

  nextFunc = new Array();

  }

  public var thing:Dynamic; //一个动作的描述

  public var nextFunc:Array; //我的后续动作数组

  }

  

手机游戏回合制游戏战斗机制归纳式设计

  值得注意的是,除了Root部分(也可以说是第一层枝)这里,我们需要按顺序来执行外,其他的地方,只要是同一支展开的,都是同时执行。我们通过这样一个结构,整理出所有的Thing,形成一棵大树丢给客户端。而BattleAction中的那个Thing(Dynamic),便是每个动作,还是借着刚才的举例:

  1,角色做动作

  class BattleThing_ChaDoAction

  {

  public function new(_chaUid:String, _actionId:Int)

  {

  chaUid = _chaUid;

  actionId = _actionId;

  }

  public var chaUid:String; //哪个角色

  public var actionId:Int; //什么动作

  }

  2,角色播放特效

  class BattleThing_PlayAnimOnCha

  {

  public function new(_onChaUid:String, _effectName:String, _dummyPos:Int, _playTimes:Int = 1)

  {

  onChaUid = _onChaUid;

  effectName = _effectName;

  dummyPos = _dummyPos;

  playTimes = _playTimes;

  }

  public var onChaUid:String; //谁身上播放

  public var effectName; //特效名

  public var dummyPos:Int; //绑点

  public var playTimes:Int; //播放次数,Magic:-1为循环

  }

  3,跳数字

  class BattleThing_PopTextOnGuy

  {

  public function new(_chaUid:String, _text:String, _fontName:String)

  {

  chaUid = _chaUid;

  text = _text;

  fontName = _fontName;

  }

  public var chaUid:String; //谁身上

  public var text:String; //什么内容

  public var fontName:String; //什么字体

  }