2015年8月25日
教程:使用“enterFrame”监听器实现动画
在 Corona 中,有两种动画方法:精灵动画和程序动画。精灵动画 (指南) 涉及使用以循环方式显示的多个帧(图像),从而产生对象内部运动的错觉。相比之下,程序动画涉及使用编程技术来移动、旋转、缩放或以其他方式更改对象的状态。
之前的教程概述了使用过渡的程序动画。本周,我们将讨论如何使用 "enterFrame"
事件来操作对象。
概述
当应用程序运行时,Corona 会每秒更新屏幕 30 次或 60 次,这个概念称为每秒帧数或 fps。您可以通过应用程序的 config.lua
文件中的 fps
值来控制此速率 (指南)
1 2 3 4 5 6 7 8 9 |
application = { content = { scale = "letterbox", width = 320, height = 480, fps = 60, -- 30 或 60 }, } |
当应用程序运行时,每次 Corona 准备更新屏幕时(每一帧),它都会生成一个名为 "enterFrame"
的事件 (参考)。将此事件与事件监听器结合使用,您可以通过在每一帧更改对象的某些方面来实现动画效果。
基本设置
考虑一下像太空侵略者®这样的经典游戏。在这个游戏中,外星人以设定的模式来回移动,偶尔会有一个特殊的 UFO 飞过屏幕顶部。让我们说明如何使用 "enterFrame"
监听器来移动那个 UFO
1 2 3 4 5 6 7 8 9 |
local ufo = display.newCircle( 340, 15, 10 ) local timeToMove = 5 -- 动画持续 5 秒 local fps = 60 local numberOfTicks = fps * timeToMove -- 即 300 个刻度 local finalDestination = -20 local distanceToMove = ufo.x - finalDestination -- 即 360 个点 local pointsPerTick = distanceToMove / numberOfTicks -- 每个刻度 1.2 个点 |
这种设置可能比您编写的更冗长,但它有助于解释所涉及的数学原理。计时基于时钟/帧“刻度”,因此在 60 fps 的游戏中,每秒将触发 60 个事件。如果我们想在 5 秒内将我们的 UFO 移动穿过 320 点的屏幕,我们必须精确计算出在每个刻度上移动多少。
点与像素
Corona 使用在 config.lua
中通过 width
和 height
定义的虚拟“内容区域”大小(见上文)。但是,这通常不会精确“映射”到真实设备上的像素宽度。例如,iPhone 6 屏幕的宽度为 750 像素,而 iPad Air 屏幕的宽度为 1536 像素。因此,对于 UFO 在屏幕上的基于帧的动画,将每个位置更改转换为点会很有用。
在此示例中,我们总共有 300 个“刻度”来移动 UFO 穿过屏幕,因为 fps 为 60,我们将使其在 5 秒的时间内移动 (60 × 5 = 300
)。UFO 的起始 x 位置为 340,这比内容区域的右侧偏移 20 个点 (320+20
),其目的地 x 位置为 -20,这比内容区域的左侧偏移 20 个点。因此,实际移动距离为 360 个点 (340 − -20 = 360
)。由于我们需要知道每个刻度移动对象的点数,我们只需将距离除以刻度数 (360/300)
即可得到结果 1.2
。
使用“enterFrame”事件
现在我们已经计算出了移动 UFO 的方法,让我们编写一个在每个刻度上执行的函数
1 2 3 4 5 6 7 8 9 |
function ufo:enterFrame() self:translate( -pointsPerTick, 0 ) if self.x <= finalDestination then Runtime:removeEventListener( "enterFrame", self ) end end Runtime:addEventListener( "enterFrame", ufo ) |
在该函数之外 — 在此示例的最后一行 — 我们将 "enterFrame"
事件监听器添加到 Runtime,并将 ufo
显示对象作为目标传递。这将启动基于帧的动画过程。
对于函数声明,我们使用 :
运算符将该函数“附加”到 UFO 对象,以便传递该对象。这被称为 Lua 对象方法,通过这样做,我们可以将函数内的 UFO 对象作为 self
访问。
在函数内部,在每一帧中,我们希望将 UFO 向左移动。已知 pointsPerTick
值为 1.2
,只需使用 object:translate() API 从对象的 x 坐标中减去该值即可。额外的代码会检查 UFO 是否经过了 -20 的最终目标点,如果经过,则会移除 "enterFrame"
事件侦听器,从而有效地停止 UFO 的移动。
当然,这仅执行沿 x 轴的非常简单的运动,这可以通过过渡轻松完成,但基于帧的动画可能用于更复杂的场景,例如按模式移动对象、来回重复移动敌人、滚动游戏背景等。
增量时间
请注意,在使用基于帧的动画时,可能需要考虑“增量”时间波动。这是因为,在内部,不能保证帧精确地按时触发,并且取决于您的应用中发生的其他活动量,这种不精确性会影响整体动画。如果需要更高的精度,请参阅本教程,其中概述了如何利用增量时间。
结论
如您所见,"enterFrame"
事件为 Corona 中动画对象提供了另一种选择,尤其是在所需的动画超出单个过渡的能力时。
Thomas Vanden Abeele
发布于 08:02,8 月 26 日有点短,但提到 enterFrame 事件很酷 - 我认为这是游戏(代码)设计中最强大的概念,也是每个初学者都应该努力掌握的东西。