记录一下回合制战斗系统的实现思路,想到哪里写到哪里。
回合制游戏的一般流程:
限定时间内
该回合战斗数据
播放战斗动画
直到战斗结束
以梦幻西游为例:
- 对于概率性的技能,输出一定是一个确定的结果(触发 / 未触发)
- 对于伤害数值,输出的是一个具体数字,并包含暴击等信息
方案 1:纯技能数据驱动
如果数据格式基于招式(技能,平A也可以看做一个技能),那么每个可攻击单位每次攻击行为的数据可能是这样的:
对于一次完整的战斗,可能涉及多达 20 个单位,每个单位每次攻击的数据约 220 字节,一个回合约 4KB,10 回合仅战斗动作数据就达 40KB。
方案 2:客户端参与战斗逻辑
针对方案 1 的缺点,方案 2 做了如下改进:客户端参与战斗逻辑处理,数值和概率等由服务器发送,其余结果可以在本地产生。即使出现外挂,也只影响该玩家自身的体验,关键数据仍由服务器掌控。
异步战斗是方案 2 的极端形式:战斗开始前,服务器已经把结果全部计算好,客户端只需模拟展现。服务器只传递概率性的已确定事件,客户端按数据快照(装备信息等)模拟出整个过程。
之前做的小游戏三国志OL就采用了这种方式。但对于更复杂的游戏(如迷你西游),单位攻击时可能存在反馈机制(反击等),数据结构需要用树形结构来表达因果关系:
无论是半自动还是全自动的回合战斗,都可以用这种数据结构来表示。关于具体实现还可以参考行为树框架——XYGame-AI设计4-行为树-第2版本。
客户端不参与技能逻辑时的处理方案
如果客户端不参与技能的具体逻辑感知,就需要包含具体的事件信息。来看几个典型场景:
这类在技能执行期间嵌入复杂逻辑的情形,可以通过配置表来配置技能的关键执行点或逻辑组合——添加或修改技能逻辑与数值,只需修改配置表。
处理手法:这类情况都具有很强的顺序性(触发反震一定是因为攻击),需要预读下一个技能,判断关联性:
通过配置表定义 3 个截断点:技能释放前、释放中、释放后。按服务器的验算逻辑序列化来看:
对于技能1触发了2个技能的平行关系,可以通过加权判定来决定执行顺序。这种方式本质上回到了行为树的框架思路——因果关系用子树表示:
录像系统的设计考量
录像只需保存这棵树。针对不同需求,有以下考量:
两种战斗模式分析
- Buff/Debuff 基于回合计算
- 梦幻西游:半自动,每回合可下达指令
- 迷你西游:全自动,异步战斗
- 服务器交互仅限于每回合等待指令
- 客户端可自由控制播放速度
- 技能和 Buff 按时间计算
- 偏向动作 MMO 的操作方式
- 技能释放完成即可下达下一个指令
- 客户端和服务器各维护一套 CD
- 对录像系统不友好
模式 1 引入 CD 时,可在每回合开始节点的快照中包含 CD 信息,客户端根据时间做插值。模式 2 若像一般 MMO 那样用消息处理 CD,录像系统则需额外做消息快照——或者客户端自己通过配置表模拟 CD,但一旦存在「减少全局 CD 50%」「立刻冷却所有技能」这类逻辑,就行不通了。
动作序列的解析与执行
把树形结构解析为一个个独立动作。之所以采用树形结构,是因为它能够表达因果关系。树的层次通常不高,宽度与执行动作的单位数量正相关。
逻辑处理器只需按生成的序列逐个执行。由于是一维信息,需要约定固定操作节点:
每遇到一个结束节点,表示一段因果关系结束。每段因果关系序列的父节点,总是导火索——比如"B 对 A 使用了反震"的导火索就是"A 对 B 使用了技能1"。
每个节点的执行状态:
解决思路:录像信息由客户端生成时,将未发送的信息也一并快照记录下来,录像播放机就变成了纯播放器。但如果客户端知晓了具体逻辑的代码(而非配置数据),这种做法就行不通——代码片段无法保存以供录像回放,虽然可以考虑变成脚本,但前提是脚本涉及的非脚本代码能保持兼容,几乎是一个恶性循环。