bangbuilds

code-poem-film

把一首古诗、一阕词(或一句诗、一段短文)做成 20–80 秒的「代码画的水墨手绘风 2D 动画」——一个红点小角色把诗走一遍,题签逐句写字,带朗诵,末句边念边写,红印章收尾;配乐和音效用 numpy 合成,封面是第一帧,最后给上传版和实测帖草稿。用户说「把 XX 这首诗 / 这首词做成动画」「做一条诗词动画 / 古诗动画 / 水墨动画」「像《早发白帝城》《赤壁怀古》那条一样做」「用代码画一首诗」「红点动画」「给这句诗配个动画和朗诵」「再来一首大气点的」时都用这个 skill;用户只给了一句诗、一个诗名、一个词牌,或者只说「下一首」也要用。不适用:有地图、有路线的历史纪录片(走 code-doc-film)、真人口播剪辑、产品宣传片、竖版视频(目前只做横版 16:9)。

bangbuilds 58 4 Updated 3w ago

Resources

8
GitHub

Install

npx skillscat add bangbuilds/code-poem-film

Install via the SkillsCat registry.

SKILL.md

代码诗词动画(code-poem-film)

把一首诗做成一条片子(绝句 20–40 秒,一阕百字的词 60–80 秒):宣纸底、水墨淡彩,一个红点落在纸上、长出眼睛,把这首诗走一遍;每句诗在右上角的题签上随朗诵写出来;最后一句红点自己提笔写,念到哪个字写到哪个字,然后落成一枚红印章。画面每一帧都是代码画的,配乐和音效是 numpy 算的,没有图片素材、视频素材、曲库。

这套做法来自两条成片:《早发白帝城》(examples/qingzhou,七绝,25.5 秒)和《念奴娇·赤壁怀古》(examples/chibi,词,72 秒)。它们翻过的车——猿挂的树悬空、鼓把旋律盖住、多音字怕念错、开头是白纸导致缩略图是白纸、一帧出错后画面静默卡住、小舟画在了礁石「上面」、淡出的东西压住片尾小字——现在都由引擎、检查脚本和 references/qa.md 兜住。你的工作是把这首诗的画面想好、演好,而不是重新踩一遍。

它擅长什么、不擅长什么

擅长:绝句、律诗,百字以内的词,一句名句,几十字的短文。有景、有动作、有转折的最好做(行舟、登楼、望月、送别、怀古、江雪)。画法库里有:山(远峰、群山、峡谷绝壁、渐隐的远山、石峰)、岸、江水、长浪、开阔水面、巨浪和浪花、日、月、星、祥云、雾、雨、雪、飞鸟、松、柳、城楼、多层楼阁、残垒、带帆小舟、乌篷小舟、战船、火、烟、人物剪影(文士、武将、仕女)、猿、酒杯、手卷的装裱,以及溅水、浪头、落地的墨点。看 references/kit-1.jpg … kit-4.jpg 就知道库里有什么、长什么样。

不擅长:人物的戏(库里的人是没有脸的远景剪影,只能站、举手、转身;主角永远是红点);一百字以上的长文(按每秒不到 3 个字算,一百字就是 70 秒,再长要节选);竖版(版式是按 1920×1080 排的);抽象说理、没有画面的句子(要先和用户商量画什么)。库里没有的东西可以现画(见 references/scenes.md),但要多花时间,开工前说清楚:赤壁那条现画了 14 样东西,整条从提出到成片用了 63 分钟。

用户只说了一句话时

「做一条《静夜思》的动画」就够开工。默认这样走:

  1. 先定题材。用户没指定诗,就给三四个候选,每个一句话说清「红点在里面干什么」,推荐一个,让用户选。用户指定了诗,就直接进入下一步。
  2. 只问会改变做法的事:发在哪(现在只能横版)、要不要朗诵、男声还是女声。用上次的选择或默认值时说一声就行,不要一项项问。
  3. 用户确认题材后一路做到出片。中途只在两种情况停下来:要花钱(真配音按次计费:先 film.py voice --plan 看会调用几次、多少字,告诉用户)、要画库里没有的大件东西。
  4. 朗诵你听不到。能做的是量:语速、停顿、轻重变化、人声比配乐高多少。选音色拿不准时出两版让用户听,不要替用户说「这个更有感情」。

流程

所有命令在工程目录里运行,tools/film.py 是唯一入口。渲染用 HyperFrames(通过 npx hyperframes 调用),工程也是一个 HyperFrames 工程,但这类片子不需要再走它的问答和分镜流程——这份文档就是完整流程。

# SK = 这份 SKILL.md 所在的目录
python3 $SK/scripts/new_project.py videos/<slug>     # 新建工程:HyperFrames 初始化 + 引擎 + 起步片子 + 笔画数据
cd videos/<slug>
步骤 做什么 命令 / 文件
1 定稿 把诗填进 poem.json:标题、作者、每句、印文、多音字 references/poem-json.md
2 空跑 不花钱:估每句时长,让起步片子先跑起来 film.py voice --dry → film.py build → film.py qa
3 分镜 每句诗画什么、红点做什么、哪个动作卡在哪个字上 写进 film/timing.js 的注释里;references/acting.md
4 画场景 把起步片子里的「长卷」换成这首诗自己的画面 film/timing.js、film/scenes.js;references/scenes.md
5 看图 抓关键帧、看总览图,改到干净 film.py qa;references/qa.md
6 朗诵 真配音(缓存,同一段只花一次钱;长篇逐句合成);读得太快的句子本地放慢;末句对着频谱图标每个字的时间 film.py voice --plan → film.py voice --spec;references/audio.md
7 编曲 每个声音绑在时间表的一个时刻上 film/score.py → film.py score
8 出片 检查 → 渲染 → 核对 → 上传版(长片另出 720p 小版)、封面、总览图、数字 film.py render
9 交付 发上传版和封面;说清没验证的;按实测写帖子草稿 references/delivery.md;film.py stats

每到一个节点记一笔:python3 tools/film.py log "分镜确认"。

四个文件各管一件事

文件 管什么
poem.json 字:标题、作者、诗句、印文、落款、多音字、音色
film/timing.js 什么时候:这首诗每个动作的时刻。朗诵、题签、末句、总时长由引擎按配音实际长度推出来
film/scenes.js 画什么:场景和红点的表演
film/score.py 响什么:每件乐器在哪个时刻响

engine/ 和 tools/ 是引擎,新片子一般不用改;现画的新东西如果别的诗也用得上,就写进工程的 engine/,出片后同步回这个 skill 的 template/engine/ 并在 examples/gallery 里加一格。舞台(engine/stage.js)已经包了:封面及其淡出、题签逐句写字、末句随朗诵逐字书写、红点变印章、落款、整片的宣纸质感。你交给舞台的只有四样:build(一次性画好的贴图)、scene(ctx, t)(每一帧)、cover(c, time)(封面那张画)、titleFrom()(红点从哪儿跳起来去写字)。

film/timing.js 里时间表的写法:

const T = { drop: 0.35, land1: 0.85, …, bloom: 3.0, launch: 8.6, hop: 17.15, write0: 17.9 };
PoemTiming.finish(T, {
  lines: [
    { at: 3.3 },                 // 第 1 句:这个时刻开始念
    { gapAt: T.launch },         // 第 2 句:把句中的停顿(千里江陵|一日还)对在 launch 这一刻
    { after: 0.3, min: 10.3 },   // 第 3 句:上一句念完 0.3 秒后,但不早于 10.3
  ],
});

句子少(绝句)就像上面这样,先定画面的时刻,再把每句朗诵摆上去。句子多(词、律诗)反过来:先定每句之间停多久(全用 after,长的停顿是留给画面的),朗诵的时刻由配音实际长度推出来,再把画面的动作挂在「第几句的哪个位置」上(T.v[i] 是第 i 句开口的时刻,T.vEnd[i] 是念完的时刻)。这样换音色、改语速,画面跟着走。写法见 examples/chibi/film/timing.js。

做的时候守住的几条(为什么)

  • 每一帧只由时间决定。不用时钟、不用 Math.random(用 QZ.hash / QZ.rng)。渲染是多个进程乱序抓帧的,画面依赖「上一帧」就会错位。
  • 全片只有红点是正红色。印章是它变的,所以也是红的;别的东西(太阳、旗子、花)用藤黄、胭脂、赭石。观众的眼睛靠这一点红跟住主角,检查脚本也靠它判断主角在不在画面里。
  • 先空跑再配音。voice --dry 估出来的时长足够排场景;真配音按次收费,分镜没定就配是白花钱。同一音色同一段文字有缓存,重跑不再花钱。
  • 动作卡在字上,写在时间表里。「千里江陵」和「一日还」之间朗诵会换气,小舟就在这口气里弹射出去;猿在念「猿声」的时候叫。这种对位是这类片子好看的原因,用 gapAt / at 把它写死,不要靠凑。
  • 多音字写进 poem.json 的 say(朝→昭、还→环、重→崇)。合成用同音字,读音必然对;画面上写的永远是原字。你听不到,这是唯一能保证不念错的办法。
  • 东西站在山上时,问贴图要高度(tile.hs),并且算对「画面 x → 贴图 x」的偏移。上次猿挂的树悬在半空,就是偏移加错了,看截图才发现。
  • 末句的每个字要对着频谱图标时间(voice --spec,写进 poem.json 的 onsets)。不标就是平均分,笔会和声音错开。
  • 水面上的东西,谁的水线低谁在前面。小舟的水线比礁石的水线高,却画在礁石后面,看上去就像船搁在石头上。先定每样东西的水线,再按从远到近的顺序画。
  • 会淡到大字后面的场景里,别留东西在落款和片尾小字底下。两条片子都在这里翻过车;最干净的办法是让那样东西在红点离开后自己淡出。
  • 声音没人听过。film.py score 会打印每段的频段占比和人声高出配乐多少分贝:60–150Hz 超过 45% 就是鼓太重,人声余量低于 7 分贝就是被盖住。交付时必须请用户听,并且如实说你没听过。

检查是流程的一部分

python3 tools/film.py qa

它做三件事:跑 HyperFrames 的工程检查;按时间表里的每个时刻抓一帧,自动查「几乎是白纸」「红点不在画面里」「红色太多」「画面没动」「这一帧报错了」;把这些帧拼成带时刻和事件名的总览图 build/qa/contact-N.jpg。总览图要自己打开看——脚本看不出构图空、字压住了主体、东西悬在半空。

一帧出错时,错误信息会用红底白字写在画面顶上(而不是静默卡住上一帧),所以看图就能发现。

film.py render 渲染后还会核对:帧数和时长对不对、响度是不是 −16 LUFS、每段朗诵在成片里的位置和计划差多少毫秒、中间有没有画面不动的片段、第一帧是不是白的。

参考文档

文件 什么时候读
references/poem-json.md 填 poem.json、写 film/timing.js 之前:全部字段、时间规则
references/scenes.md 画场景之前:舞台接口、画法库清单、镜头和视差、晕开/淡出/转场、怎么画新东西
references/acting.md 排红点的戏之前:压扁拉伸、蓄力、眼神、表情、和朗诵对位的几种办法
references/audio.md 朗诵和配乐:语音插件、多音字、读频谱图、乐器清单、自检数字怎么看
references/qa.md 检查和调试:每条警告的意思、翻过的车、浏览器预览
references/delivery.md 出片后:文件清单、交付时说什么、实测帖怎么写、数字从哪来
references/kit-1.jpg … kit-4.jpg 画法库里每样东西的样子。开工前看一眼,库里有的不要重画
examples/qingzhou/ 完整范例《早发白帝城》(绝句):连续镜头、视差、晕开、物体转场、远景、对位。短诗照着它改最快
examples/chibi/ 完整范例《念奴娇·赤壁怀古》(词,72 秒):按停顿排时间、逐句配音、一个世界换三个时辰、人物和火、画中画。长篇照着它改

用范例新建一个能直接跑的工程:

python3 $SK/scripts/new_project.py videos/demo --example qingzhou

升级已有工程的引擎(不动 poem.json、film/、assets/、audio/):

python3 $SK/scripts/new_project.py videos/<slug> --sync