思维的惯性
晚上在办公室晃荡,对面的同事在加班写代码。我凑上去看看在写什么。我向他了解了后明白了,大约是服务器上角色 buff 的实现吧。
BUFF 这个术语是现在网络游戏中非常常见的。给角色加一个 BUFF 通常意味着对虚拟角色的一些数值上的临时修正:例如,攻击力 +5 ,防御 -10% ,速度加倍,等等。
玩过魔兽世界的朋友应该很容易理解这些。通常游戏里的 BUFF 设定比我上述的例子更加的复杂。
这里不谈游戏设定,谈谈实现。
同事在做的实现框架,给 BUFF 留了几个接口,其中有两个吸引了我的目光。
一个叫做 start 一个 stop 。分别用于 BUFF 产生的时候需要处理的逻辑,和一个用于 BUFF 消失时处理的逻辑。这是一个很自然的设计。尤其在 C++ 程序编写的习惯里,就相当于一个构造过程,一个析构过程。
比如,一个攻击 +5 的 BUFF ,可以在 start 事件处理里修正攻击值(+5),然后在 stop 里做一次逆运算(-5)。
我突然有一种直觉,感觉这个设计不是特别合理。因为它要求程序员去实现一对互逆的逻辑。这段时间我对需要编写事物处理的成对可逆处理的设计特别反感。所以立刻就意识到了实际问题所在。
首先,许多逻辑上成立的可逆运算都是事实上有漏洞的。比如某些特定条件下的浮点加减运算就有可能不可逆(暂时没有去构造一个例子,但是我认为是可以构造出来的);定点数的乘除运算就很容易导致不可逆。
还有更严重的,如果一个 BUFF 会把某个值设置成固定值(比如清 0 或设为 max),那么不借助缓存变量,这个效果就完全不可逆。
当然有经验的数值策划,会把公式里的乘法规则提取出来,安排合理的次序去计算。比如大多数游戏中,乘法加成系数都是先累加再一次乘起来的。(btw, 早期刚接触 diablo 的时候,我完全不能理解:为啥两个加 20% 的装备装到一起是 40% 的效果而不是 44% :D 也有游戏设计的更复杂一些,例如 Eve 里,就有所谓的叠加惩罚。哦,好象又写跑题了。)
我问了一下同事,诸如一些特殊情况如何处理的问题:比如玩家顶着 BUFF 断线,重新上线的数值恢复问题。还有如果在 BUFF 有效期间,人物升级或更换装备导致基本数值变更,怎么保证 BUFF 消失时的逆运算正确性……
这些自然可以找到方法在现在的框架下正确的处理。但前提是实现的人考虑完备,或是多记录一些中间数据。当两件有关联的操作(BUFF 出现和消失)在不同的代码中实现时,BUG 就有可能滋生。
接下来我又询问了一下别的项目的同事,咨询了一下类似的设计他们是如何实现的。居然,大家都用的相同的手段。看来,思维还真是有惯性啊。
我后来想到的解决方案其实很简单。取消 start / stop 接口,改提供一个 apply 。角色永远记录两套数值,一套基础数值,一套产生实际效果的临时数值。
每次状态改变(增加新的 BUFF、改变基础数值,或是改变装备等),都激发所有存在的 BUFF 的 apply 方法在基础数值上全部重新计算一遍,得到新的临时数值。这样有可能会增加一些计算量,但是系统设计会更简洁一点。
Comments
Posted by: 正逍遥0716 | (36) July 21, 2017 07:56 PM
Posted by: lichsword | (35) August 10, 2012 12:55 PM
Posted by: Anonymous | (34) February 7, 2012 05:23 PM
Posted by: 涛 | (33) December 1, 2011 12:23 AM
Posted by: wago | (32) August 16, 2011 12:50 PM
Posted by: 王清 | (31) December 7, 2009 08:52 AM
Posted by: w2moon | (30) September 29, 2009 06:29 PM
Posted by: venjet | (29) April 22, 2009 02:41 PM
Posted by: anonymouse | (28) March 9, 2009 10:38 AM
Posted by: Anonymouse | (27) March 11, 2008 11:41 AM
Posted by: kingx | (26) January 18, 2008 05:00 PM
Posted by: Holimion | (25) December 9, 2007 08:18 PM
Posted by: nothanks | (24) December 5, 2007 03:40 PM
Posted by: Cat Chen | (23) November 24, 2007 11:24 PM
Posted by: Anonymous | (22) November 20, 2007 04:50 PM
Posted by: Ricepig | (21) November 20, 2007 04:16 PM
Posted by: ljc | (20) November 20, 2007 02:07 PM
Posted by: rockcarry | (19) November 19, 2007 07:26 PM
Posted by: Cloud | (18) November 19, 2007 07:11 PM
Posted by: rockcarry | (17) November 19, 2007 06:07 PM
Posted by: rockcarry | (16) November 19, 2007 05:55 PM
Posted by: nothanks | (15) November 19, 2007 05:50 PM
Posted by: rockcarry | (14) November 19, 2007 05:38 PM
Posted by: rockcarry | (13) November 19, 2007 05:27 PM
Posted by: rockcarry | (12) November 19, 2007 05:02 PM
Posted by: rockcarry | (11) November 19, 2007 04:52 PM
Posted by: Anonymous | (10) November 18, 2007 01:21 PM
Posted by: longtrue | (9) November 17, 2007 01:25 PM
Posted by: arden | (8) November 17, 2007 09:11 AM
Posted by: aynrsx | (7) November 17, 2007 12:00 AM
Posted by: Cloud | (6) November 16, 2007 01:45 PM
Posted by: mikeshi | (5) November 16, 2007 11:30 AM
Posted by: nothanks | (4) November 16, 2007 10:24 AM
Posted by: xuxing | (3) November 16, 2007 10:09 AM
Posted by: Anonymous | (2) November 16, 2007 09:18 AM
Posted by: mgwtest | (1) November 16, 2007 03:13 AM