体素动画 是做什么的?
制作看起来像积木游戏的场景,而不使用任何来自这类游戏的元素:每个纹理都在代码中运行时生成,角色是原创设计。输出是一个自包含的 HTML 页面(可交互、可分享),并且在需要时,可以从同一页面渲染出逐帧精确的视频。
本技能包含的文件
assets/voxel-kit.js:工具包(纹理、材质、应用循环、轨道摄像机、带昼夜循环的天空、岛屿地形、工作室平台、角色骨架、粒子、弹出、爆炸、火把、路径辅助、录制器、捕获模式)。在编写场景前请务必阅读它:下面的 API 参考是摘要,该文件是权威。assets/template.html:包含工具包标记、岛屿、角色和简短时间线的起始页面。examples/house-builder.html:一个建造者一块一块地搭建房屋,欢呼,爆炸,循环。examples/melting-crystal.html:物理示例(6×6×6 冰晶格的林德曼熔化,附带体素温度计、水坑和重新结冰)。科学解说类可复制其结构。scripts/inline_kit.py:将页面中的<!-- VOXEL_KIT -->替换为工具包,生成单个文件。scripts/render.py:逐帧渲染为 PNG 帧和.mov/.mp4/.webm。
如果只有此 SKILL.md(文件夹缺失),请先从 https://github.com/normix93/voxel-animations 获取完整的技能包再构建。
工作流程
- 用一句话确定创意:什么被建造、变换或解释,以及循环如何结束(重置、爆炸、冻结、摄像机飞出)。确定目标:交互页面、用于剪辑的视频(透明背景或不透明)、竖版短视频(1080×1920)。
- 将故事板组织成时间线:3-6 个节拍并附上时长,例如
0-2 秒 空地 → 2-40 秒 建造 → 40-44 秒 欢呼 → 44-49 秒 爆炸 → 循环。每个节拍是app.onUpdate状态机中的一个mode(参见房屋示例)。 - 从
assets/template.html开始,保留工具包标记,编写 SCENE 部分。重用工具包中的组件,不要重写。将所有内容保持在一个页面中。 - 构建页面:
python3 scripts/inline_kit.py scene.src.html scene.html。 - 检查它:打开页面(或使用
render.py渲染几帧到一个文件夹并查看)。检查:方块悬浮或重叠、角色穿墙、摄像机切入树木、前几秒空白、透明方块闪烁。 - 交付:当环境支持时,将页面作为工件发布,同时提供 .html 文件;如果需要,渲染视频(参见导出)。
风格规则
- 仅限原创。 绝不使用现有游戏中的纹理、音效、标志、生物、角色或名称。不要将输出称为“Minecraft”相关内容:使用体素、方块、方块世界。工具包中的程序化纹理和戴安全帽的建造者可以安全使用和修改。
- 像素清晰:使用
NearestFilter的 16×16 纹理(工具包已处理)。硬光、清晰阴影、每种材质颜色少、小噪点。 - 运动有重量:方块弹出时带有超调(
app.pop),碎片使用重力(≈20–22 方块/秒²)和阻尼弹跳,角色以约 5 方块/秒的速度行走并平滑转向。任何物理相关的动画都不要使用线性补间。 - 一目了然:一个主要主体在中央,摄像机缓慢环绕(
autoSpeed0.05–0.1),背景平静(天空或studio颜色)。 - 文字:页面可以有标题和状态行。对于视频,使用
?clean渲染(无 UI)。如果视频中需要标签,使用非常短的词语;解释应放在旁白或页面中,而不是帧上。
工具包 API(摘要)
const app = VK.createApp({
title, subtitle, hint, // UI 文字(使用 ?clean 或按下 H 键隐藏)
sky: 'day' | 'night' | 'studio', // studio = 纯色(studioColor),无雾
cycle: false, dayLength: 48, tod: .27, // 时间 0..1(.25 日出侧,.75 夜晚)
camera: { radius, phi, theta, target: [x,y,z], auto: true, autoSpeed: .07 },
speed: 1, seed: 7, fileName: 'my-scene', shadowSize: 28,
});
app.M.grass | dirt | stone | sand | planks | roof | log | leaves | glass | water | ice | snow | cobble | brick | lava
VK.solid(0xff8800, { glow, opacity, noise, border }) // 彩色像素块材质
VK.block(app, 'planks' | material, x, y, z, { pop: true }) // 单个网格(移动/弹入)
VK.instanced(app.scene, material, [[x,y,z], ...]) // 静态方块,一个绘制调用
const world = VK.island(app, { seed, radius: 18, flatRadius: 7.5, trees: 10, water: -1, snowLine })
// → world.groundTopAt(x, z), world.hAt(ix, iz)
VK.platform(app, { size: 16, top: .5, mat: 'stone', topMat: 'cobble' }) // 工作室地板
const hero = VK.character(app, { ground: world.groundTopAt, position: [x,0,z], yaw, colors: { shirt:[r,g,b], hat: 0xf5c518 | null, ... } })
hero.goTo([[x,z], ...]) · hero.arrived · hero.face(x, z, dt) · hero.jump(v)
hero.pose = 'idle' | 'reach' (set hero.reach 0..1) | 'cheer' | 'panic' | 'wave'
hero.lookTarget = {x,y,z} | null
VK.standOutside(x, z, { halfX, halfZ, cx, cz }) // 站在盒子旁的位置以操作其中一个方块
VK.routeAround(from, to, { halfX, halfZ, cx, cz }) // 绕过该盒子的路径点
VK.explode(app, meshes, { center, power, ground, onDone })
VK.torch(app, x, y, z) // 光照在夜晚变强
app.puff(position, material, n, power) · app.pop(mesh) · app.clearParticles()
app.onUpdate((dt, time) => { ... }) // dt 已按速度缩放;捕获模式下确定性
app.rand() // 种子随机数:使用它而不是 Math.random
app.status(html) · app.button({ label, key, toggle, on, onClick }) · app.standardButtons({ cycle })
app.start()
保持场景正确的规则:
- 使用
app.rand()(带种子),绝不使用Math.random(),以保证渲染可重复。 - 将所有运动放在
app.onUpdate中使用dt;绝不使用setTimeout/setInterval进行动画(否则捕获模式会失效)。 - 方块大小为 1×1×1,中心位于整数坐标;高度为
h的柱子顶部表面在h + 0.5处。角色站在groundTopAt(x, z)。 - 静态场景 →
VK.instanced;任何移动、弹入或爆炸的 →VK.block。 - 大量对象:移动网格保持在约 1500 以下;超过该数量,使用 InstancedMesh 进行动画。
配方
- 建造序列:制作有序计划
[{type,x,y,z}](逐层,沿周长行走;屋顶用蛇形行)。对于每个项目:寻路到standOutside,设置pose='reach',在到达约 45% 时生成下一个。参见examples/house-builder.html。 - 用方块解释物理:将数量映射为可见的东西。温度 → 抖动幅度 ∝ √T(均分定理);压力 → 方块碰撞;能级 → 塔的楼层;场 → 方块箭头;熵 → 有序墙壁重新洗牌;重力 → 失去支撑时倒塌的堆叠。使用体素仪表(如
melting-crystal.html中的温度计)显示控制变量,而不是屏幕上的数字。 - 变换:更改
mesh.material(水 ↔ 冰、石头 → 熔岩),使用scale.y压平,在两个位置之间使用缓动 lerp 滑动。 - 原子/分子:使用
VK.solid(colour)立方体作为粒子,glow: true用于电子或光子,小立方体(mesh.scale.setScalar(.3))用于轨迹。 - 可循环的结局:爆炸(
VK.explode)→ 重置,或冻结 + 慢速摄像机,或夜幕降临(app.sky.cycle = true)并点燃火把。
导出
URL 参数(适用于任何使用工具包构建的页面):
?clean 隐藏 UI · ?alpha 透明背景(无天空、无雾)· ?seed=3 ·
?speed=2 · ?night · ?capture&fps=30 渲染器使用的逐帧精确模式。
快捷键:H 隐藏 UI · Space 暂停 · V 快速实时 WebM 录制(当页面从本地文件打开时有效;托管预览可能阻止下载)。
逐帧视频(推荐用于剪辑):
pip install playwright && python3 -m playwright install chromium # 一次性安装,同时需要 ffmpeg
python3 scripts/render.py scene.html --seconds 12 --fps 30 --out scene.mov # ProRes 422 HQ
python3 scripts/render.py scene.html --seconds 12 --alpha --out overlay.mov # ProRes 4444 with alpha
python3 scripts/render.py scene.html --size 1080x1920 --warmup 3 --out short.mp4 # 竖版 H.264
python3 scripts/render.py scene.html --seconds 5 --out frames/ # 仅 PNG 帧
--warmup 跳过录制前的场景秒数;--params "seed=3&night" 添加 URL 参数;--headed 使用可见浏览器(真实 GPU,在大多数笔记本电脑上更快)。使用 --alpha 时摄像机仍会环绕;设置 camera: { auto: false } 以获得静态叠加层。
交付前检查
- 页面打开时无控制台错误;第一帧已显示主体。
- 循环至少正常工作两次(重置清除网格、灯光和粒子)。
- 角色从不穿墙;除非有意,否则无悬浮。
- 对于视频:使用
?clean渲染,正确的尺寸/帧率,如果要求了 Alpha 则检查是否包含。 - 代码、标题、文件名中均无第三方游戏名称、纹理或角色。