从多角度看易扩展的游戏技能系统设计一个好扩展的技能系统能让技能、BUFF、道具之间的关系能够很好的表示和调整,对游戏开发而言无疑是减少了不少的加班时间。那么要怎么设计一个易扩展的游戏技能系统呢?游资网从游戏策划、程序和玩家这三个角度进行整理:策划角度:作者:猴与花果山感觉这个问题很久不提了但是还是值得拿出来探讨下。我花了至少5年在设计和实践(我自己经历了大小7个项目,虽然大多最后因为其他原因都没上)这样一套机制并且可以说基本解决了这个问题!我并不想藏着他,我也和很多朋友分享了,同时也感谢他们提出了很多意见,包括细节和优化等方面,正因为互相之间的借鉴和交流,我到今天已经把这套机制归纳的非常好了,还是想把它分享给更多想做好游戏的人,希望大家能进一步交流,把它更完善化,以形成一种规范,这套机制适用于任何类型的游戏开发,因为他是一个很棒的思路。首先分析一下可扩展性:我从一个设计师的角度来看,所谓的可扩展性是——你并不知道策划下一个设计的是什么,但是你需要在尽可能不改动核心代码的基础上去把它实现了,并且在调试的时候(甚至是上线之后要做调整的时候),你可以并不伤筋动骨的去修改它。这里除了你要有好的代码规范外,还需要抽象一套机制来实现它。实际上,好的策划的脑洞是非常大的,你真不知道他会设计出什么样的技能,但你并不能说一些设计因为无法实现就理所应当被埋没了,(我对策划设计Dota类游戏的思路要求是开放的,发挥想象力的,因此我想了这样一个机制:[设计思想]游戏系统设计思路的牢笼一味追求实用性他们的核心在于:明确区别了AOE\Buff和技能3块,策划应该从这个角度出发思考问题。这既然是一套机制,你可以把它用在任何游戏的框架当中。比如我要做一个Dota传奇的卡牌游戏,一样可以用这套机制,但是核心在于——你的策划要有能力归纳出游戏中的回调点。当然我们在使用这套机制的时候,逻辑上实现并没有任何问题,但是我们一样会遇到一些从逻辑变成动画的困难,尤其是当我们的战斗在服务器上一瞬间完成了,但是要把结果告诉客户端吗,并且有客户端重新验算一遍的时候,因此我在之后有总结了一套作法,来完成这个事情:手游回合制游戏战斗机制归纳式设计这个解决的是,当你有各种有趣的buff,但是又想不大改客户端的时候,你应该这样去建立这个框架。你可以想象如果你做一个MT类型的游戏,战斗是服务器一瞬间的,但你又要客户端重演,我们举个例子:比如我门设计了在MT类型游戏中加入地形因素:可以有火海,火海每回合开始的时候对所有场上敌我英雄造成火焰伤害。然后有个英雄是一只凤凰,凤凰有2个被动效果:受到火焰伤害的时候变成治疗自己相当于伤害值的血量。战斗中第一次死亡可以复活,回复最大生命值50%,如果在火海中则回复100%。这样一个英雄和地形,我们如何实现呢?如果你看了我上面的几套机制,并且理解了,那基本没有难度,你根本不需要硬编码。但这里有个问题,我如何让客户端重现?这就是上面这篇说的关键了。程序角度:作者:扼杀黑暗技能没什么框架,只是有很多字段罢了。比如cd施法距离、释放动画、飞行动画等等。。。其实游戏技能不是一直不是什么难点,毕竟根据每个属性实现逻辑就好了。技能真正麻烦一点是其实是所谓的“效果”。因为从很久以前,游戏设计的时候就把效果这个概念添加进来了。对于游戏战斗对象主体,我们暂时叫做BattleAgent简称BA。影响BA的数据有很多,比如移动速度攻击力基础属性等等,影响的入口也有很多:技能buff/被动技能装备强化宝石魂等等,而这些实际上从影响结果没什么区别。首先我们先谈区别,对于这些数值影响,其实区别只有入口或者说是作用的方式,技能是BA(castor)对BA(target)释放造成的瞬间数值影响。buff是castor对BA(target)安装后造成的持续数值影响,分为按时触发瞬发和持续修改数值。装备是特定容器对BA持续修改数值。所以这里游戏开发者们抽象出了效果这个概念。对与效果而言,只存在2个行为:对BA产生数值影响对BA撤销数值影响所以效果最终定义为:interfaceEffect{voidcast(BattleAgenttarget);defaultvoidreverse(){}}而对于其他功能实体来说,就可以简化为效果的容器:interfaceEffectContainerextendsEffect{ListEffectgetEffects();}这样我们就只要定义不同效果容器就可以了,比如技能:classabstractSkillimplementsEffectContainer{publicvoidspellTo(BattleAgenttarget){foreach(EffecteffectingetEffects()){effect.cast(target);}}}对于buff:classabstractBuffimplementsEffectContainer{publicvoidupdate(){foreach(EffecteffectingetEffects()){effect.cast(target);}}}对于被动技能(其实也是buff):classabstractBuffSkillextendsBuff{publicvoidinstall(){foreach(EffecteffectingetEffects()){effect.cast(target);}}publicvoidunstall(){foreach(EffecteffectingetEffects()){effect.reverse(target);}}}装备同理被动技能,是不是很清晰?而对于复杂的技能效果,因为我们已经抽象出了Effect。所以怎么实现也就很容易了!classDamageEffectimplementsEffect{privateintdamage=100;publicvoidcast(BattleAgenttarget){target.hp-=damage;}}看起来是不是很简单,我们来写个变羊。这个技能包括2个效果外形修改和属性。1外形变羊classChangSheepEffectimplementsEffect{publicvoidcast(BattleAgenttarget){target.gameObject=GameManager.getAnimeObject(sheep);}}2攻击力和防御力变0速度变慢classPropChangeEffectimplementsEffect{publicvoidcast(BattleAgenttarget){target.atk=0;target.def=0;target.speed=50;}}就是这么简单,同学你明白了吗?如果要深入一点的话,就是变羊是持续型的,到了时间会变回来。所以我们要一个可以触发buff的效果:classTriggerBuffEffectimplementsEffect{BuffSkillbuff=newBuffSkill(){publicListgetEffects(){returnnewList().add(newChangSheepEffect()).add(newPropChangeEffect());}}publicvoidcast(BattleAgenttarget){inttime=3000;//3秒target.addBuff(buff,time);}}然后把这个TriggerBuffEffect加到技能能上就ok了,就完成了一个可以变羊3秒的技能。玩家角度:作者:梧桐这篇文章想表达的是我作为一个玩家,不是作为商业公司的员工,所谓的程序、美术或策划,对感兴趣的一些游戏技能体系的一些分析。这个要看追求了,没追求一张excel表配置上几百个参数,碰到一个需求加一个总能写出来;有追求甚至能和war3一样十几年前的游戏就可以在没有源码情况下做出dota(然而总有嘴强王者认为这没有什么大不了);又或者像我一样幻想有没有可能设计出一套集成GTA(模拟现实)+神之浩劫(moba)+虐杀原型(超现实动作)又可供玩家扩展的技能系统?技能是如此有意思的事情,精巧的、可扩展的技能框架简直可以说是游戏最大的乐趣,远不是一些泡菜网游里面一句一切皆buff就能把我打发了的,所以很久以来我对探索技能在各种游戏里面的实现乐此不疲,分享一点研究的线索。比较少谈具体的编码细节,因为就像我本篇最后说的那样,研究多了甚至开始反思技能这样一个概念到底存不存在,或者说不同游戏类型讨论的技能真的是同一种东西吗?如果游戏真的是现实生活的映射,那我们人类作为角色拥有技能吗?最简单的就是虚幻竞技场那种fps,感觉这种没什么好说的,虽然虚幻竞技场的改装枪是一个玩点,但是代码层面并不复杂。复杂点的泡菜网游rpg或者moba可以参考星际争霸的银河编辑器,风暴英雄就是用这个做出来的。熟悉war3不熟悉sc就下载ga的war3mod(魔兽争霸III2015),这是论坛头目在sc体系下完整的复刻了war3,可以观察war3里面的技能一个一个是怎么实现的,常见的比如暴风雪、空中锁链、变羊、风暴之锤等等,其实简单的的网游技能无非就是这些技能的变种。里面大体来说把技能相关的东西解耦成了如下模块,unit、action、order、ability、behavior、effect、actor,然后通过反射和连线配置来扩展游戏。简单描述这个体系,很复杂,也不求没研究过的人能看懂:ability就是通俗意义上的技能,包装成order被unit调用,ability本身不负责技能逻辑,只有使用ability消耗的资源(比如消耗多少魔法)前摇后摇之类的处理,他指向一个effect,在ability调用时候转发到effect身上;effect晚点说,先说behavior,常见的behavior类型就是添加一个buff,也就是对一个属性的阶段性修改,behavior是有状态的;回头说effect,effect最终组成了一颗effect树,effect常见的有发射投射物、应用behavior、周期性的调用下一个effect、对区域进行搜索然后对搜索到的每个对象进行调用effect、造成伤害、创建单位等,除了周期性效果之外的effect都是无状态的,只是简单的对逻辑进行一次函数调用。以上所有的一切描述都是纯逻辑层面的,而actor是表现体,所有的特效、动画都是通过actor监听以上所有对象的信号来做出相应的反应,而不是常见的动画驱动逻辑,换句话说,如果actor不监听逻辑层的信号,表现会有问题,但是游戏的核心逻辑还是照样跑,包括unit类其实都不包括任何的表现信息,只是在unit类创建时候通知actor创建并做表现。actor很类似一个完全被动驱使的客户端,其他模块是服务器。最后,在整个一套异步流程中,需要一些全局的类似ai中的黑板数据,记录比如施法者、施法位置、目标攻击位置、目标攻击对象等,由于effectbehavior的树状向下扩散,所以这个黑板数据其实也要不停地更换上下文,这个就有点复杂了。另外,表现层的actor的技能动作处理,也比较有意思,采用动作名称模糊匹配的思路:大概就是这样的:对角色当前各种状态进行采样(比如心情、天气、角色上一个状态名称等),根据状态类型优先级自动选择最合适的且存在的动作,比如如果资源同时存在Attack00Attack01动作,那就随机播放一个Attack,再比如风暴英雄里面普通死亡、爆炸死亡、天空击飞死亡播放不同动作也是在这个层次处理的。当然,动作模块的设计是个大问题,各大引擎中间件也经常是一个版本一个方案的大改,他这套方案也有局限性。好处就是用起来方便,ruby曰:约定大于配置。想象一下,当玩家新做一个路人角色,这个角色在看到美女时候就自动做特定的好玩动作,而这一切需要做的也许仅仅是,配置一个npc,给他一个特定名称的动作,比如lovestand。星际目前有296个关键字可以相互组合。再谈一个小细节,一般自研引擎都