译文|GAMEMAKER STUDIO 系列:构建更好的动画系统

作者:highway★
2018-12-03
21 18 2

作者:Nathan Ranney

翻译:highway★

在开发 Kerfuffle(译注:游戏挂了,过于追求视觉效果、没钱、再加上一些其他问题,他们现在在做 Knight Club)时,我需要一个动画系统,允许我在游戏中保持任何单独的动画帧(译注:格斗游戏/动作游戏为了提升打击感,会采用帧冻结的技术),而无需手动添加或删除精灵帧。 我还需要能够根据当前动画帧触发某些动作。 使用此设置,我可以创建 hitbox,播放声音或更改状态,同时完全控制屏幕上绘制的所有内容。

变量

这些是与动画系统相关的重要变量。 如果后面你感到困惑,请回头再仔细看看。


frameSpeed 

The speed in FPS that the game counts through the frames to be displayed. Default is 1.

游戏通过要显示的帧计算的 FPS 速度。 默认值为 1。


frameCounter 

Increases every frame by the frameSpeed.

每帧按 frameSpeed 递增


currentFrame 

The frame of the sprite currently being drawn on the screen.

当前正在屏幕上绘制的精灵帧


frameData 

Current list of frame data the game is reading from, based on the animation that needs to play. Idle, run, attack, etc.

游戏正在读取的当前帧数据列表,基于需要播放的动画。 如空闲,奔跑,攻击等


frameDuration 

Total number of in game frames to display the current sprite frame.

显示当前精灵帧的游戏帧总数。


maxFrames 

The total number of frames in any given sprite.

任何给定精灵中的帧总数。


animSprite 

The actual name of the sprite resource in GameMaker. sprMomo_Idle, for example.

GameMaker 中精灵资源的实际名称。 例如,sprMomo_Idle。

脚本

后面我们要用到的脚本。

frame_reset();

//将 frameCounter 和 currentFrame 重置为0
frameCounter = 0;
currentFrame = 0;

animation_set();

该脚本接受两个参数。 首先,frameData(相关的帧数据列表)和第二个是 animSprite(你想要绘制的精灵资源)

//animation_set ( argument0, argument1 );

frameData = argument0;
animSprite = argument1;

帧数据

每个动画都需要一个帧数据列表。 这是一个列表,其中包含每帧动画播放的游戏帧数量。 每个数据列表都使用以下命名约定。 frameDataIdleframeDataRunframeDataDash 等。 

(译注:如果你初次接触这些并对这些内容感兴趣,可以扩展阅读一下,google 搜一下街霸系列的 frame data,会有很多更详细的资料。btw  indienova 如何插入表格呢?)

Momo(我们游戏中的一个角色)空闲动画的帧数据

请注意,所有列表和值都以 0 开头。因此,即使此动画有 12 帧,列表中的最大数字也是 11。这包括你要显示的帧! 如果你希望它在游戏中显示 5 帧,则列表中的值应为 4

** GMS 特定说明**

确保在不再使用时手动删除列表! 否则你可能会遇到内存泄漏!

帧计数器

现在我们有一个帧数据列表,我们需要实际根据该数据设置动画。 我们需要做的第一件事是弄清楚 maxFrame 是什么。

maxFrames = sprite_get_number( animSprite ) - 1;

然后,如果您的 currentFrame 恰好大于或等于最大帧,并当 frameCounter 大于或等于精灵帧应出现在屏幕上的最大帧数,则重置为第一帧。

if ( currentFrame >= maxFrames - 1 && frameCounter == frameDuration ) 
{
     frame_reset();
}

现在 frameCounter 可以完成它的工作。 它计算应该显示精灵的当前帧的帧数,然后一旦达到该最大值,将当前帧切到精灵的下一帧,并重置为 0 以再次开始计数。

frameCounter += frameSpeed;
frameDuration = ds_list_find_value ( frameData, currentFrame );
if ( frameCounter == frameDuration )
{
     currentFrame ++;
     frameCounter = 0;
}

注意:

使用 maxFrames 也是结束动画和更改为新动画或状态的好方法! 在整个攻击动画一直播放之后,我使用 maxFrames 从攻击状态切换回正常状态。

** GMS 特定说明**

sprite_get_number 是一个内置的 GMS 函数,它返回精灵中的帧数。 此函数返回确切的帧数,并且不会从0开始计数! 所以如果你有一个5帧的精灵,这将返回5! 这就是为什么在检查 maxFrames 时,我们这样做,同时从其值中减去1。

切换精灵

Kerfuffle 中的所有内容都运行在一个相当简单的状态机上。 根据角色所处的状态,动画会发生变化。

//存储当前的动画精灵,以便我们稍后检查
currentAnim = animSprite;
switch ( state ) {
     case normal:
          //如果玩家向左或向右,则改为跑步精灵
          if ( left || right )
          {
               animation_set ( frameDataRun, runSprite );
               //如果玩家没有按任何按钮,则更改为空闲精灵
          } else {
               animation_set ( frameDataIdle, idleSprite );
          }
     break;
     case dash:
          //如果玩家状态为前冲,改为前冲精灵
          animation_set ( frameDataDash, dashSprite );
     break;
}
//针对上一个动画检查当前动画。
//如果这些动画不相同,请将 frameCounter 和 currentFrame 重置为0。
if(lastAnim != currentAnim)
{
     frame_reset();
     lastAnim = currentAnim;
}

** GMS 特定说明**

尽可能使用宏或枚举而不是字符串。 我曾经使用字符串作为玩家状态(即:“normal”而不是 normal),这可不是个好主意。 字符串处理速度较慢,如果您输入错字,GMS 可不会提醒你! 你的代码将失效!

有关宏的更多信息,请查看 YellowAfterLife 的这篇文章。

https://yal.cc/gamemaker-on-global-variables/

投入使用

现在我们已经设置完看上面那些玩意儿,我们要开始使用它们。 由于我们绕过了像 image_speedsprite_index 这样的 GMS 内置函数,我们需要自己绘制精灵。 这真的很容易! 我们只需要使用 draw_sprite_ext

//draw 事件

draw_sprite_ext ( animSprite, currentFrame, x, y, 1, 1, 0, c_white, 1 );

就是这样了! 很简单吧? 如果您对如何改进此问题有任何疑问或意见,请告诉我们。我会尽快回复。如果你还有什么其他想了解的内容,请告诉我~


Follow me on Twitter!

本文为用户投稿,不代表 indienova 观点。

近期点赞的会员

 分享这篇文章

您可能还会对这些文章感兴趣

参与此文章的讨论

  1. Rusty 2018-12-05

    很适合格斗游戏这种必须精确到帧的类型

  2. 陈康 2019-01-17

    这篇文章意犹未尽

您需要登录或者注册后才能发表评论

登录/注册