命令系统测试及调试

命令系统测试(Testing)及调试(Debugging)是一个非常复杂的话题...

为了让一个命令系统不会出错(Bug),或者把错误减少到一个可接受的程度,我们需要进行测试或者检查。检查出问题了我们就需要调试以解决问题(虽然我认为重写是比较方便快捷的方法,如果那bug不是我造成的话)。

其实我也没太多可以讲的,毕竟这很需要天分(bug体质和特别聪明的脑袋)以及经验。只能讲一些比较常见的思路及错误,以及一些能够尽量避免发生那些错误的办法。

测试

地图的测试就没啥好讲的,把地图拿去玩几回看看能不能玩出bug呗。

上面其实准确来说也是没错的,然而我们可以更仔细的检查。

时序问题

我们的地图大多依赖于高频来不断执行一些操作,这代表了我们的命令之间是有时序分别的,这可能带来时序方面的问题。

模块(一组用作某个功能的命令)有两种,一种是独立模块,一种是有依赖的模块。
前者是一个模块做完整个完整的功能,故此一般对时序要求不大。
后者为依赖别的模块做完某个功能,故此必须仔细检查时序。

比如之后的熔炉过热系统就是一个独立模块的例子。里面所有功能都写在一个很简单的模块里,不依赖于别的模块。这种模块假如是一些针对玩家行为的功能,比如说是 当玩家吃了块饼干,就怎样怎样,这种情况可以尝试把模块扔给进度进行调用。

而之后的技能系统就是一个有依赖的模块的例子。里面分为几个模块,比如说技能事件依赖于道具触发模块,冷却补给依赖于道具触发模块。这种情况就需要小心检查其依赖性,避免发生一些时序问题。

多目标问题

比如有一个命令系统是:

# test:item

# 测试石头是否存在
testfor @e[type=item] {Item:{id:"minecraft:stone",Damage:0s}}
# 假设我们的执行者的stats变量分数=SuccessCount,成功时就干掉这物品
execute @s[score_stats_min=1] ~ ~ ~ kill @e[type=item]

这命令没问题么?
其实有很大的问题,这问题就是,当场地上有多个物品,并且不是全部都是石头的时候。

我们来模拟一次,当场地上有一个石头物品的时候,那testfor命令就会成功。stats分数就会为1。
然后我们就会杀掉所有物品。
那么,如果还有其他物品咋办?结果就是和石头陪葬。


这例子展示了一个常见的问题:多目标的问题。我们只是检查某个特地的东西是否存在,然后却把所有同类的东西全部进行某个操作,当还有其他东西同时存在时就可能导致错误操作。简单来说就是,我们限制好我们的目标。

我们处理可能出现多个目标(包括玩家)的情况时,检查条件是否成功应该改为添加标签/更改分数等。
比如上方的命令可以改为

scoreboard players tag @e[type=item] add stone {Item:{id:"minecraft:stone",Damage:0s}}
kill @e[tag=stone]

很编程的测试

地图的系统一般都会分作几部分(什么?你说你没分?那么不是你的地图太小就是你的做法不行了),我们一般会逐个功能部分进行测试(这个我们叫作单元测试,unit testing)。

单元测试让我们把大型的系统分拆作小部分测试,更容易渗透进不同部分进行测试,发现隐藏bug。然而,即使我们把所有系统都进行单元测试了,这代表我们的系统没有bug么?

当然不是了,我们整合不同系统的时候也可能会出现问题(比如顺序问题),这时候我们就需要集成测试(Integration testing)了。

简单来说就是...整个系统来测试。

这些我也不太懂,你们上网自己找找吧

归类、边缘及错误情况

会编程的基本上能跳过这部分了
别看,真的别看,我不想被你们揍

我们如果要测试一个命令系统是没问题的,我们需要准备测试数据。
然而数据范围那么大,我们难道是随便扔一堆数据进去?还是全部都测试了?

当然不可能了,我们自然是不能全部都测试了。然而选择数据的时候也不是随便选择的,我们需要把情况归类。以某些条件为分界线,分出不同的类别,还有那些条件本身作为边缘情况。

那么错误的情况呢?不需要理会么?当然不是。我们做系统的需要假设一件事(而且经常是事实):

玩家/用户都是白痴,而且他们不会看 玩家须知

所以我们还得弄进一些错误情况来测试系统能不能正常处理。


以一个输入考试分数的系统为例子,0-59分就say 你这学渣哈哈哈,60-79分就say 哎哟你竟然合格了,80-89分就say 这次还不错,90-100分就say 大神受我一拜

正常情况有啥呢?

  • 1-58分随便拿一个
  • 61-78分随便拿一个
  • 81-88分随便拿一个
  • 91-99分随便拿一个

然后边缘情况

  • 0
  • 59
  • 60
  • 79
  • 80
  • 89
  • 90
  • 100

还有错误情况

  • -1
  • 101
  • bla bla bla
  • (啥也没有)

对于这些我们一般还是直接脑补就算了,没啥必要开游戏。

游戏Bug

游戏的特性真的让人欲罢不能,即使你写得很正常,有时候事情还是会出问题。

一些比如是 进度(Advancement)、判据(Criteria)、命令统计(Stats)、NBT(特别是Attribute相关),经常出现特性。使用的时候记得先进行独立测试

一句总结:你们要学会原谅Mojang。

命令输出

有时候错误还是能从输出中略窥一二的,所以测试小模块时不必关掉命令反馈。

如果发现出错但无法定位问题时也可以使用命令输出寻找,或使用以下技巧:

挂着记分板的显示位置显示分数(当然就是看看那些分数有没有问题啊)。
甚至会使用saytellraw命令显示出某些执行期间的数据,让我们看看有没有错。(如果没记错这好像叫echo checking?)

模拟多人

一个人的时候怎么测试多人系统?

如果那多人系统不涉及玩家专有的功能,而是一些逻辑相关的东西,则可以使用普通实体充当玩家。

如果真的是多人系统,那你可以开个盗版服务器,然后开几个客户端进入测试。这也是没办法之中的办法了。

找其他人

其他人没有制作者的偏见,是比较容易发现一些隐藏的问题的。
至于一些常见问题,或许找着个大佬一眼就能看出来了。

所以不耻下问还是很重要的。

找UP主

不知道为什么,我们发现很多UP主都是Bug体质。

找UP主玩或许能够发现更多错误,因为他们不会有地图制作者的偏见或者预设,能够更深入测试。(不看简介直接开始者更佳)

能够接受的错误

部分功能是很难用命令系统完美实现的,总是有一些精准度问题的。
比如说向着玩家朝向发射东西,锁定哪根箭是玩家发出的等等。虽然可以通过一些方法(前者则穷举大量角度,后者则加上大量限制),然而理论上还是有精准度问题,还是有可能出现错误的。
针对这些情况呢,我们会选择接受,选择原谅。因为我们真的没有更好的方法了。

不过,在实现那些功能前你们需要问一下自己两个问题

  • 这精准度问题真的很影响用户体验么? 如果没啥大问题的话就不管好了。
  • 这精准度问题能通过穷举/加入其它限制解决么?如果是穷举的话,穷举量在1000以内么? 太多或者没法搞的话我们的建议就是放弃这个功能。因为命令真的有其限制,不是没有限制的。

常见错误

这里是一些常见的错误...

解决错误的最好方法其实是一开始就不犯这个错误。

  • 写错字,如redTeam写成radTeamredTaem。或者是把I(大写的 i )认成l(小写的 L )。 解决方法:使用自动补全或者是命令检查系统。这绝对不是广告
  • 命令参数次序错误。比如是effect命令里的倍率和秒数错误。这就真的没法方便的找到错误了...一开始就应该看着wiki写。
  • 格式错误,如NBT转义错误,JSON忘记了"",括号不平衡等等。建议就是一开始写好一点。 括号方面建议先输入一套括号然后再输入内容。
  • 选择器错误,如多个相同的参数,或把score_xxx=误解成检查分数等于,其实真正的意思是分数小于等于。
  • 忘记初始化,如忘记添加记分板变量,或者stats命令忘了初始化分数,把没有分数当0分等。
  • 记分板分数operation命令错误。如把“把每个实体的a分数赋值到其b分数”写成scoreboard players operation @e b = @e a。其实应该得使用execute命令逐个实体执行赋值。
  • 错误预测,如Attribute实际执行和预测不一样。
  • 命令顺序错误。(常见于模块整合时,如先reset了分数再使用分数的数值,当然出问题了。)
  • 游戏特性。这真的没办法。或许你可以选择多看看 Bug列表https://bugs.mojang.com/projects/MC/issues/

常见错误就只能列到这里了,有啥常见的记住跟我们讲,我们之后加上。

results matching ""

    No results matching ""