技能系统设计笔记 5

记录时间:2009年12月14日

接12月12日

      另外一个问题是操作与逻辑的一致性和即时性,例如本地玩家在执行一个技能时突然中断了当前操作,技能的逻辑也应该立即中断,后面的关键事件不会执行。看起来合理的需求在网络上实现起来就有问题了,因为中断信号也许不能即时的到达网络终端。实现时这带来了或多或少的麻烦。例如在技能开始后0.5″打断它,在网络终端上可能表现成0.6″才被打断,如果两个终端都进行逻辑计算,那情况就会变得更加严重。

      由于早期实现时两个终端都要进行逻辑计算,因此唯一的办法是保证两个终端的运行时间完全一致。我们使用客户端时间去更新服务器上的技能逻辑,虽然问题被解决了,但带来了更严重的设计问题,服务器的更新不在是时间唯一的了。有些系统需要用服务器的本地时间更新,有些系统又需要用客户端时间更新,这种不一致可能会带来更深层次的实现问题和安全问题。而且在更新频率上相信客户端也会带来很多安全性问题。   

      其实如果逻辑计算都统一到服务器终端,不同终端间的运行时间也不需要完全一致,我们完全可以使用不同的本地时间去更新技能逻辑,这样带来的缺陷必然是对操作上的体验会有一定延时,但不会造成逻辑错误和奇怪的设计。

技能事件和处理流程

      如果将技能设计为一个逻辑独立的系统,必然需要通过某种方式与其他系统进行交互。典型的交互行为为读取,修改其他系统的数据。当然从另一个角度来讲,其他系统也需要在技能的处理过程中得到某种通知,以便进行自身的逻辑运算(例如PK中每次攻击带来的其他逻辑关系)。设计的时候,我们将上述关系拆分成了两种行为:直接访问和间接通知。在技能系统读取其他逻辑数据的时候,采用直接访问的方式,因为这里不需要考虑时序(如果使用间接方式)问题,也不影响技能系统本身的内封闭性。而技能系统本身可以看成是一个大的事件处理器,根据时间和部分简单的外部接口(主要是操作层面),会源源不断的产生各种事件,并将这些事件通知到外围的各个系统。

607-41266476301845c9

      技能事件是技能系统产生逻辑的一个重要步骤,其实在实现层面上,技能系统被拆分为技能执行和技能状态两个模块,这两模块间也是通过技能事件来交互的。这里实际上有设计问题,下面再详细说明。技能事件大体上可以分为

enum Skill_Event_Type
        {
                SET_INVALID                             = 0,
                SET_POSITIVE_EVENT                      = 1,            //主动事件 脚本产生
                SET_INTERRUPT                           = 2,            //打断事件
                SET_ATTACK                              = 3,            //攻击事件
                SET_DAMAGE                              = 4,            //伤害事件 参数1:伤害逻辑ID 参数2:伤害类型 参数3:伤害值
                SET_BEGIN_CAST_SKILL             = 5,
                SET_END_CAST_SKILL                      = 6,
                SET_MISS                                = 7,            //未命中   参数1:伤害逻辑ID 参数2:伤害类型
                SET_CRITICAL_DAMAGE                     = 8,            //暴击            参数1:伤害逻辑ID 参数2:伤害类型 参数3:伤害值
                SET_KILL                                = 9,            //杀死           
                SET_HP_RECOVER                          = 10,           //HP恢复  参数1:来源类型        参数2:恢复值
                SET_MP_RECOVER                          = 11,           //MP恢复  参数1:来源类型        参数2:恢复值
                SET_HP_COST                             = 12,           //消耗HP事件 参数1:来源类型 参数2: HP
                SET_MP_COST                             = 13,           //消耗MP事件 参数1:来源类型 参数2: MP
                SET_ATTACH_STATE                        = 14,           //添加buff事件 参数1: 来源类型,参数2: stateTypeID
                SET_DETACH_STATE                        = 15,           //驱散buff事件 参数1: 来源类型, 参数2: stateTypeID
                SET_NEGATIVE_EVENT                      = 21,           //被动事件 脚本处理
                SET_BE_INTERRUPT                        = 22,
                SET_BE_ATTACK                           = 23,
                SET_BE_DAMAGE                           = 24,           //被伤害事件 参数1:伤害逻辑ID 参数2:伤害类型 参数3:伤害值    
                SET_BE_BEGIN_CAST_SKILL          = 25,
                SET_BE_END_CAST_SKILL            = 26,
                SET_BE_MISS                             = 27,           //未被命中  参数1:伤害逻辑ID 参数2:伤害类型
                SET_BE_CRITICAL_DAMAGE           = 28,           //被暴击
                SET_BE_KILL                             = 29,           //被杀死
                SET_BE_HP_RECOVER                       = 30,           //HP被恢复 参数1:来源类型        参数2:恢复值        
                SET_BE_MP_RECOVER                       = 31,           //MP被恢复 参数1:来源类型        参数2:恢复值
                SET_BE_ATTACH_STATE                     = 32,           //被添加buff事件 参数1: 来源类型,参数2: stateTypeID
                SET_BE_DETACH_STATE                     = 32,           //被驱散buff事件 参数1: 来源类型,参数2: stateTypeID
                SET_MAX_COUNT,
        };

将整个流程设计为事件触发是有好处的,因为它很大程度上降低了模块之间的嵎合度,而且也使逻辑流程简化。可惜的是整个游戏系统中的事件无法用技能事件完全描述,有很多其他系统产生的事件没有被包含到这个设计中

上面的设计其实还有一个比较严重的问题,就是技能执行和技能状态之间的事件机制,其实当一个事件产生的时候,事件双方的技能状态都会参与到这个事件的逻辑计算中,并以最终计算结果作为该事件的结果发放给其他系统。开始实现的时候没有按照这个逻辑来作,而是将原始事件本身发放事件双方的状态独立处理,这导致一个事件的最终结果对于双方来说可能是不一样的。

posted @ 2009-12-14 17:41  BadKeeper  阅读(717)  评论(0编辑  收藏  举报