Badkeeper 的开发日志
作者QQ:2163410 There is no formula for success, except perhaps an unconditional acceptance of life and what it brings.
posts - 36,  comments - 70,  trackbacks - 0

记录时间:2009年12月14日

更依赖于事件的技能设计方案

如果我们将技能事件的定义和功能扩展,会发现其实远程技能,近程技能的共通点。前期设计中,我们只考虑了事件的瞬时性,换句话说,我们认为只有瞬时发生,并只发生一次的逻辑才属于技能事件。如果我们在事件上加入时间,变化性等特点,可以惊喜的发现原来近程/远程技能的逻辑是如此统一。

646-41266476fb746ebe

我们可以尝试用新的事件结构来描述近程和远程技能

646-41266477052de3ef 

646-412664770687c65a

就像上面所描绘的一样,近程技能和远程技能在设计上达成了统一。近程/远程技能在技能控制,时序更新,事件触发和结果派发的各个关键点上的逻辑达成了一致,它们之间的差异性被转移到事件本身中。采用这种设计,技能的实现并没有带来多余的效率损耗。

当然上面的设计也带来了其它层面上的设计变动,而且这种变动会深刻的影响与技能系统相关模块的设计。就是事件本身在终端间的同步。新的设计将事件本身设计为一个有时序的逻辑个体,就像对前期设计的剖析一样,一段有时序的逻辑,必然需要在时序的多个关键点上进行网络同步,因为我们不仅关心它的最终结果,也同样关心它的变化。

等一下再来考虑同步的问题,我们先集中精力设计一下技能事件的处理流程。

技能事件本身有可能会产生新的事件(考虑一次攻击可能会随机的带来DEBUFF效果)。当然我们也可以继续扩展事件本身,让它把所有可能产生的新逻辑都囊括到自己本身中,这样就不会有新事件产生了。但这种选择会导致技能事件本身变得过于庞大和脆弱,因为新的事件类型可能会在设计后期被发现,这时候需要重新回来扩展事件的结构,并且这种修改是线性的,一个新改动可能会影响所有已经定义好的设计。

最后我们还是选择了让事件本身保持简洁和概念一致,攻击事件   恢复事件   BUFF事件   等等等等,新的功能由新的事件来完成,旧的事件和处理方式不受到后期需求变动的影响。

就像前面剖析的一样,事件的结果对于双方是唯一一致的。事件也许会产生新的事件,但不要影响现在事件的处理。在前期设计中,我们产用事件产生时立即发放给双方的方式,这里的问题有三个

1.事件的处理不一致,这在前面已经说明了

2.可能会导致事件处理死循环,如果一个事件产生新事件,新事件再产生新事件。。。。。。。。。

3.只能处理没有生命期的事件

立即派发的方式不可取,我们可以选择采用LazySend的方式。在新的设计中,所有事件都在产生时被抛送到一个全局的事件管理器中,等待管理器去定期的更新和处理。当然这样事件的产生和处理就不在同一个时间片中执行了,但考虑到网络延时和事件管理器的更新频率,这个问题也没什么关系。

posted @ 2009-12-14 20:54 BadKeeper的3D引擎开发笔记 阅读(25) | 评论 (0)编辑

记录时间:2009年12月14日

一种新的技能系统设计

这次设计的主要目的是解决掉第一版设计的各种已有问题和可预期的潜在问题,并保证技能制作的主要流程,不对当前已经完成的技能功能做出太大的调整。

我们先将需要达到的目标列一下:

  1. 统一远程技能和近程技能的设计,简化子弹和技能执行之间的逻辑关系和制作流程
  2. 技能的逻辑计算应该完全以服务器为准,在保证逻辑和表现的一致性前提下,尽可能避免网络延时带来的体验误差
  3. 技能的一次事件,结果应该是唯一的,参与事件的双方应该处理这个唯一结果,并在处理过程中相互不依赖
  4. 技能的逻辑制作流程与美术制作流程应该尽可能独立
  5. 新的设计应该着重于解决和优化上述问题,但不能对目前制作流程和已有资源造成太多修改

我们再来列举一下前期设计和实现的问题

  1. 子弹的设计过于独立,丧失了近程/远程技能的设计一致性
  2. 技能的逻辑计算分散,缺少统一计算终端,有很多潜在问题
  3. 技能事件的处理不统一,无法维护事件结果的唯一性
  4. 目前的技能系统资源量涉及得比较多,有CSV,脚本,代码,同时美术制作流程与逻辑制作流程没有完全独立

我们需要一种新的框架,能够在尽可能利用目前流程和资源的前提下优化和解决种种设计问题。

目前我想到了一种与前期设计的冲突不太尖锐的新框架,如果采用这种设计可以解决掉上面的大多数问题,提供更好的设计一致性和制作流程独立性。这种新框架还处于初步设计阶段,有很多细节没有完全考虑清楚,后续笔记会着重于阐述这种设计,细化思路,并与需达成目标进行对比。

posted @ 2009-12-14 20:53 BadKeeper的3D引擎开发笔记 阅读(15) | 评论 (0)编辑

记录时间: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的3D引擎开发笔记 阅读(22) | 评论 (0)编辑

记录时间:2009年12月12日

技能的同步设计:

      如果只是考虑客户端的功能,技能的同步功能是不会影响到技能的设计的。可惜我们并非生存于一个完美的世界,MMORPG中网络的不稳定性,安全性导致了技能同步的复杂度和对技能设计的深层次影响。

    技能同步主要面对的问题是:

      1.客户端表现的既时性

      最理想的情况下,用户不需要为网络状况买单,即使是在网络质量很差的情况下,我们也希望他能够拥有完美的游戏体验。

      2.客户端之间表现的一致性

      不要出现网络玩家与本地玩家在技能表现上有明显差异

      3.技能逻辑的一致性

      如果在本地看到一次技能成功暴击,而对方实际没有受到任何伤害,玩家的体验会很糟糕

      4.技能的安全性

      不要试图欺骗服务器,使用非法手段修改技能逻辑

      在最早的设计中,设计的重心偏向于问题1,2。为了即时性,我们忽略了问题3,4的重要度。结果是不言而喻的,对于MMORPG来说,问题3,4的优先级要远高于1,2。重新调整设计不可避免。但前期的设计也不是一无是出,因为我们已经确定了一个技能表现的基线,并借此发现了很多单纯靠设计无法发现的潜在问题。

      今天我先记录下早期的设计思路,阐述这种设计的来源与优缺点,为后期的调整设计确定基线。

      从本地的表现来看,技能可以看成是一个连续的,有时间限制的事件序列。在这个时间线上,会出现一些关键事件,例如伤害,发射子弹或其他。我曾经试图利用信号原理来描述这一事物。一个连续的信号序列上,必然存在一些离散的特征点,这些点才是该信号区别于其他信号的关键信息。换句话说,技能事件才是该技能真正需要在客户端-服务器,客户端-客户端之间传递的信息,每个终端都可以根据这些关键信息还原出技能的连续信号(如果它需要)。

      我们可以很容易的识别出这些关键点,技能的启动,中断,结束,打击点、、、。剩下的问题是,我们需要在这些关键点上同步什么数据,以及我们应该以哪个终端的数据作为还原技能的数据标准。

      最理想的情况,数据的来源应该是唯一的,这样我们可以很简单的满足问题3。在MMORPG的网络拓朴中,这个来源必然是中心服务器。但在前期设计时,我们将网络延时的严重性和问题1的重要性夸大了,服务器的数据来源也许会很晚,玩家的体验也许会很差,等等等等。问题夸大的结果是我们选择了一种看似优美,实际会导致更多问题的的设计。

     这种设计的中心是忽略掉问题3,如果不需要考虑问题3,那我们可以选择多个数据来源,每个终端都选择自己作为数据来源,网络延时再也不能影响我们了,所有人都是在玩单机游戏,这不是很美妙的事情吗,没有人作过,也许没有人想过,太完美了,我们来动手吧。

     看懂了吧,我们在各个终端上完全独立的执行着技能,并触发着各自的关键事件,直到技能结束。客户端无需等待服务器的应答,无需为网络延时买单。技能的安全性也得到了保证,应为最终的逻辑数据是由服务器产生的,也许客户端可以在本地作弊,但最终的结果仍然是正确的,别的网络玩家甚至看不到作弊玩家的不合理行为。

     如果只有作弊时才会出现问题3时就好了,这样我会一直坚持这种设计,直到它成为一种工业标准:P。

     其实即使忽略掉多人情况下的各种复杂问题,上述设计带来了一种更为复杂的需求

如何实现世界的一致性

      如果各个终端的世界是不一致的,那同一个技能逻辑也会在不同世界中产生不同的结果。但世界是不可能完全一致的,因为我们在网络上。一个玩家在本地恢复了HP,他在别的终端上也许还没有恢复,因为网络延时了!!。换句话说,我们没有解决问题,只是把问题转移了,最终我们仍然需要为网络延时买单。,

      多人游戏的情况下,这种问题会加剧并最终锐化。感谢我们尽快的发现了这个问题。

      有问题的设计要及时修正,这次我们不要这么极端了,逻辑性的关键事件,还是交给中心服务器来处理吧,并即时的把处理结果通知给其他终端,本地终端仍然处理一些非逻辑的关键事件,例如特效,音效。也许技能的特效和作用之间无法完全一致了,但问题还不算太严重,如果加入一点小技巧(将关键点的时间与网络延时合起来考虑),大多数情况下体验还是不错的。

posted @ 2009-12-14 17:40 BadKeeper的3D引擎开发笔记 阅读(17) | 评论 (0)编辑

记录时间:2009年11月24日

早期的技能系统设计在实现过程中首先发现了子弹模块的设计问题。

从技能系统的实现角度来看,子弹应该只是技能执行的一种扩展,本身不应该单独存在。否则加大了技能系统的制作难度,让逻辑变得分散。

当初考虑的原则是基于状态机。根据技能的表现,认为技能执行可以看成人物的一种行为状态。人物在技能执行时会中断掉其普通行为状态,在执行完技能后会回到普通行为状态。

这种设计的考虑是根据游戏内技能的表现来决定的,如果要求游戏中可以一边跑一边发技能,则前面的设计假设都不成立了。

如果将技能本身考虑为人物行为状态的一种延伸,则子弹的存在会影响这种设计的一致性。因为人物不可能要在执行完技能后等待子弹碰撞或者爆炸后才能回到普通行为状态。

因此一开始的设计将子弹作为一种独立的逻辑实体,技能只负责发射子弹,然后人物回到普通行为状态。子弹实体创建后独立的移动和更新,与发射者脱离关系。

这种设计从逻辑上看是合理的的,可以将一个技能的逻辑根据状态机的特性分拆,但在实际制作时发现由于逻辑分散,会导致实际制作人员思维混乱,并且难以发现BUG。设计需要简化。

      换个角度来看待技能和子弹。实际上技能是分为近程和远程的,子弹只是远程技能的一种特性。技能要起作用,必然要有一个关键时间点,在该时间点上,技能可以产生伤害,附近Buff等。

近程技能的时间点是在技能执行的时间段内的,远程技能的时间点是在技能执行的时间段外的,因此如果将技能的效果提取成一种独立的操作,不依赖于某个技能或者子弹来执行,则可以通过

对时间的不同描述将近程和远程统一起来。

      当然这种设计本身是有缺陷的,如果按照上面的设计,则子弹就变得和近程技能一样,是无法躲闪的。丧失掉了远程技能的设计多样性。

      第一次设计的优化是从实现层面考虑的,考虑到子弹本身的逻辑对于每个远程技能来说没有太多的公用性,取消掉了子弹的单独脚本。子弹逻辑被整合到了技能脚本中。但子弹本身的设计并未被废除掉,在逻辑上子弹实体仍然与技能独立,远程技能的逻辑成为生成子弹,然后结束,此后子弹单独存在并更新。

      例如一个远程技能被如下脚本实现:

--技能 阳关三叠
Skill_22100701 =
{
  StageCount = 1,
  Stage_0 =
  {
    KeyCount = 2,
    Key_0 =
    {
       Time = 0.58;
       Name = "attack"
    };
    stage_start = function(object,skillID)
                SI:FaceToTarget(object,TARGET_LOCKED);
                SI:PlayTargetAnimation(object,TARGET_ME,"阳关三叠");
                SI:SetTargetMoveSpeed(object,TARGET_ME,0);
                return RE_SKILL_SCRIPT_NORMAL;
    end;
    stage_update = function(object,time,skillID)
       if(time > 0.8) then
         return RE_SKILL_SCRIPT_END;
       else
         return RE_SKILL_SCRIPT_NORMAL;
       end;
    end;
    stage_timer_callback = function(object,keyname,skillID)
                if(keyname == "attack") then
                        SI:ShotBulletToTarget(object,TARGET_LOCKED,skillID,0,1,DT_HAND_L);
                end;
    end;
  };
        BulletCount = 1;
        Bullet_0 =
        {
                bullet_explosive = function(object,bullet,touchedActorID,skillID)
                        SI:AttachSkillState(object,touchedActorID,32100701,10,0,0,0,0,0);
                end;
        }
};

这次设计优化只是减小了资源(脚本,表格)文件之间的关联性,并没有简化技能的实现逻辑,技能和子弹的设计仍然缺乏高度的一致性。

posted @ 2009-12-14 17:39 BadKeeper的3D引擎开发笔记 阅读(15) | 评论 (0)编辑

<2010年2月>
31123456
78910111213
14151617181920
21222324252627
28123456
78910111213

搜索

 

常用链接

随笔档案(36)

相关连接

积分与排名

  • 积分 - 23282
  • 排名 - 3075

最新评论

阅读排行榜

评论排行榜