游戏动作感设计初探

http://blog.codingnow.com/2009/09/action_game.html

最近两年似乎大家一致的想把网络游戏向所谓动作感、打击感的这个方面推进。我想是因为,已经有太多人厌倦了打木桩吧。

我们在三年前就一直在考虑这个问题,但似乎走向了歧路。一个劲的考虑如何解决网络同步问题,怎样在一定网络延迟下保持公平,怎样避免作弊……

结果,尝试了多次后的结论就是,我们依然没有找到正确的方法解决以上问题,暂时把这一块搁置起来。最近,公司又有许多同事热切希望给游戏加入更多的操作感。我们又重拾这个课题。这次,换了个角度看这个问题:假设我们完全不考虑网络这个因素,就算做单机游戏,我们其实也没有太多动作游戏的设计实现经验。那么首先要解决的是如何把游戏的操作手感做出来,之后才应该考虑怎样在网络环境下实现它们。

为此,我纠结了很久。花了很长时间思考,以及编写了大量的代码印证,总算感觉自己的思路稍微清晰了一点。做一下记录,不一定正确,只是写出来或许可以给其他同僚提供一些思路。在内部讨论的过程中,这些也被广泛的质疑,许多人都不太认同。所以,一家之言,辜枉看之。


首先,所谓好的操作感打击感,和显示引擎的关系并不那么密切。如果指望引入物理碰撞系统、布娃娃系统、更好的动作融合模块,就可以一劳永逸的解决问题。让人物格斗以真实物理公式去运作,就能够给玩家爽快的体验,我认为不是正确的解决之道。

从最强调操作感的格斗类游戏来看,玩家的对抗运算,从来都不是建立在所谓物理碰撞等这些系统之上的。从格斗游戏的开山之作街头霸王,大学里,我们寝室间最为流行的真侍魂,到我最喜爱的 VF ,都有简单且严格的游戏规则。设计人员在那些基本规则上建立起平衡的游戏。前段时间我在 google reader 上[读到一篇文章《從快打旋風談格鬥�戲》,深以为然。

第二,做为需要操作感的游戏,一定是主要由游戏内在逻辑去驱动动画,而不是由动画驱动逻辑。所以不应该过于依赖画面最终的表现来做任何判定和处理。在以往的回合制游戏中,由于不强调操作,而偏向画面的动作表现,往往在动画数据中插入大量信息(比如关键帧反馈)。这是因为,当我们只关心每个动作的结果时,这样做能减少动作控制和外界的耦合。但,一旦我们希望提供更多的细节控制,即游戏设计的基本元素细于单个完整的格斗动作,再这样做就本末倒置了。

设计人员必定需要把握拆分后的动作片段的细节,才可以掌握最终游戏的平衡。而不是任由美术人员做出华丽的招数,然后根据招数的表现来左右最终的打击判定。

当游戏从 2d 过度到 3d 后,表现力变的更强、灵活度更高,我们的制作人员反而有点束手无策了。不知道该怎么控制 3d 人物动画,还转而去追求所谓物理碰撞等等原本属于渲染的枝节技术,我十分怀疑是否走对了路?当游戏中的动作画面是一帧帧画出来的时候,我们反而可以做出强烈的打击感,这是件没道理的事。看过铁拳、VF 等等 3d 格斗游戏,我们知道,一定是我们没找对方法,而不是 3d 的错。


关于动画动作控制。我曾经在 blog 上探讨过一次。实践过程中,由于设计依旧不够简洁,我们在互动性强的游戏中,去掉了引导动作的设计(因为本来就要做仔细的控制)。复杂的动作片段组合,用有限状态机来实现最好。状态机使得分片的代码可以分开来写,减少了耦合。并且,状态机是用纯数据表达的。如果条件成熟,还可以设计一种 DSL 来描述。不过在研究阶段,我们并没有完全肯定最终怎样表达最好,暂时我是用 lua 的表来描述的。这块数据可以根据游戏的具体动作设计来独立修改,能够交给非专业程序设计人员协同维护。这也使得单一程序源文件不至于过大。(流程数据化后,让数据和算法分离)

状态机的好处还在于,提高了动作控制模块的健壮性。让一些非法的状态切换很早就可以检测出来。减少把代码写成一锅粥的可能性。

比较难处理的是动作中的互动元素。即攻击者和受击者的动作配合问题。这部分逻辑,由攻击者对象管理或受击者对象管理都不自然。比较容易想到的是提到第三个独立模块去处理。另外,3d 或所谓 2.5D (我不喜欢这个不严谨的词) 游戏中,不同于格斗游戏的是,存在多打一的情况。不确定性更多。这些难点,我经过一些思考后,得到的方案是这样:

先抽象出“冲突(Clash)”这个最小战斗单元。一个“冲突”被定义成一对一的一个招式。一个冲突中一定存在一个且只有一个攻击者和一个防御者。冲突过程中,双方总是趋向于面对面,即处于一个一维空间(如果有跳跃的设定,则是 2D 空间)。在这个冲突空间中,双方能做的主要动作是前进和后退。这个前进和后退都可以是主动完成或被动完成的。冲突本身会因为招式,迫使双方保持在合适的距离。因为只有距离合理,才能更真切的表达出打击感。因为保持距离的需求,大多数情况下会逼迫防守者后退。冲突模块同时会不断的督促参与者调整自己的面向,保持正确的冲突空间。

每场局部战斗,在时间轴上都由多场冲突构成。在动作表现上,每个对象只能同时存在于一个冲突中(逻辑上,它还是可以受到多方面而来的伤害)。只有退出一个冲突,才能进入下一个冲突。每个冲突中,只要有一方宣布退出,那么冲突就结束。

最终,我们实现了一个冲突管理器,称为战场。以冲突的固定心跳频率(略低于游戏逻辑)来更新。每次更新时,会根据每个对象实体发出的消息请求以及优先级,来最终配对,构成一场场冲突,逐个处理。

细节不再展开。下面再谈另一个问题。

动作游戏中,我们不再简单的处理人物的位置。允许他们重合站在一个点上。因为位置对动作性非常重要。但是我个人比较排斥所谓堵门的设定。我希望更自然的处理游戏世界中碰撞的问题。

当然,我不会采用任何的所谓物理系统,(物理碰撞系统可以加入游戏,但不是在这里加入)。作为游戏设计者和实现者,我认为无此必要,甚至会画蛇添足。它让我不太容易把握游戏的平衡。我简单的把规则定为:允许对象的坐标重合,但是如有可能,它们都趋向于保持距离。结合上面提过的设定,再加另一条原则:游戏世界的每个对象都尽力完成自己应该做的事情,让这个世界趋向合理。比如:我被人击中会硬直一段时间,或是需要后退。这并不是攻击方强加给我的,而是通过冲突模块建议给我,以及动作控制的状态机建议给我的。然后我尽量按照建议来行动。作为游戏对象,一切事情都是主动完成,不分主动被动。

对象一个游戏对象,它的行动来源可以来至自身的状态机运转、来至于战场模块,来至于环境障碍,来至于环境的位置管理器。最终在一起决策,做出合理的行为。

至于 PC 和 NPC 的区别,仅在于外部对状态机的输入是 AI 还是控制器。


在实现这些东西的时候,我发现虽然我把事情分成了许多小模块逐个实现。但是我一开始并不能把握所有的需求(应该是因为我没有此类游戏的制作经验)。所以无法一开始就设计好全部接口。也不容易分工给不同的同事一起来做实现。

所以我选择了一种方案,每个模块间都采用消息通讯的机制。并且异步的处理消息。每个模块都认为外部有可能发生一些不合理的事情,而被允许过滤掉一些消息。同时也会认为自己提交的请求会被拒绝受理。这样,单独编写模块会相对容易。这个思路来至于 Erlang 对我的影响。

异步处理的必要性还在于,有许多事件虽然先后到达,但逻辑上需要相互决策。当我们解开小模块间的耦合,就失去了调整模块运作的先后次序的机会。这样,我们只好再把逻辑处理划分成时间片,让每个时间片的事件可以放在一起做一个协调。同时对象需要根据不断的状态变化,对自己的行为做一个反馈。(所以,我在实现冲突中被击打者后退的动作时,并没有由冲突模块准确发送应该后退多少,而是简单的发送一个“后退”消息,由持续不断的反馈机制来做到最终需要的效果

除了消息传递这个基本机制,我还给整个系统增加了另一个基本元素。被称为探测器(Detector) ,这个东西用来取代直接向对象索取数据。比如,我可以创建出一个对象位置的探测器,从中拿到对象在世界中的坐标。这个间接层我还没有找到足够的理由说明它和直接获得对象属性的区别和好处。但是直觉暂时告诉我,是有必要的。因为在现实中,我们了解到外部的一切都来至于我们的感官感受到的东西。也就是说,我们看到的东西仅仅是外部世界在视网膜上的图象投影,并不是外部世界的本质。Detector 就是模拟的这一点。

在模块划分上,对于这些不确定的需求,我倾向于按功能划分模块,而不是对象的实体。倾向于用非侵入式的方式扩展功能,而不是在一个类上不段的增加代码。比如,消息通讯对象就不是放在需要收发消息的对象内部的,而是独立出来,由对象自己去从通讯模块中找到属于自己的接口。因为我不希望有一个类不断的增加成员数据,庞大到不可收拾的地步。而且,在执行次序上,我更希望按功能去统一处理:形象点说,我希望处理完所有对象的视觉信号后,再处理所有对象的行动。把对象的视觉模块放在一起管理,可以更方便的做到这点。

posted @ 2016-08-19 15:38  00000000O  阅读(218)  评论(0编辑  收藏  举报