虽然许多人觉得即时制MMOPRG玩起来更加流畅爽快刺激,但回合制MMORPG在
国内的网游市场上仍然拥有不少忠实玩家。个人觉得抛开某几款大红大紫的回合
制MMORPG带来的跟风效应来说,回合制的战斗系统有其本身的优点,低操作门槛
且较休闲的战斗模式下提供了可以很深层挖掘的配合策略。
好的回合制战斗系统需要:
* 平衡的数值分配
* 可控的属性增长
* 合理的技能设计
* 与技能互补的BUFF设计
* 有惊有喜的怪物AI
* 可控的装备系统设计
* 低门槛的基本操作
* 可深层挖掘的战术搭配
* 引导玩家沟通的战场内容设计
* ...
要全部做到位真不容易。
或许说远了,本文要说的是回合制MMOPRG的战斗系统程序设计。
而且,是非常传统的那种。
所以,下文会假设我心中的"非常传统的那种回合制战斗系统"是众所周知的*_#
---------------------------------------------------------------------
回合制战斗特殊性是:它将参战双方阵营都纳入一个看似副本的"战场"中进行
一整场属性互动;完毕后才可与真正的游戏场景中的其他复杂系统交互。
这个战场上的互动对象通常包括以下几类对象:
* 玩家角色
* 玩家宠物(召唤物)
* 怪物
* 战场道具(通常只指战斗背包中的药,功能性物品等)
而作为一个网络游戏,驱动这些对象进行互动的系统就只有两个:
* 网络协议系统(处理战斗协议的上/下行)
* 定时器系统(触发定时的时间片来驱动战斗状态机)
据上所述,设计战斗系统的程序基础框架时便可以有一定的原则:
* 基础模块关系清晰
似乎有点教条式了,但是,越复杂的系统越需要越清晰的模块关系。
自己造一座城,然后把自己困在里面,那不就悲剧了吗。
战斗系统可能是一个回合制MMORPG中最复杂的系统之一,因为最长时间的
玩家间连续有状态交互就发生在这个系统中。什么bug都可能出。
* 基础对象间复杂度差异尽量小
其实基础对象无非几种:
Fight(战斗对象)
Camp(阵营对象)
Host/Watcher(战士对象/观察者对象)
Pet(宠物或怪物对象)
AIChip(AI对象)
Buff(Buff对象)
Skill(技能对象)
Item(道具对象)
...
虽然不可能每个模块代码行数量一致。但是将复杂度差异控制到尽量小,
能使你后续对功能点的定位速度提高一个数量级;你肯定不愿意两年
内对为了对某个功能的修改而一次一次地对拥有10000行的fight.xx文件
vi + search上一万次。
一个有效的方法是引入更细粒度的新对象来分摊原有对象的功能点,
比如Camp中可引入 Map(站位网格信息对象) 来独立处理阵法站位映射。
* 绝大部分新需求只需要简单派生处理
基础模块中应该完成策划案中对战斗系统的通用需求。
而预留少量几个接口:比如 (1)初始化配置接口 (2)奖励接口 (2)惩罚接口
供派生重写。而预留之前应该与策划人员商定绝大部分的新玩法需求可以用
你预留的接口派生重写的方式简单满足。只有这样战斗基础模块才有价值。
* 对较特殊需求留有改动可能性的空间
做游戏不同于做一次性出最终版的传统软件。做游戏,做的就是反复。
所谓反复,可能是为了某个需求你得将整个构架反过来再复过去。
有人很热衷于重构么?如果有,那也是出于不得已。
将Fight包含的基础对象链中的class作为可配置的,是相对灵活的方式。
在类似Python, Lua等将class作为first class value的语言中,很方便。
* 将驱动方式抽离单独模块
回合制战斗的主要的驱动方式是 网络协议 的接收。
只所以要将驱动方式单独抽离,可方便地提供不同形式的驱动源输入。
或许你只希望一个单机版的录像文件驱动战斗?
或许你希望一个文本行格式的测试用例驱动战斗?
或许你彻底不需要任何形式的驱动?
* 必须给策划留下[极大的]配置空间
如果有可能,最好编写大量的代码生成器,并提供一个策划可接受的
编辑方式(比如 xls / csv 等基于单元格的格式)。
类似Skill,Buff,AI,Trigger,Equip等都交给策划来100%控制吧。
想什么时候改xls就什么时候改,改完运行一下代码生成器生成代码文件,
立即可以看到改动效果,节省不少策划<->程序的交流成本。
当然,这样做需要预先对Skill,Buff,AI,Trigger,Equip等的可配
置内容有策划<->程序间的讨论和约定;但很明显这是一劳永逸的事情。
* 可以在线热更新绝大部分战斗配置
配置,就难免出错。特别是由策划来配置。所以必须提供一个热更新
的机制使得服务器不停机便可以令新配置生效。所幸的对是现在绝大部分
脚本语言而言,做到这一点已经不是多难的问题。
* 核心代码不traceback
是程序都会有错误,没错。但是经过严格的编写和测试,我们应该保证
核心代码极少出现致命的错误。如果出现了,也别乱try/catch + log 让
它过去就算,让战斗系统挂掉吧,早挂早好。
* 配置代码即使traceback,也使影响尽量小
配置代码是代码生成器根据策划填写的xls生成的。可以列为"不受信任"
的函数块。战斗系统调用这些函数块时,应该做好它们会traceback的
准备。但是,即使它们出现traceback,也应该做一些事情使得对游戏世界
的影响尽量小,比如:(1)错误结束的战斗不给予奖励 (2)错误结束的战斗
至少能保证所有战斗中玩家能够退出战场返回正常游戏场景继续游戏。
(3)认真记好错误现场的日志。
* 严格时序的详尽战场日志记录
在上下文战场信息缺失的战斗日志中追查错误,事倍功半。
严格时序,所有战场事件有效信息的日志是必要的。至少前期是必要的。
最好每场战斗有单独不重复的战斗ID,每行日志有ID+回合数信息再加事件
信息。为错误的追踪查证提供有力的支持。
* 可重入的战斗测试用例机制
可重入是指同一份输入数据驱动两次独立的战斗,战斗系统会得到两次
完全一致的输出结果。如果战场日志足够详细,我们可以认为能得到两份
完全相同的战场日志(包括所有的掉血,命中率,暴击率计算结果值)。
实现可重入的战斗测试用例机制在回合制战斗系统中是可以做到的,
使用自定制的随机数函数,并在输入中将外部因素全部重置(其实外部因素
不多),然后驱动(这里就看出【将驱动方式抽离单独模块】的必要)直至
完成一场战斗即可。
* 可重入的性能分析用例
引入合适的profiler机制来对可重入的测试用例进行性能分析。毕竟战斗
在回合制MMORPG中是重头戏。能看出瓶颈的点应该尽早优化,持续优化。
---------------------------------------------------------------------