前面几篇我们让 AI 出静态立绘、出了统一风格的贴图、也讲过像素完美渲染。但一个「站着不动」的角色卖不出游戏——玩家要看到它迈腿、起跳、被打飞。今天把「AI 出的图」推进到「AI 出的动画」:一张精灵表(sprite sheet)怎么切、怎么配成 SpriteFrames、帧率定多少才不「癫痫」、脚底锚点怎么对齐才不「抖」——这些都不是「好看」的问题,而是能不能玩的问题。

今日速览

  • 两种素材形态:单帧图片序列(一帧一个 PNG)和精灵表(一张大图里按网格排好所有帧)。Godot 的 AnimatedSprite2D 两种都能直接吃。
  • 核心资源是 SpriteFrames:它是一个 Resource,存「动画名 → 帧序列 + 帧率 + 循环模式」。同一份动画数据可以被多个角色共用。
  • 像素动画别用 60 FPS:跑动 1012 FPS、待机 48 FPS 才「像老动画」;用 60 FPS 逐帧反而僵硬又费素材。
  • 朝向靠翻转,不靠第二套图:AnimatedSprite2D.flip_h = true 就能让角色朝左,省掉一半美术工作量(也是 AI 出图省钱的关键)。
  • 脚底锚点决定生死:角色 pivot 不放脚底、帧间高度不统一 → 跑起来整个人「上下抽搐」。AI 出图前就要把「统一画布、脚底对齐」写进提示词。

核心学习内容

1. 从「一堆 PNG」到 SpriteFrames

最稳的流水线是单帧序列:让 AI 逐帧出图(或从一段生成视频里抽帧),然后在 AnimatedSprite2D 的 SpriteFrames 属性里点 New SpriteFrames,把 8 张跑动帧拖进面板,把动画从 default 改名为 run,Speed (FPS) 设成 10。再加一条 idle、一条 jump,一个能跑会跳的角色就有了。

代码侧只要一个「看状态播哪条动画」的控制器:

extends CharacterBody2D

@onready var sprite: AnimatedSprite2D = $AnimatedSprite2D

func _physics_process(_delta):
    var on_ground := is_on_floor()
    if not on_ground:
        sprite.play("jump" if velocity.y < 0 else "fall")
    elif absf(velocity.x) > 5.0:
        sprite.play("run")
        sprite.flip_h = velocity.x < 0   # 朝左只翻转,不做第二套图
    else:
        sprite.play("idle")

flip_h 这一行,等于白嫖了一整组左侧动画。

2. 精灵表:一张图,切着用

精灵表更省事:一个角色 4×4 网格,一张 hero.png 装完 idle/run/jump/attack。三条路切它——

  • AnimatedSprite2D + SpriteFrames:在帧面板里对同一张图逐个设 region(等于自动建 AtlasTexture)。
  • Sprite2D + hframes / vframes:直接把图切成网格,再用 frame 属性或 AnimationPlayer 驱动当前帧。适合「Sprite2D 的 Animation 属性」这条路。
  • AtlasTexture:手动指定 atlas + region,把一张大图裁成一帧帧;它最大的价值是把许多小图打包进一张大图,省显存、省 draw call。

精灵表最大的坑是间距:AI 出的图常带不等宽的透明边、相邻帧粘连。切帧时要么在生成阶段就约定「严格等距网格、无 padding、每格透明背景」,要么在导入后手动 region 微调。等宽网格是唯一能让「切帧脚本」自动跑起来的前提。

3. 让动画「像」而不是「对」

三个立刻提升观感的细节:

  • 像素图要关过滤:项目设置里把默认纹理过滤设为 Nearest(或导入时逐张设),否则放大后一片糊。上一环的像素完美渲染讲的就是这个。
  • 循环点:跑动首帧接尾帧要能连上;待机太短会「抽搐」,常用「正放 + 倒放」做呼吸感。SpriteFrames 的循环模式(LoopMode)记得设对——jump 这种一次性动画要设成不循环,播完靠 animation_finished 信号切回 fall。
  • 帧率分层:跑动 12、待机 6、受击只闪 1~2 帧——不是所有动画都同一帧率,这才有「打击感」。

🎯 DeepSeek 提示词

提示词 1:让 DeepSeek 写「精灵表批量切帧脚本」

适用场景:AI 出了一张 4×4 网格精灵表,手动切 16 帧太累,想要脚本一键切好。

你是 Godot 4 (GDScript) 工具脚本专家。请写一个编辑器工具脚本(@tool,extends EditorScript),
把一张等距网格精灵表切成独立 PNG 帧。

【输入】sprite_sheet.png,参数:columns、rows、cell_w、cell_h(每格像素)。
【任务】遍历网格,用 AtlasTexture 的 region 逐一裁出每帧,导出为
  res://art/hero/{动画名}_{序号}.png(动画名由参数按行传入,如 ["idle","run","jump","attack"])。
【输出格式】完整 .gd 文件 + 使用说明(如何挂到编辑器运行)。
【约束】
- 用 Image.get_region() + save_png(),不要依赖第三方库。
- 每格四周留 0 padding;若坐标越界要报错而不是静默截断。
- 中文注释,变量命名清晰。

提示词 2:让 DeepSeek 写「动画状态控制器」

适用场景:角色已有 idle/run/jump/fall/hurt 五条动画,想让它们跟随物理状态自动切换。

你是 Godot 4 (GDScript) 游戏程序员,角色根节点是 CharacterBody2D,子节点含 AnimatedSprite2D。
请写一个动画控制器,逻辑规则如下:

【切换规则(优先级从高到低)】
1. hurt 生效期间播放 hurt,且 animation_finished 才恢复;
2. 不在地面:velocity.y < 0 播 jump,否则播 fall(jump/fall 不循环);
3. 在地面且 abs(velocity.x) > 5:播 run,并按 velocity.x 正负设置 flip_h;
4. 其余:播 idle。
【输出格式】完整 .gd 片段 + 一段说明「为什么不能在 _input() 里播动画」。
【约束】
- 同一帧内不要重复调用 play()(会重置动画,导致卡帧)——请用「记录当前动画名,变了才 play」的写法。
- 中文注释,保留我的命名风格。

提示词 3:让 DeepSeek 当「精灵表提示词工程师」

适用场景:要让绘图 AI(即梦 / Stable Diffusion / Midjourney)出一张能直接用的精灵表,先把「出图 Prompt + 动画清单」编好。

你是 2D 像素游戏的美术技术总监。请为角色【填写:比如「戴斗篷的森林游侠」】产出一份可直接使用的
像素动画精灵表制作方案。

【任务 1】给出动画清单表:动画名 | 帧数 | 播放帧率 | 是否循环 | 循环点说明。
  至少覆盖 idle / run / jump / fall / attack / hurt。
【任务 2】给出一段英文出图 Prompt,要求:
  - 严格 4x4 或 1xN 等距网格,每格 32x32,纯透明背景,无边框,无 padding;
  - 固定风格锚(调色板 ≤16 色、2px 描边、正面朝右);
  - 明确每个格子里该画哪一帧动作。
【任务 3】给出 3 条「防止风格漂移」的负面提示词(negative prompt)。
【输出格式】三段,可整段复制。
【约束】只针对我填写的角色;网格与尺寸必须显式写死,不要用「合适的大小」这种模糊表述。

学习心得

美术是独立开发者最容易卡死的地方,而「动画」又是美术里最贵的一环——请人做一套角色动画动辄上千元。今天这条路子的价值在于:它把「动画」拆成了「AI 出帧 + 规则约束 + 引擎播放」三段,每一段都能用提示词和参数说清楚。对「用 DeepSeek 做一款 Steam 游戏」这个目标来说,这意味着你不再需要会画画,只需要会定规范:网格尺寸、帧率、循环点、锚点、翻转——这五条写进提示词,DeepSeek 和绘图 AI 就能替你产出「足够好玩」的动画。记住:玩家能容忍「简陋」,但绝不原谅「抽搐」。

明日关注(checklist)

  • 挑一个角色,用「提示词 3」产出动画清单 + 出图 Prompt,先只做 idle 和 run 各 4 帧跑通全流程。
  • 把 SpriteFrames 的 jump 设为不循环,接上 animation_finished 信号,验证「落地自动切回 idle」。
  • 在项目设置里把默认纹理过滤改成 Nearest,对比开关前后像素图的清晰度差异。

本文由自动化学习流水线每日生成,坚持即学即记。