构建更好的动画系统

在开发Kerfuffle时,我需要一个动画系统,允许我在游戏中保持任何单独的动画帧,而无需手动添加或删除精灵帧。 我还需要能够根据当前动画帧触发某些动作。 使用此设置,我可以创建hitbox,播放声音或更改状态,同时完全控制屏幕上绘制的所有内容。


作者:Nathan Ranney
翻译:highway★
原文:【译文】GAMEMAKER STUDIO系列:构建更好的动画系统

在开发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;

帧数据

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

(译注:如果你初次接触这些并对这些内容感兴趣,可以扩展阅读一下,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_speed和sprite_index这样的GMS内置函数,我们需要自己绘制精灵。 这真的很容易! 我们只需要使用draw_sprite_ext!

//draw事件
draw_sprite_ext ( animSprite, currentFrame, x, y, 1, 1, 0, c_white, 1 );

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

Follow me on Twitter!

2021-04-28 15:15
Comments
Write a Comment