暴雪游戏推荐

暗黑3野蛮人攻速讨论:Thinking In IAS

时间:2013-06-23 00:00 作者:gazeofdoom 手机订阅 神评论

新闻导语

我所说的很多东西,都是基于假设,测试结果接近也不能说明他就一定是对的,未必可靠,只有官方说的才可靠。

七 对表进一步修正

前面我们推出了帧数-攻速-攻击次数表,并收集了一些数据,但仔细将这些数据与表对照,是有问题的,主要是2个:

1)比如2.01攻速应该是30F,1分钟120下,实测是124下,也就是实测打击数多余预测。

2)比如1.98和2.01攻速没有发生变化都是124,变化发生在2.01个和2.04之间,变成了128,但根据这个表,断点应该是2.00整,变化应该发生在1.98和2.01之间才对。前面在做小旋风测试的时候,断点也确实是发生在2.00,他就没这问题。

发现了这两个问题后,我又做了一个更直观的测试。1.00攻速,打5分钟,结果:

攻速 总伤害 暴击次数 非暴击次数 5分总次数 每分次数
1.000 215380 22 288 310 62

1.0攻速5分钟打出了310次,每分钟62次,这很奇怪,用传统的想法,攻速1.0么一分钟应该60次,解释不了;用我的理论,1.0攻速正好是 60F的断点,达到1.0就应该意味着每次猛击的时间是60F,一分钟3600F,应该也是正好60次才对,即使1.0是算作59F,那根据表也应该是 61次/分,还是解释不了。

其实这个现象我已经不止一次的看到有人提出过了,因为这很容易观察到,打2分钟,发现打出来的次数比120多,或者打120下,发现用时不到120秒,即使用更粗糙目测+秒表,这个现象也很明显。接下来我试图对这个现象进行解释。

前面写的东西,都是客观的,但从这里开始,已经既没有官方的结论,也没有D2的经验可供参考,完全是我的主观推测,伴随着各种假设,以及去分析假设靠不靠谱,所以不要认为我接下去说的都一定是对的,很可能完全不是这么回事儿。如果你对这些不是很确定的东西不感兴趣,那么读到这里就可以结束了,截止第六段已经可以说明一些问题。 愿意读下去的人,可以帮我一起把遗留的问题解决。

为了解释1.0攻速1分钟打出62次这个现象,我做了两种假设:

1)1秒不是由精确的60F构成的,可能是62,或者说1F的时间不是精确的1/60秒,可能是1/62秒。一秒62F,1分钟3720F,一次攻击60F,那就会有62次。

2)依然是1秒钟精确的60F,这不变,但由于某种原因,在攻击过程中,一个完整的攻击动作中的某几个F被吃掉了,一个本需要60F时间完成的猛击动作,只用了58F的时间就打完了,一分钟还是3600F,每击只花了58F,就有62次。

以上两种假设都是有可能的,我个人比较偏向于第二种

假设一:我觉得这种假设可能性不大的原因有两个,一是因为现在的技术要做到准确的一秒60F并不难,二是经过一些研究,我觉得1F确实是1/60秒,回到前面尾气测试时贴的2.01 2.04这两个攻速的视频,我用他们来找1F的时间长。

在尾气测试中,我们知道尾气无论是断点的位置,还是在某个F值时的跳数,都是与理论计算出的表格完全符合的,他并没有猛击这样断点位置和攻击次数发生偏离的问题。他没有发生问题,我的猜测是因为连续猛击是由若干个独立的动作拼接而成的,在拼接的过程中可能会出问题,而一次小旋风的若干跳,他们是一个不可分割的整体,是一次成型的,所以我觉得去观察小旋风是靠谱的。

在2.01 2.04尾气这两个视频中,我保留了白字没有关闭,用来观察F的长度。视频的采样率是60,也就是视频里的一秒是由60张画面组成的,每张画面长1/60 秒,如果游戏中的1F确实是1/60秒的话,视频里每一副画面的时间长短就应该与1F相等,虽然他的生成节奏可能和F不同步,但时长是一致的。两个视频我都采用逐画格播放,按一下键盘,放一格,来观察到底用了多少格画面,即多少个1/60秒来完成这些跳。

在这两个攻速时,小旋风每跳都是9F,总共都应该是20跳。攻击动作都会有前摇,判定,后摆的过程,由于小旋风一直是那个样子,我看不清的前摇从哪里开始后摆到哪里结束,就只能从判定点来看,于是从第一次产生伤害,到最后一次产生伤害,这样去掉了第一跳的前摇,和最后一跳的后摆,总共就是19跳的时间,应该是9*19=171F。如果1F的长度确实等于1/60秒,那么这断时间也应该有171个画格,至少比较接近。观察下来的结果是:

2.01的视频中,第一跳从6分第8格开始,9分01格结束,一共173格

2.04的视频中,第一跳从5分第11格开始,8分01格结束,一共170格。

如果像假设一中说的,一秒有62F,每跳应该是9/62秒,19跳应该是19*9/62秒=19*9*60/62格=165格。

显然170和173还是更接近于171而不是165. 所以通过这个研究我觉得1F的时间应该就是1/60秒整,他不会被挤压。

假设二:既然研究表明1F应该就是1/60秒没问题,那么可能性就更倾向于假设二了,一个攻击动作的某几个F被吃掉了。为甚么会这样,前面已经有提到,因为我们在按住鼠标连续攻击时,这些攻击是有若干个动作连接形成的动作序列,你单独打一下猛击就停,根据攻速,他应该耗时比如20F,这没问题,但当按住鼠标连续打很多个猛击的时候,比如按住鼠标让他打10下,就不是200个F,某些F被吃掉了或者说丢失了,变成了可能190F,于是只需要比原来更短的时间就能打完。为甚么会这样,我说不出绝对正确的理由,只能继续假设。好吧,在假设里还要做假设。。。

接下来假设为甚么某些F会丢失,是怎么丢失的。

假设三:当你按住鼠标,等于不停的在向游戏发出攻击的指令,也就是你发送的比他执行的快,游戏受到这些来不及执行的指令时,生成一个指令序列,动作 1连着动作2连着动作3这样,在链接的过程中,发生了损耗,就好比你要把一些断掉的细纸条链接起来,你需要在每一条的末尾涂一些胶水,把后一条粘上来,这样就使断裂的纸条产生了连贯性,当然问题就是每一个纸条比原来短了一点,因为涂胶水的部分被后一条覆盖了。 既要把纸条连接起来,又不能损失一丝长度,现实中是做不到的。所以我猜测在游戏中,为了保证动作的连续性,也可能损失一点长度。

为甚么尾气没有问题,因为他是作用在怪身上的,你仍出去一个尾气,接下来他就是自己运作,与你本身不构成动作序列的关系,于是就不需要拼接,就没问题。

如果确实是这样的话,那本来一个10F的猛击,会变成9F,或者8F,一个60F的猛击会变成59F或者58F。等于每个攻速对应的F值会比表中低 1-2,这个看起来还算有点味道,比如测试中的1.74攻速 109跳,本来1.74攻速应该对应1.71的35F,给他减2F变成33F后,正好对应109次。

但这个假设还是有问题,你会发现,测试数据中,某些攻速需要-1F来看表,而有些又是需要-2F,这个无法解释。还有,降1-2个F看表不会改变表上的那些数字,这样依然无法解释为甚么会出现186次,这个数值。

总之这个假设感觉还是不太对,我觉得是因为我把F丢失的方式想的太简单了,确实,一款2012年的游戏,动作序列的连接,做的像连纸条这么简单,也是稍微扯淡了点。。。有必要把他想的再复杂一些。

假设四:我尝试来设计一套更复杂连接方式。

根据平时的游戏经验,我想到一个东西,半秒,有些东西以半秒为单位来显示,比如回血绿字,半秒一跳,他就像是一个结算周期,是一个比F更大的时间单位,之后我称一个半秒为1U。现实中,我们用毫秒,秒,分等单位来衡量时间,而游戏系统内,是以F和U为单位来计算时间,拍一巴掌需要几F,拍死一只怪需要几U,应该这样来说。

按理说1秒=60F,半秒应该是30F,但我要假设的是1U=31F,也就是1U实际比半秒长一点点=31/60秒,1F还是稳定的1/60秒。

为甚么我假设1U=31F,31这个数字,对编程来说也是有一定特殊性的。编程时经常会去用一些2的整数次方的数字,比如2^5=32。所以我会准备一个32个格子的容器U去存放F,并且编程中我们知道,各种容器,数组啊,集合啊,等等,格子的编号都是从0开始的,第一格编号是U[0],第二格是 U[1],依次。为了方便,很可能第一格留空,如果从第一格开始放,会变成U[N]里放的是第N+1个单位,后续调用很麻烦,从第二格开始放,这样格子的编号与单位的编号就一致了,比如编号是U[N]的格子放第N个单位,会比较比方便。这样一共32个格子,第一格空掉,使用的就是从U[1]到U[31]这 31个格子,U[0]空着不用。

说这些东西只是我想说明以1U=31F来作为一个游戏内时间计量单位,以一个32格的容器来制作U,这些在编程时的可能性是有的。

回到动作序列的连接,在假设三中我提出直接把各个动作连起来,这种方式不光是过于简单,而且实现上会受更多其他因素的干扰,因为每个动作的时间太短了只有几F,也许会受网络等因素干扰。相比之下,1U是一个比较长的时间单位,受影响的机会要小的多,所以更好的方式是,先把将要执行的每个动作放进一个更大的大容器U里,然后再去连接这些U。就像先把东西装进车厢,然后连接车厢变成火车。如果一3个动作是10F 10F 11F,正好放进一个U里,如果一个动作比较长,比如有40F,那么前31F放在第一个U1里,后9F放在U2里,这样。

当我们给游戏发送连续的游戏指令时,比如按住鼠标说攻击攻击攻击,就生成了动作序列,序列生成的方式就是一个U接一个U这样拼上去。拼U的人并不关心U里装的是甚么,他只管把1个个U拼起来并且保证一秒正好由2个U组成,由于每个U的长度是32格(第一格空着,二到最后一格存放这动作内容),直接拼时间会凑不拢,于是,这个拼接工把每个U去头去尾,变成30格即30F的长度,组装,就正好是整数秒了。去头没关系,头本来就是空的,去尾就有问题了,这里是存着东西的,但没办法,这一格里的F就被吃掉了,丢失了。

每秒由2U拼接成,每U丢失了1F,这样,等于每秒会丢失2F。这2个U里原本装着62个F,其中第31F和第62F在拼接时被吃掉了。

这样的话,游戏用1秒60F的时间,就能完成了62个F的动作,因为其中2F被吃掉了,不用表现了,这便是假设4得出的结论。看上去和假设一有点像,都提到了62F,但是不一样的。假设一里的一秒62F,是在1秒里确实装了62个F,每个F的时间被挤压了,假设四里的1秒62F,每个F的时间还是 1/60秒,没被挤压,只是有2个F丢失了,1秒里装的还是60个F。

造成这个问题的原因,我觉得是程序制作时的一个失误,设计者肯定是希望1.0攻速打一分钟出现60次攻击的,现在出现62次肯定不是他故意为之。在 1个U里,如果只储存30个F,头尾都空掉,就没问题了,拼接的时候去头去尾正好去掉两个空格,设计初衷应该是这样,但可能在实现时的忽略,把最后一格也存了一F,问题就发生了。其实平时编程时,诸如搞错数组序号这种事是很常见的错误,也没甚么稀奇。

根据假设四,1分钟60秒,能够用来完成60*62=3720F的动作,当然实际的时长还是3600F,能在3600F完成3720F的动作,因为 120个F在拼接时丢失了。也就是说,如果一个动作需要20F的时间,在假设四这样的机制下,1分钟能完成3720/20=186次。Oyeah,我很欣喜的发现我得到了186这个之间无法解释的数字。 同理,用3720/不同的帧数,就能得到在这个帧数下1分钟的攻击次数,于是我们得到一张修正表。

帧数 攻速 理论每分次数 修正次数 帧数 攻速 理论每分次数 修正次数
60 1.000 60.00 62.00 30 2.000 120.00 124.00
59 1.017 61.02 63.05 29 2.069 124.14 128.28
58 1.034 62.07 64.14 28 2.143 128.57 132.86
57 1.053 63.16 65.26 27 2.222 133.33 137.78
56 1.071 64.29 66.43 26 2.308 138.46 143.08
55 1.091 65.45 67.64 25 2.400 144.00 148.80
54 1.111 66.67 68.89 24 2.500 150.00 155.00
53 1.132 67.92 70.19 23 2.609 156.52 161.74
52 1.154 69.23 71.54 22 2.727 163.64 169.09
51 1.176 70.59 72.94 21 2.857 171.43 177.14
50 1.200 72.00 74.40 20 3.000 180.00 186.00
49 1.224 73.47 75.92 19 3.158 189.47 195.79
48 1.250 75.00 77.50 18 3.333 200.00 206.67
47 1.277 76.60 79.15 17 3.529 211.76 218.82
46 1.304 78.26 80.87 16 3.750 225.00 232.50
45 1.333 80.00 82.67 15 4.000 240.00 248.00
44 1.364 81.82 84.55 14 4.286 257.14 265.71
43 1.395 83.72 86.51 13 4.615 276.92 286.15
42 1.429 85.71 88.57 12 5.000 300.00 310.00
41 1.463 87.80 90.73 11 5.455 327.27 338.18
40 1.500 90.00 93.00 10 6.000 360.00 372.00
39 1.538 92.31 95.38 9 6.667 400.00 413.33
38 1.579 94.74 97.89 8 7.500 450.00 465.00
37 1.622 97.30 100.54 7 8.571 514.29 531.43
36 1.667 100.00 103.33 6 10.000 600.00 620.00
35 1.714 102.86 106.29 5 12.000 720.00 744.00
34 1.765 105.88 109.41 4 15.000 900.00 930.00
33 1.818 109.09 112.73 3 20.000 1200.00 1240.00
32 1.875 112.50 116.25 2 30.000 1800.00 1860.00
31 1.935 116.13 120.00 1 60.000 3600.00 3720.00

这张表看起来就比较靠谱了。与前面的一些测试结果差异也很小(我是指跳数上的差异比较小,断点位置的差异还是有的,这个下一步再解决)

再做一个测试来说明假设四比假设三更靠谱,通过表的走势可以看到,随着攻速的提升,攻击次数的变化也就跨度就越大,要验证哪一种假设更靠谱,比较好的办法是制造一个高攻速,看看他的攻击次数更接近哪种假设。

我做了3.816攻速,双持2把1.8速度的匕首,穿除了旧祖鞋外所有的满值攻速装,加变身。结果:

攻速 总伤害 暴击次数 非暴击次数 总次数
3.816 807047 31 202 233

(两把匕首的最小值是一样的,所以在饰品的最小值属性加成后,主副手的DPH一样,打出的数字一样,还是可以用同样的办法来统计攻击次数)

1分钟233次,这个数字在修正后的每分次数中可以找到,对应16F 3.750攻速的232.5次。而在原来的理论次数中,没有与之对应的数值225 240都差的很远,按照假设三的少看1-2个F去找,找不到,说明假设三不靠谱。

最后的这个高攻速测试应该可以表明,我的假设四还是有点谱的,毕竟在高攻速下,攻击次数的跳跃幅度是很大的,根据假设的修正能与实测很接近,应该不是巧合了。

八 遗留的问题和结论

最后通过假设四我依然没有解决的问题是,为甚么断点不是出现在60/N这些位置。这个很遗憾,这也是我长篇大论的主要原因,我想让看的人了解我的想法,帮我想想为甚么断点会偏移,偏移到哪去了。我不想用大量的实测去逐个逼出断点的位置,这样还是搞不清为甚么,肯定是有某个原因在里面的,还是先想出原因,算出理论值,再用实测去验证比较好。

更新,我暂时找清出断点位置,可以作为一个初步结论,还是比较准确的,可以作为参考,这张表适用于猛击,先祖:

帧数 每分钟攻击次数 攻速 帧数 每分钟攻击次数 攻速
60 62.00 0.983 30 124.00 1.967
59 63.05 1.000 29 128.28 2.034
58 64.14 1.017 28 132.86 2.107
57 65.26 1.035 27 137.78 2.185
56 66.43 1.054 26 143.08 2.269
55 67.64 1.073 25 148.80 2.360
54 68.89 1.093 24 155.00 2.458
53 70.19 1.113 23 161.74 2.565
52 71.54 1.135 22 169.09 2.682
51 72.94 1.157 21 177.14 2.810
50 74.40 1.180 20 186.00 2.950
49 75.92 1.204 19 195.79 3.105
48 77.50 1.229 18 206.67 3.278
47 79.15 1.255 17 218.82 3.471
46 80.87 1.283 16 232.50 3.688
45 82.67 1.311 15 248.00 3.933
44 84.55 1.341 14 265.71 4.214
43 86.51 1.372 13 286.15 4.538
42 88.57 1.405 12 310.00 4.917
41 90.73 1.439 11 338.18 5.364
40 93.00 1.475 10 372.00 5.900
39 95.38 1.513 9 413.33 6.556
38 97.89 1.553 8 465.00 7.375
37 100.54 1.595 7 531.43 8.429
36 103.33 1.639 6 620.00 9.833
35 106.29 1.686 5 744.00 11.800
34 109.41 1.735 4 930.00 14.750
33 112.73 1.788 3 1240.00 19.667
32 116.25 1.844 2 1860.00 29.500
31 120.00 1.903 1 3720.00 59.000

如果你的攻速达不到下一档,那就只有上一档的攻击次数,比如攻速是1.98,没有达到会突变的2.034,那实际就只有1.967的攻速,1分钟只能打出124下。

相关阅读:暗黑3