依旧面向新手
译注:非完全翻译,有些片汤儿话就不翻了,只翻重点 :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用起来要稍微花些时间来设置,但是这带给我们的好处却更灵活更方便管理自己的代码~ 希望这些内容对你有帮助!感谢你的阅读。
感谢分享