2D 精灵动画

简介

在本教程中,你将学习如何使用 AnimatedSprite(动画精灵)类和 AnimationPlayer 来创建 2D 动画角色。无论是自己绘制还是直接下载现成的,拿到的动画角色素材一般就是两种形式:一系列单独的图片,或者一张包含所有动画帧的精灵表(Sprite Sheet)。两者都可以使用 Godot 的 AnimatedSprite 类进行动画。

我们首先会用 AnimatedSprite 来把一系列单独的图片做成动画,然后我们再会这个类来把精灵表做成动画,最后我们还会学习使用 AnimationPlayerSpriteAnimation (动画)属性来把精灵表做成动画。

注解

以下示例的美术素材由 https://opengameart.org/users/ansimuzhttps://opengameart.org/users/tgfcoder 共同提供

AnimateSprite 与若干单独的图片

在这个情况下, 你有一组图像, 每一个都包含你角色的动画的一帧. 对于这个例子, 我们将使用以下动画:

../../_images/2d_animation_run_preview.gif

你可以在此处下载此示例项目: 2D_movement_demo.zip

解压缩这些图像并将它们放在项目文件夹中. 使用以下节点布置场景树:

../../_images/2d_animation_tree1.png

注解

根节点也可以是 Area2DRigidBody2D . 动画仍然会以同样的方式制作. 一旦动画完成, 你可以为CollisionShape2D形状分配一个形状. 更多信息请参见 Physics Introduction .

现在选择 AnimatedSprite , 并在它的 SpriteFrames(精灵帧) 属性中, 选择"新建 SpriteFrames".

../../_images/2d_animation_new_spriteframes.png

点击新的 SpriteFrames 资源, 你会看到一个新的面板出现在编辑器窗口的底部:

../../_images/2d_animation_spriteframes.png

从左边的文件系统面板, 将这8张独立的图片拖放到SpriteFrames(精灵帧)面板的中间部分. 在左边, 将动画名称从 "默认" 更改为 "奔跑".

../../_images/2d_animation_spriteframes_done.png

回到属性面板, 在 Playing(播放) 属性的复选框里打勾. 您现在应该可以看到在视区中播放的动画. 然而, 它有点慢. 为了解决这个问题, 更改SpriteFrames面板中的 速度 (FPS) 为10.

你可以通过点击 "新建动画" 按钮并添加其他的图像, 来添其他的动画.

控制动画

动画完成后, 你可以通过代码中的 play()stop() 方法控制动画. 这里有一个简单的例子, 按住右方向键播放动画, 松开后就停下.

extends KinematicBody2D

onready var _animated_sprite = $AnimatedSprite

func _process(delta):
    if Input.is_action_pressed("ui_right"):
        _animated_sprite.play("run")
    else:
        _animated_sprite.stop()
public class Character : KinematicBody2D
{
    private AnimatedSprite _animatedSprite;

    public override void _Ready()
    {
        _animatedSprite = GetNode<AnimatedSprite>("AnimatedSprite");
    }

    public override _Process(float delta)
    {
        if (Input.IsActionPressed("ui_right"))
        {
            _animatedSprite.Play("run");
        }
        else
        {
            _animatedSprite.Stop();
        }
    }
}

AnimateSprite 与精灵表

你还可以很方便地使用 AnimatedSprite 把精灵表做成动画。我们会用到这张公共领域的精灵表:

../../_images/2d_animation_frog_spritesheet.png

右键单击图片, 选择 "图片另存为" 来下载图片, 然后将图片复制到项目文件夹中.

设置场景树的方法与之前使用单独图片的时候相同。选中 AnimatedSprite 后在 SpriteFrames (精灵帧)属性里选择“新建 SpriteFrames”。

点击创建出来的 SpriteFrames 资源。底部面板出现后,这次我们选择“从精灵表中添加帧”。

../../_images/2d_animation_add_from_spritesheet.png

在弹出的打开文件对话框中,选择你的精灵表。

接下来会打开一个新的窗口,里面会显示刚才的精灵表。你首先需要修改精灵表中纵向和横向的图片数量,我们的这张精灵表里横向有四张图片、纵向有两张。

../../_images/2d_animation_spritesheet_select_rows.png

然后,在精灵表中选择动画中想要包含的帧。这里我们选中上面的四个,然后点击“添加 4 帧”来创建动画。

../../_images/2d_animation_spritesheet_selectframes.png

现在你就可以看到在底部面板的动画列表里看到这个动画了。双击 default(默认),然后把动画的名称改成 jump(跳跃)。

../../_images/2d_animation_spritesheet_animation.png

最后,在检查器中勾选 AnimatedSprite 的 Playing(播放)就可以看到青蛙跳起来了!

../../_images/2d_animation_play_spritesheet_animation.png

AnimationPlayer 与精灵表

在使用sprite sheet(精灵清单)时, 另一种方法是使用标准的 Sprite 节点来显示纹理, 然后用 AnimationPlayer 来实现从纹理到纹理的动画变化.

考虑一下这个sprite sheet, 包含6帧动画:

../../_images/2d_animation_player-run.png

右键单击图片, 选择 "图片另存为" 下载图片, 然后将图片复制到项目文件夹中.

我们的目的是, 循环着一个接一个地显示这些图像. 首先布置你的场景树:

../../_images/2d_animation_tree2.png

注解

根节点也可以是 Area2DRigidBody2D . 动画仍然会以同样的方式制作. 一旦动画完成, 你可以为CollisionShape2D形状分配一个形状. 更多信息请参见 Physics Introduction .

将sprite sheet拖拽到Sprite的 Texture 属性里, 你会看到整个清单显示在屏幕上. 要把它分割成单独的帧, 在属性面板中展开 Animation 部分, 将 Hframes 设置为 6 . HframesVframes 是sprite sheet中水平和垂直帧的数量.

../../_images/2d_animation_setframes.png

现在尝试更改 Frame 属性的值. 你可以看到它的范围从 05 ,Sprite 所显示的图像也随之改变. 这就是我们想要动画化的属性.

选中 AnimationPlayer , 然后点击 "动画" 按钮, 然后点击 "新建" 按钮. 将新动画命名为 "walk". 将动画长度设置为 0.6 , 点击 "Loop" 按钮, 让动画重复播放.

../../_images/2d_animation_new_animation.png

现在选中 Sprite 节点, 并单击钥匙图标, 添加一个新的动画轨道(track).

../../_images/2d_animation_new_track.png

继续在时间轴的每一点添加帧(默认为 0.1 秒), 直到你得到了从0到5的所有帧. 你会看到这些帧出现在动画轨道(animation track)上:

../../_images/2d_animation_full_animation.png

按下动画上的 "播放" 键, 看看效果如何.

../../_images/2d_animation_running.gif

控制AnimationPlayer动画

正如AnimationSprite一样, 你可以通过代码中的 play()stop() 方法控制动画. 同样, 这里有一个简单的例子, 按住右方向键键播放动画, 松开后就停下.

extends KinematicBody2D

onready var _animation_player = $AnimationPlayer

func _process(delta):
    if Input.is_action_pressed("ui_right"):
        _animation_player.play("walk")
    else:
        _animation_player.stop()
public class Character : KinematicBody2D
{
    private AnimationPlayer _animationPlayer;

    public override void _Ready()
    {
        _animationPlayer = GetNode<AnimationPlayer>("AnimationPlayer");
    }

    public override void _Process(float delta)
    {
        if (Input.IsActionPressed("ui_right"))
        {
            _animationPlayer.Play("walk");
        }
        else
        {
            _animationPlayer.Stop();
        }
    }
}

注解

如果同时更新一个动画和一个其他的属性(比如说, 一个平台跳跃游戏可能会更新sprite的 h_flip/v_flip 属性然后同时开始一个 "转身" 动画), 要记住 play() 不是即时生效的. 它是在下次 AnimationPlayer 被处理时生效的. 这可能要到下一帧, 导致现在这一帧变成 "错误" 帧-应用了属性的变化, 但动画还没有开始. 如果这会造成麻烦的话, 在调用 play() 后, 你可以调用 advance(0) 来立即开始播放动画.

总结

以上的例子演示了使用 Godot 提供的两个类来制作 2D 动画的方法。 AnimationPlayer 相比 AnimatedSprite 而言略显复杂,但同时也提供了更多的功能,因为你还可以同时动画位置和缩放之类的其它的属性。 AnimationPlayer 也可以和 AnimatedSprite 配合使用,你可以自己试一试,看看怎样的做法更适合你自己。