作者:ZackBell
翻译:highway★
在这篇文章中,我将解释我在调试游戏时的过程,以及一些快速发现错误并找出游戏瓶颈的提示。 我会尽可能多地讨论这些工具,但鉴于它是如此广泛的话题,我很可能会错过几点。 您自己尝试所有这些工具并找出方法让您的调试体验变得更轻松,更好。 阅读官方文档也是在阅读本文后了解更多信息的好方法。
调试和分析究竟是什么?
我们都在这种情况下编写代码,运行游戏,并没有像预期的那样工作。 然后我们回到代码,看了20多分钟后,感觉是,“这没问题,应该好使啊”。 我们首先想到的解决方案是通过使用调试消息或在屏幕上绘制文本来检查变量的值或条件语句的结果。(译注:比如我……)
这对于快速调试一个小功能来说很好,但是当你有很多移动部件一起工作时,生成这些消息可能会很快失去控制。 这时候debugger(调试器)就变得很有用了, GameMaker的debugger允许您逐行运行您的代码,检查每个变量的值及其变化。 它还可以显示更多高级信息,例如纹理和表面的状态,当前图形选项,缓冲区的值等。
在项目的后半部分,由于对象互相交互,您的游戏速度会变慢,因此分析通常更有用。 这是探查器派上用场的地方,它提供了游戏运行时每个阶段调用哪些函数的详细信息,并以易于分析的方式编译数据。 一旦找出缓慢运行的功能/部分,就可以对其进行优化。
进入调试模式
让我们从基础开始。 您可以通过按顶栏上的Play按钮或按F5启动您的游戏。 但是,要使用附加的调试器启动游戏,则需要按下Bug图标,或者也可以使用F6。
游戏启动后,您将在代码编辑器的顶部看到一个新选项卡,并打开多个其他窗口。 您可以自定义这些窗口中的每一个窗口的布局,我的布局如下图:
如果窗口没有弹出,或者您意外关闭了窗口,请转到Debugger→Windows,并且会显示一个可以显示的可用窗口的完整列表。
调试工具栏
一旦进入调试器选项卡,您将在顶部看到一组按钮。 让我们回顾其中的每一个,我们将在文章中详细讨论更多细节。 这是工具栏的外观:
从左到右,依次为这些部分:
- 继续,中断和重新开始游戏:这些按钮让我们暂停(中断)程序,继续下一个断点(或者如果没有设置其他断点,继续执行),然后重新启动程序。
- 关闭游戏:与上面类似,可以停止游戏的执行。
- 步入,跳过和跳出:这些按钮在应用程序暂停时逐行推进我们的代码。 你会很频繁的使用这个。
- 实时调试:这是一个切换按钮,用于控制我们是否希望在应用程序运行时或者仅在暂停时更改调试器中的变量。
- 丢弃收集的数据:在调试时,调试器会保留它在上次运行时收集的数据,以便在关闭应用程序后引用它。 该按钮将丢弃所有的数据。
- 内存,FPS和彩色圆圈:显示当前内存使用情况,当前每秒帧数(FPS),当调试器连接并且应用程序正在运行时,该圆圈将变为绿色,否则为红色。
Breakpoints断点
现在你已经通过附加的调试器运行了游戏,可能你已经注意到没有太多变化。 你仍然无法看到变量的值,似乎正在工作的唯一东西就是Graph窗口。 原因是调试器需要暂停应用程序来检查变量和缓冲区的值(有一种方法可以实时执行此操作,但稍后我们会做到这一点)。 暂停游戏的一种方法是通过调试工具栏,按下中断(暂停)图标。 然而,这会暂停游戏,无论你点击按钮时的哪一点,所以它都会暂停在一个无法控制的随机位置。 停止应用程序的更好方法是使用断点。
有三种方法可以在特定行上设置断点:
- 在你想要断点的行上左键点击行号槽。
- 右键单击一行,然后左键点击Toggle Breakpoint。
- 选择一行,然后按F9。
请记住,如果您在空行或带注释的行上设置断点,则游戏将在下一个有效行中暂停。
现在我们已经设置了一个断点,如果我们使用调试器来运行游戏,应用程序将在该代码行执行之前自动停止。 因此,例如,如果您停在一个为变量赋值的行上,变量本身不会有该值,但只要我们进入下一行,它就会改变。 即使游戏正在运行,也可以在任何时候设置或删除断点。
游戏在我想要的部分暂停了, 然后呢?
游戏暂停时,您可以用几种不同的方式检查变量的值。 最简单的方法是将鼠标悬停在代码中的变量名称上。 知道当前行上面的变量值已经更新了这个帧,而下面的值没有。 这也意味着如果你有一个局部变量,他们将不会被初始化。
以这种方式检查值对于整数或字符串等变量类型非常有用,但是从数据结构或对象获取的信息并不太有用。 在这些情况下,最好使用变量和实例调试窗口来获取更多信息。
默认情况下,“Variables变量”选项卡中有三个部分。 在Locals窗口中,您将看到所有局部变量的值,其中包括self。 如果单击self对象上的加号,则会看到与正在检查的对象关联的所有实例变量。 中间的Globals窗口,向我们展示所有已定义的全局变量及其值。 最后,在右侧的Watch窗口中,我们可以指定变量随时跟踪。 要在Watch窗口中添加变量,我们可以点击“Add new Watch ...”,然后键入变量的名称,或者右键单击其他窗口中的任何变量(本地,全局,实例,所有实例)和 然后选择“添加监视(Add Watch)”。请注意,如果您尝试观察当前超出范围的变量,则调试器将无法引用它(它将显示<无法评估(unable to evaluate)>)。
有时你会发现,当你真的想看到它的值时,变量不会显示你想要的信息,例如实例或数据结构ID。 在这些情况下,您只需右键单击该变量,将鼠标悬停在“查看为(View As)”上,然后选择您希望变量显示的方式。
这是事情变得有趣的地方,因为你可以做的不仅仅是查看变量的值。 实际上,您可以通过双击值单元格然后输入新值来更改所需的任何值。 这个改变会立即影响变量,让你在不需要重新编译游戏的情况下即时测试。 如果以这种方式更改某个值,则在下次运行游戏时不会再发生,因此如果你觉得某个值的效果更让你满意,那就记下来之后在代码里替换掉。
现在我们可以看一下“Instances实例”选项卡,它默认包含三个部分。 第一个标记为Instance的实例包含所有有关运行代码的实例的变量和信息。 您将看到您创建的变量的值,还有所有GameMaker中的所有内置变量和对象,比如ID,位置,物理变量,计时器等。
您还可以搜索在“All Instances所有实例”选项卡中创建的每个实例。 最后,如果在游戏窗口中单击某个实例,它将显示在“Selected Instance选定实例”部分,但前提是该对象具有GameMaker可引用的碰撞掩码(collision mask)。 如果您的房间中有同一对象的多个实例,并且需要选择特定的对象,这对于使用“所有实例”部分来说很困难,因为它们都具有相同的名称,这是非常有用的功能。
在暂停时浏览您的代码
我已经提到你可以在游戏暂停后,逐行运行你的代码,无论是手动还是断点,但还没有解释如何做。 如果你看看调试栏,你会注意到这些图标:
这些是您在浏览代码时的助手。 从左到右,这些按钮是:步入,跳过和跳出。
步入
当你按下这个按钮时,代码将执行下一个逻辑步骤。 如果你的行有一个变量赋值,它将执行它,然后移动到下一行。 但是,如果您徘徊的线路有脚本调用,那么它将跳转到该脚本并停在第一行。 您可以继续浏览脚本的代码,直到您到达结尾,然后返回到初始呼叫。 请记住,只有用户定义的脚本是这样,而GameMaker函数则不会,因为它们的实现对用户是隐藏的。
跳过
这个和前一个类似,我们逐行地逐行执行代码,但这次如果你在一个函数调用的行中,你将不会被接受。 相反,它将执行该脚本中的所有操作,出来,然后进入下一行。 除非我正在做一些深入的调试部分,我需要检查每一步发生的情况,这是我使用最多的按钮。
跳出
最后,这个按钮会在调用堆栈中向上移动一步。 如果您不确定调用堆栈是什么,那么它就是为了达到您所使用代码的一部分而执行的函数调用列表。因此,例如,如果您处于对象创建事件的范围内,然后调用一个脚本在其内部调用另一个脚本,那么你将深入三个函数。 如果现在按下Step Out按钮,则会进入堆栈中的上一步(第一个脚本调用)。 如果再按一次,则会返回到对象创建事件。 当你在一个脚本内部时,这很有用,你知道它正在按照预期工作,并且想要回到调试你开始的部分。
如果您想跳过其中的大部分代码,另一个导航代码的好方法是将另一个断点放在要到达的位置,然后按下Play按钮。 这将使游戏继续执行,直到它到达下一个应该是您刚放置的断点。
如前所述,您也可以按Pause 按钮来停止游戏的执行,但这会在游戏当前执行的任何地方停止,因此它可能是一个随机位置。 您也可以通过按播放/暂停按钮旁边的环形箭头重新开始游戏。
Real-time debugging实时调试
大多数调试器只允许您在应用程序暂停时检查变量。 在由GameMaker提供的最新工具迭代中,可以在游戏运行时观察变量的状态。 如果按下代码步骤图标旁边的按钮,您将激活它。 在运行游戏时,变量的更新速度会尽可能快,并不总是每帧更新一次,但通常可以使用。 并非所有的窗口都可以使用这个功能,并且它的使用主要是监视实例被创建/销毁以及实时变量值。
这些工具可以在90%的情况下有效地帮助调试您的游戏。
更多资源
在我的下一篇文章中,我们将介绍一些有助于调试表面(surface),着色器(shader)和自定义绘制(draw)代码的工具。当然F1更重要,或者在线看Debugging部分。
译注:当东西越做越多,越来越多交互影响的时候,游戏一旦出现BUG经常会让人陷入焦虑,灵活的利用GMS2自带的debug工具,能让我们更快的找到自己的问题所在。希望这篇文章对跟我一样的GMS2初学者有些帮助~ 共勉
非常nice大佬