依旧面向新手
译注:非完全翻译,有些片汤儿话就不翻了,只翻重点 :p ,我对部分代码进行了补充,加上了对应事件位置和注释,方便 gml 初学者理解操作,我在读原文的过程中有些地方比较困惑,自己适当修改了一下文章的内容。如果有错误出现,麻烦指正。感谢!
作者:Nathan Ranney
翻译:highway★
如果你看过我之前的文章,应该会注意到我用了很多自定义的计时器,而不是 GMS 内置的 alarm
系统。我这么做的原因是,一呢~ GMS 的 alarm
数量上有限制,二呢~ 并不是很直观,有时候写着写着我就记不住 alarm[0]
里面写过什么,当东西一多,就容易乱套。
(译注:当你的项目越来越大越复杂,可能会用到很多 timer,这时候一个个打开 alarm 去查看很容易让人……怎么说?懵逼? 或许更严重,尤其是你的显示器不够多不够大,想想以前 GMS1的 IDE,打开无数个窗口……wtf,如果你用自定义的 timer,一个窗口就全看的到,很直观~ 当然,最好灵活的使用 region,可以让代码更加整洁)
译注:下面的示例,请确保游戏以 60fps 运行,如果你对 GMS2 还不熟悉,可以在屏幕右侧 Resource 最下方找到 Option 选项,点击 Main,在弹出窗口中可以看到 Game frames per second。
概述和设置
那究竟什么是 Timer
(计时器)呢?Timer
是一种机制,允许您向上或向下计数到某个值,然后在达到该值时触发某些内容。例如,假设你的角色被毒性伤害攻击到了。毒性伤害通常是随着时间的推移,每隔一段时间就会造成少量伤害。想象一下你的游戏以每秒 60 帧的速度运行,你决定毒药伤害应该是 1
点伤害。你的角色有 100
点血量,你的代码看起来像这样:
示例代码:
//Create event poisonDamage = 1; //Step event if(poisoned){ hp -= poisonDamage; }
这段代码里没有 Timer 或任何其他限制,它每秒以 60fps(游戏每秒 60 帧)的速度将你的 hp
降低 1
。这意味着......你的角色有 100
点血,在 1.6
秒内死掉!oh,shit,这毒也忒 tm 狠了吧?你可不想这样,对吧?你真正想要做的是每隔几帧对你的毒害造成伤害,或者每隔几秒。那么代码应该看起来像下面这样:
示例代码:
//Create event poisonDamage = 1; poisonTick = 60;//补充,原文并未初始化 //Step event if(poisoned){ poisonTick --; if(poisonTick <= 0){ hp -= poisonDamage; poisonTick = 60;//重置 Timer } }
译注:请确保 option 里游戏是以 60fps 运行
这里,我使用一个新变量 poisonTick
来确定何时应用毒害。所以现在,之前要 1.6
秒,现在需要 60
秒。 poisonTick
每帧减少 1
,一旦 poisonTick
达到 0
,玩家 hp
将减少 poisonDamage
值,并且 poisonTick
将重置为最大值 60
。这需要比使用 alarm
稍微复杂一点点,但更好用。
要以这种方式使用 Timer
,您至少需要一个变量,该变量存储你正在计算的任何内容。在上面的例子中是 poisonTick
。我强烈建议你在重置计数变量时使用第二个变量。这会更方便管理(译注:当你自己的小项目进行了一段时间,重新打开并查阅代码时候,如果你看到一堆数字,可能早已经忘了当时写下它的具体含义了,所以能用变量就用变量,变量名可读性越高越好,如果你不是 solo 开发,你可不想你的战友碰到一个变量就问你,“哎,这是啥意思?”)。下面的例子会仔细说一下。
再来一个例子
使用手动 Timer
的另一个好处是可以在多种情况下使用相同的 Timer
变量。我经常在游戏中使用一个名为 actionDur
(动作持续时间)的变量来确定任何一个角色动作可以执行多长时间。假设一个角色有两个动作,攻击和前冲,两者都是依赖于 Timer
的。在 Timer
持续时间内,角色被锁定在某一动作中。你也可以使用两个计时器来管理它,比如 attackDur
和 dashDur
。或者你可以使用单个计时器,并在两个地方使用它。咱们看看下面的例子。
复用 Timer
//Create event actionDurMax = 60;//这里没有像上面的例子中继续使用数字了,注意变量名 actionDur = actionDurMax; //Step event if(attack){ actionDur --; if(actionDur <= 0){ actionDur = actionDurMax;//重置 Timer attack = false; } } if(dash){ actionDur --; if(actionDur <= 0){ actionDur = actionDurMax;//重置 Timer dash = false; } }
如您所见,两个动作都能够使用相同的计时器变量。请注意,如果您在多个位置使用相同的计时器变量,则每次使用计时器必须完全独立于其他用途。因此,再次使用上面的示例,我必须确保攻击和前冲不能同时为 true
。如果两个变量同时都是 true
,那么 actionDur
会以 2
倍速倒计时。我强烈建议为你的角色设置状态机,这会让你更方便的管理何时运行的代码。
当然你也可以不用倒计时(正数/倒数都无所谓啦~),稍微修改一下就好。无论您选择正数还是倒数(这里数读三声),这都是个人偏好。
正数 Timer
//Create event actionDur = 0; actionDurMax = 60; //Step event if(attack){ actionDur ++; if(actionDur >= actionDurMax){ actionDur = 0;//重置 Timer attack = false; } }
译注:如果你有点儿困惑,打开手机时钟,一个是计时器,一个是秒表。对,就是它们。
那么,如果您想为多个操作使用单个 Timer
,但这些操作持续时间有所不同那会怎么样呢?最简单的方法是,如果你使用的是我一直在讨论的相同类型的设置,那就是在动作发生时定义计时器的起点。再次使用攻击操作,让我们看看它是如何工作的。
单个 Timer - 但是持续时间不同
//Create event actionDur = 0; actionDurMax = 60; //Step event if(attack_button) { attack = true; actionDur = 30;//这里作者还是继续使用数字了,建议不要这样做~ } if(attack) { actionDur ++; if(actionDur >= actionDurMax) { actionDur = 0; attack = false; } }
在这个例子中,我们不得不使用一个新的代码块。我们添加了 attack_button
变量,你可以想象这是一个玩家在游戏中推动攻击按钮。当按下该按钮时,攻击变为真,并且 actionDur
设置为 30
。默认情况下,actionDur
设置为 0
,当按下我们的虚拟攻击按钮时,我们会覆盖它。但是,当攻击设置为 true
时,actionDur
仍在累加,然后判断是否达到 actionDurMax
。只有这一次,它不是从 0
开始,而是从 30
开始。这实际上减少了我们的攻击行为的持续时间。下面我们再弄的稍微复杂一点儿,我们加上第二个动作:
持续时间不同的 Timer - 多个动作
//Create event actionDur = 0; actionDurMax = 60; //Step event if(attack_button){ attack = true; actionDur = 30;//这里作者还是继续使用数字了,建议不要这样做~ } if(attack){ actionDur ++; if(actionDur >= actionDurMax){ actionDur = 0; attack = false; } } if(dash_button){ dash= true; actionDur = 10;//这里作者还是继续使用数字了,建议不要这样做~ } if(dash){ actionDur ++; if(actionDur >= actionDurMax){ actionDur = 0; dash = false; } }
译注:这里就不详细解释代码含义了,看到这儿的话你肯定已经了解这些东西到底在干嘛了~ 不是么?
关于自定义 Timer
,虽然稍微要比 Alarm
用起来要稍微花些时间来设置,但是这带给我们的好处却更灵活更方便管理自己的代码~ 希望这些内容对你有帮助!感谢你的阅读。
可以再详细说一下关于变量不要用数字是为什么啊,我刚入门,如果我在后面为自己的数字加备注的话会不会更好
@L_eve:如果过一段时间回过头看代码,要是都是数字,不方便阅读吧~ 注释上还好,我也是入门~ 可能说的不对
@highway★:那我大致明白你的意思了,为了方便以后读取和整理最好是用变量或者在后面注释,无论对自己还是合作的别人读取代码会更方便,了解!
@L_eve:我觉得不要用数字还有一层就是,如果直接写数字日后要修改这个计数的时候就要挨个找挨个儿改了,如果用变量控制,只要去改那个变量就好了