做 2D 游戏动画的人应该都懂一个道理:序列帧是真的贵。
之前我做过一个小型横版格斗 Demo,角色就两个,每个角色只有 5 个动作。原画找朋友画了关键帧,中间帧我自己硬补,一周下来,最大的感受不是眼睛疼,是心疼。24fps 的格斗动画,一秒就要 24 张,就算砍到 12fps,一个三秒的拳招连击也要四十多张。每一张还要保证线条风格一致、形变连贯,纯手绘死磕,纯粹是拿时间去填。后来我看到了 ToonCrafter 这个开源项目——它只靠两张原画,就能生成两帧之间的全部过渡帧,直接输出丝滑的序列帧,还能拼成游戏引擎直接可用的 Sprite Sheet。这篇文章就是我把它接进游戏动画流程后的一些实操记录和踩坑经验,想给自己省时间的手绘动画人、独立游戏开发者,以及做 2D 动态表现的设计师,都可以照着跑一遍。
1. 项目核心思路与设计拆解
1.1 序列帧动画:每个游戏人都绕不开的成本大山
先说为什么序列帧那么贵。动画本质上就是连续的静止画面快速播放,游戏里的 2D 动画通常会把一套动画的所有帧放在一张纹理图集里,也就是 Sprite Sheet。引擎从左上角开始按行列切割,然后按照帧率一张接一张播放,就形成了角色动画。
这套流程看着简单,但“把中间帧画出来”是所有成本的核心。原画师只需要画动作的起势、极限拉伸、落地这种关键 pose,中间那十几二十张过渡姿势,全部要动画师手动补。画完还得一遍遍翻着检查有没有跳帧、曲线是否圆润、线条粗细是否一致。哪怕你对着原画认真补,稍不留神一个手抖,动作就会卡一下,前期看不出来,跑起来特别明显。
更麻烦的是风格统一。卡通角色线条少、颜色平,反而对形变要求更高。你看一个角色挥手,关键帧只有手臂抬起来和放下落这两个状态,中间帧如果只是机械地按位置插值,手臂就会像一条僵硬的棍子。真正的动画中间帧是有缓动、有挤压拉伸的,这恰恰是手绘动画不可替代的价值——也是最烧钱的地方。所以当自动补帧工具出现的时候,很多人的第一反应是:终于能省中间帧的钱了。
1.2 ToonCrafter 核心原理:为什么不走光流插值的路子
但真要拿自动补帧说事,还得先看清楚算法老路子的天花板。市面上很多视频补帧算法走的是光流路线:先用光流估计出相邻两帧里每个像素跑到了哪里,再沿着运动轨迹算出中间像素的位置。这套方案在实拍视频里表现很好,但在卡通动画上基本会翻车。卡通角色大色块、线条简单,很多时候一个转身、一个踢腿,像素的对应关系是断裂的,光流根本找不到“谁去了哪儿”。你硬插,得到的就是模糊的残影。
ToonCrafter 走的是另一条完全不同的路:把中间帧当成一个“按条件生成”的问题。简单说,扩散模型见过大量动画画面,它可以用生成的方式“想象”出两张关键帧之间最可能出现的画面。你说不清中间那一刻角色手臂精确的角度是多少,但模型不需要你说清,它自己会去推理。这种生成式补帧天然就更适合卡通动画——因为卡通动画本来就没有真实物理运动,它追求的是“看起来舒服”的运动节奏。
从工程角度看,这个思路绕过了光流最难处理的对象对应关系。角色转身时前后的身体部位在像素级没有对应关系,但语义上又确实是一个人在运动。光流插值解决不了语义级别的运动,生成模型反而可以。这也是为什么同样两张图,传统补帧工具出来的是一团糊,ToonCrafter 出来的是动作自然的中间帧。
1.3 两种模式:普通补帧与草图引导
项目里有两个模式,一个是直接补帧,输入首尾关键帧直接生成中间帧;一个是草图引导,在输入图之外再给一张只有轮廓的草图,表达你想让角色哪个部位怎么动、往哪个方向动,模型就能生成一段循环的、带有轻微动态的动画。草图模式在游戏里最大的用途是做 keep-alive 动画——就是角色原地待机的时候,呼吸、头发飘、尾巴甩、裙摆晃动这类不需要夸张动作、但必须“活”着的循环动画。这类动画以前为了省钱经常一个 sprite 挂在那一动不动,现在可以低成本做出来了。
两个模式的底层模型是同一套生成式架构,但推理方式有区别:普通补帧更像视频插帧任务的生成化变体,草图引导则把草图的线条当成了控制条件,告诉模型“这里需要有运动倾向”。实际用下来,草图引导对帧间稳定性的帮助非常大,不只是能控制动作,还能压住背景闪烁。如果你只是随便丢两张图进去直接补帧,出来的动画可能闪得没法看;加了草图引导之后,画面会明显稳很多。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备:把项目跑起来需要什么条件
2.1 硬件门槛:你的显卡到底行不行
先说结论:跑 ToonCrafter 不需要特别夸张的算力,但也别指望核显能做。官方推荐的高显存模式需要 24GB 左右显卡,不过项目提供了低显存模式,实际测试下来 8GB 显存也能用 512×512 的分辨率一次生成 12~16 帧。我甚至见过有人用 6GB 卡,把分辨率降到 384 跑了短序列。
如果你要用草图引导模式,显存压力比普通补帧还要大一点,因为模型需要额外编码一张草图输入。我的建议是:在 8GB 显存左右的机器上跑项目,优先开启低显存模式,不要同时开太多后台应用,同时把最终需要的高分辨率任务拆成低分辨率生成加后期放大两步走。这样体验是最顺的。另外需要注意,项目主要面向 Linux 环境开发,Windows 下用原生环境跑容易遇到各种底层库的兼容问题,最省心的方式是用 WSL 2 或者直接装一个 Linux 双系统。
2.2 从克隆仓库到跑出第一帧:依赖安装全流程
安装流程其实不复杂,主要分三步:建环境、装框架、拉项目和权重。给一个参考命令流程:
bash复制conda create -n tooncrafter python=3.10 -y
conda activate tooncrafter
# 按自己显卡驱动版本安装对应 CUDA 的深度学习框架
pip install torch --index-url https://download.pytorch.org/whl/cu121
# 克隆项目,注意要带上子模块
git clone --recurse-submodules <ToonCrafter项目仓库地址>
cd ToonCrafter
pip install -r requirements.txt
pip install -U opencv-python xformers
克隆这一步很容易踩坑,项目里引用了若干子模块,如果直接 git clone 不带 --recurse-submodules,后面运行的时候会莫名其妙找不到某个模块。如果你已经克隆完了才发现没带子模块,可以进目录执行一次 git submodule update --init --recursive 补救。
深度学习框架的版本一定要和显卡驱动、CUDA 版本对应。买新显卡用新驱动,装新版本框架问题不大;老显卡老驱动,硬上最新框架反而会因为编译产物不兼容跑不起来。建议先用 nvidia-smi 看一下驱动支持的最高 CUDA 版本,再回去选对应的安装命令。
2.3 模型权重和目录结构:别把文件放错位置
项目跑起来之前还需要把模型权重放到指定目录。权重文件一般分两种:普通插帧模型和草图引导模型,文件名里通常带有 interpolation 和 sketch 字样。下载好之后按官方说明放到 checkpoints 目录下,目录结构大致像这样:
text复制ToonCrafter/
├── checkpoints/
│ ├── tooncrafter_512_interpolation/
│ └── tooncrafter_512_sketch/
├── configs/
│ └── inference_512_v1.yaml
├── data/
└── tooncrafter.py
权重放错位置最常见的报错就是加载模型时找不到对应路径。如果你看到类似 checkpoint not found 或者路径不存在的报错,别急着怀疑模型坏了,先检查一下当前工作目录和 checkpoints 目录的相对关系。不少人把权重下到了下载文件夹,再复制到项目里时多套了一层目录,模型加载器就找不到了。把权重目录直接放在项目根目录下的 checkpoints/ 里,命名保持和官方一致,基本不会出问题。
3. 实操过程:从两张原画到一张精灵图集
3.1 准备输入图像:原画不是随便丢两张就能用
输入图的质量直接决定生成结果的上限。我建议至少满足三个条件:分辨率接近 512 的倍数,最好是正方形或者接近正方形;首尾帧的角色大小相差不要太大;背景尽量干净。
第三条特别重要。如果输入的两张原画背景有大量细节,比如复杂的地板纹理、很多杂乱的装饰物,模型在生成中间帧时会把精力浪费在处理背景的变化上,角色的边缘就容易糊。所以我推荐把角色放在纯色或者简单渐变背景上生成,得到稳定序列之后再在后期合成场景背景。
另一个容易被忽略的细节是首尾帧的角色位置。让角色身体位移超过画面 1/3 的输入,直接生成的中间帧大概率会在半路出现重影、断肢这类问题。这是生成模型不确定性的根源,不是 bug。你可以把一段大动作拆成两段小动作,每段用同一个工具生成,最后拼接成完整动作。
3.2 直接补帧:一次拿下一段基础动作
准备好人物的起手式和收招姿势两张原画后,直接补帧的命令大概长这样:
bash复制python tooncrafter.py \
--config configs/inference_512_v1.yaml \
--input data/start.png data/end.png \
--output outputs/sword \
--frame_num 8 \
--seed 2024
其中 frame_num 控制生成多少张中间帧,seed 是随机种子。同一个种子结果可复现,不同种子差别非常大,有时候换个种子画面就从花的变成稳的。我建议每个动作至少跑三个种子,快速翻看结果,选最稳定的一版继续往下走。
生成完在 output 目录下会看到 final.mp4 和一个 frames 文件夹,frames 里就是按顺序排列的 PNG 序列,这些就是后续拼 Sprite Sheet 的原料。如果你开启了循环模式,生成的帧会做成首尾相接的循环动画,方便做待机特效等重复播放的动作。
3.3 草图引导:做一个游戏里常驻的待机循环动画
草图引导模式是我个人觉得最值钱的功能。做待机动画时,你需要准备两张图:一张是角色的关键帧原画,一张是画好的草图。草图不需要画得多精细,只要能用线条把想要发生的运动表达清楚:比如头发飘动方向用几条曲线、手臂摆动用一条虚线、裙摆的起伏用两道弧线。模型会参考这些运动倾向去生成一个循环动画。
命令类似这样:
bash复制python tooncrafter.py \
--config configs/inference_512_v1.yaml \
--input data/idle.png \
--sketch data/idle_sketch.png \
--output outputs/idle \
--loop \
--frame_num 16 \
--seed 7
这里有一个实际经验:草图的运动幅度宁可画小不要画大。画得太大,模型会在中间帧里给你做出奇怪的夸张形变,让人觉得角色在抽搐。小幅度的呼吸感、点头感,反而最自然。如果你第一次生成的循环动画看起来有点僵,不要急着改参数,先检查草图里画的那几笔运动方向是不是自己想要的方向。草图的一个弧线角度不对,动画里的角色可能就一直在朝向奇怪的方向飘。
3.4 用脚本拼出 Sprite Sheet 并导入引擎
生成完 frames 目录后,可以用 Python 和 Pillow 把帧序列拼成一张网格图:
python复制from PIL import Image
import os
frame_dir = "outputs/idle/frames"
out_path = "sprite_sheet_idle.png"
files = sorted([f for f in os.listdir(frame_dir) if f.endswith(".png")])
if not files:
raise SystemExit("没有找到帧文件")
cols = 6
frames = [Image.open(os.path.join(frame_dir, f)) for f in files]
w, h = frames[0].size
rows = (len(frames) + cols - 1) // cols
sheet = Image.new("RGBA", (cols * w, rows * h), (0, 0, 0, 0))
for i, frame in enumerate(frames):
x = (i % cols) * w
y = (i // cols) * h
sheet.paste(frame, (x, y))
sheet.save(out_path)
print(f"已生成 {len(frames)} 帧,共 {rows} 行,列宽 {cols}")
这段脚本假设所有帧尺寸一致,统一按从左到右、从上到下排列。游戏引擎读取 Sprite Sheet 时会按像素尺寸去切割,你的帧尺寸是多少,就在导入时把子图宽度和高度填成多少。
帧率的设置也可以算一下:假设你输入两张关键帧、生成 8 帧中间帧,整个序列一共 10 帧。如果你希望这段动作播放时长为 0.5 秒,那么帧率就是 10/0.5=20fps。在引擎里直接把动画帧率设成 20 就行。主流游戏引擎的 Sprite 编辑器基本都支持网格切割,比如按 Grid by Cell Size 自动切片,切完拖进动画轨道,设置循环播放,一套序列帧动画就落地了。
4. 问题排查:生成崩坏怎么救
4.1 帧与帧之间疯狂闪烁怎么办
生成的动画单独看每一帧都正常,连起来像眨眼一样闪,多半是帧间一致性出了问题。模型在推理每一帧的时候会有随机性,尤其是背景细节和线条边缘,每一帧可能都会有一点细微差异,累积起来就成了闪烁。
我的处理方式有三个。第一,固定 seed 多跑几轮,把几组结果用播放器翻着看,选最稳的一组。第二,把采样步数调大,默认二十步可以升到五十步,画面会更收敛,闪烁会减轻。第三,如果背景闪得不可救药,就在后期把背景单独抠出来,动态帧只生成前景角色,再合成回去。最后一个方法虽然多一步,但在做游戏美术资源时本来就经常要抠图,属于顺手操作。
还需要注意,直接补帧模式下闪烁问题相对明显,换用草图引导模式会显著改善。草图线条作为控制条件,相当于每次生成都有一个参照信息在约束画面结构,帧与帧之间的结构差异自然就变小了。
4.2 大动作场景下角色形态崩坏怎么拆解
首尾帧的动作差距太大,中间帧就容易出现肢体扭曲、多手多脚的情况。处理思路不是继续调参,而是拆段。把一段动作拆成 A 到 B、B 到 C 两段,B 是你自己调整出的中间关键帧,分别生成后再按顺序拼起来。动作拆分后每段帧数量减少,生成稳定性会明显提高。
一个参考标准是:单次生成的动作位移尽量别超过画面高度的三分之一。超过这个尺度,模型需要在中间“脑补”太多内容,容易自己发挥。如果要做一个转身 180 度的动作,最好在转身一半的位置再补一张原画作为第二段起点,这样每一段都只处理半个转身,生成质量会稳定得多。
拆段拼接的时候注意帧率统一。两段分别生成后再导出时,每段的播放时长要保持一致,否则拼接后的动作节奏会突然变速。这个可以在引擎里通过调整帧率或插入关键帧数量来补救,但最好在导出前就统一好。
4.3 显存不够:低配机器也有救
--low_gpu_mem_mode 这个参数一定别忘了。开启后推理速度会慢一些,但显存占用下降非常明显。如果还是不够,那就把分辨率从 512 降到 256 或者 384 生成,之后再对每一帧做超分放大。这个方法我用下来效果不错,线条会稍微软一点,但整体动作轮廓是完整的。
另一种策略是分批生成。不要一次生成 16 帧,而是分几次,每次只生成 4 到 8 帧,然后把分段帧序列按顺序拼接。拼接时注意让前后段的动作尽量衔接自然,必要时可以重叠一帧做交叉淡化。这项操作可以用开源视频处理工具配合脚本完成,也可以直接写个简单的文件重命名脚本,把多段帧目录合并成连续序号。
4.4 常见问题速查表
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
| 帧间闪烁 | 推理随机性、时序约束弱 | 固定 seed、提高采样步数、开启草图引导 |
| 断肢/形变 | 首尾帧动作跨度太大 | 拆段生成、加入中间关键帧 |
| 显存不足 | 分辨率/帧数/采样步数过高 | 开启低显存模式、降分辨率、分批生成 |
| 边缘模糊 | 背景复杂或输入分辨率不足 | 简化背景、提高输入分辨率、后期放大 |
| 循环动画不流畅 | 首尾位置对不齐 | 检查输入帧是否首尾一致、减少运动幅度 |
| 生成速度慢 | 采样步数多、显卡算力有限 | 用半精度推理、减少步数、开低显存模式 |
5. 实际项目中的玩法与边界
5.1 把 ToonCrafter 插进游戏动画生产管线
如果你做的是横版动作游戏、独立 RPG 或者互动叙事项目,这个工具能直接插进管线里。最省成本的用法是做原型验证:先画关键帧,拿 ToonCrafter 出动态效果,丢进游戏里看节奏对不对,满意了再让动画师补正式帧。美术资源的返工成本通常比补帧成本高得多,先用生成帧把节奏定死,后面所有美术环节都按这个节奏展开,团队协作会顺畅很多。
待机动画也适合用它做。这类动画数量多、动作幅度小、重复度高,过去经常被砍掉,角色站在那里像木桩。现在可以用草图引导模式做一套呼吸循环、一套情绪待机,替换掉静态图,游戏里角色会鲜活不少。实测下来,草图引导模式生成的循环动画几乎可以直接用,只需要在引擎里设置好循环和帧率。
5.2 衔接工具链:从关键帧到正式序列帧
关键帧的产出不一定非要手绘。你可以从骨骼动画里导出两个关键姿态,把模型摆成 A 姿势和 B 姿势各渲染一张图,输入给 ToonCrafter 生成中间帧,相当于用生成模型补出骨骼动画里最难的形变过渡姿态。也可以从参考视频里抽两帧合适的画面作为首尾,生成一段动态后,再交给美术做风格化处理。
给多个动作做批量生成时,我建议用同一个 seed。同一个 seed + 同一组参数,可以缩小不同动作之间的风格差,让角色在切换动作时线条粗细和风格更统一。如果每个动作用的 seed 不一样,画面风格会产生细微差异,在游戏里会显得角色不稳定。
后期和传统动画工具也没有冲突。生成的帧可以进图像处理软件刷一遍线条,统一粗细,也可以先做一次风格转换,让所有帧看起来更像一个动画师手绘的成品。拼好 Sprite Sheet 之后,直接在引擎里设置帧率、循环属性,一套序列帧动画资源就完整落地了。
5.3 哪些场景别指望它:明确工具的边界
这个工具解决的是“中间帧怎么画”,不是“这个动作该怎么设计”。动作的表现力、节奏、夸张程度仍然依赖人的创作。生成结果没法保证百分百可用,尤其复杂的透视变化和非刚性物体,比如飘带、尾巴、裙子大幅摆动,仍然需要人工修正。它更像一个给美术团队用的加速器,而不是一个全自动动画师。
如果是商业项目,使用前要把模型许可和素材版权看清楚。生成模型的权重和推理代码可能是开源可用的,但拿生成结果做商业游戏,需要确认模型本身的许可是否覆盖商业场景。这个一定要在项目立项阶段就让负责法务或者商务的人去确认,别等美术资源都做完才发现不能用。
如果需要极其严格的帧间一致性和可复现性,比如要精确控制每一帧的线条和颜色,那还是得靠手绘或者骨骼动画。生成模型适合的是“先出效果、再求完美”的流程,而不是对每个像素都有硬性要求的流水线。
最后说点个人的实在体会。我一开始拿到这个项目,心里想的是“丢两张图进去就能白嫖一套动画”,实际跑通之后才发现,要想结果稳定,关键反而是准备阶段:关键帧选得好不好、草图引导画得准不准、动作跨度拆得细不细。工具把“一张一张画”的时间省下来了,但把“思考动作怎么做”的责任留给你了。
另一个小技巧是:做待机循环动画时,先不管那些花里胡哨的参数,直接把草图引导模式开起来,从 8 帧、同一个 seed 开始跑,能稳定出动态再往上加帧数和调整运动幅度。每次改草图之后 seed 保持不变,对比起来才不会乱。多试几个 seed,挑一版最顺眼的,再进引擎调循环节奏,这个过程本身就是在训练你对运动规律的直觉。希望这些记录能帮你少踩几个坑,把精力省下来放在真正重要的动作设计和玩法实现上。
