mc-texture · git:20261103.0de65ea · 2026-11-03 · sha256 8494e2658cb1b9ee
mc-texture git:20261103.0de65eaA
Immutable. This exact content never changes and is served at /api/v1/blob/8494e2658cb1b9ee.
---
name: mc-texture
description: 像素纹理、纹理动画与特效。与 pixel-art-upstream(junyudev/pixel-art,MIT,已收录)配套使用——32px 以上或画人物/立绘时同时读它。当用户要画、改或修方块纹理、物品图标、GUI 贴图、粒子贴图、烟花/爆炸/烟雾特效,或要做 .png.mcmeta 纹理动画时激活:先问清用户的风格要求,用户没说再按 MC 惯例(16×16、可平铺、材质符合游戏内逻辑,如铜的四级锈蚀);照「平涂剪影→打光→内部细节→描边→五官」分层画,渲染后按 1× 原尺寸核对并至少迭代 3 轮,动画用参数和公式生成每帧。模型 JSON 走 mc-model,目录与 pack.mcmeta 走 mc-resourcepack。不适用于游戏内渲染效果调试与 GeckoLib 几何动画。
---
# mc-texture — 像素纹理与纹理动画
## 铁律
1. **1× 认得出**是唯一硬判据。放大图好看不算数,缩小到真实尺寸认不出就是重画。
2. **用户要求优先于 MC 惯例**。用户说了风格/色数/尺寸就照办;用户没说,才按本文的 MC 默认。
3. **质量来自迭代次数**,不是第一版的想象。至少 3 轮,每轮写清改了什么、为什么。
4. **只输出文字也要闭环**:写文本网格 → 渲染 PNG → 看图 → 改数字 → 再渲染。
## 配套技能:什么时候去读 pixel-art-upstream
本技能与 `pixel-art-upstream`([junyudev/pixel-art](https://github.com/junyudev/pixel-art),MIT,已原样收录)**是一套,不是两套**。默认两个一起用:
- 本文 = **MC 特有规则**(尺寸、可平铺、材质逻辑、mcmeta 动画、接线)
- 上游 = **长篇方法论**(32px 以上的构造法、脸和手的画法、风格卡、概念图转像素)
**先读上游再动手的场合**(用 `read_knowledge_resource uri=mcskill://skill/pixel-art-upstream`,或直接读 `common_skill/pixel-art-upstream/`):
| 场合 | 读哪篇 |
|---|---|
| 画布 > 32px(实体贴图、立绘、大 GUI) | `common_skill/pixel-art-upstream/reference/large.md` — 构造法全流程,23KB |
| 要把参考图/AI 出的"像素风"图转成干净像素画 | `common_skill/pixel-art-upstream/reference/imagegen.md` |
| 需要按某款游戏的风格对齐(尺寸、共享调色板、脸型语法) | `common_skill/pixel-art-upstream/reference/small.md`;风格卡目录未随仓库分发,见本节末 |
| 想知道「1× 判据」「半尺寸放大失败」这些结论的出处 | `common_skill/pixel-art-upstream/reference/large.md` 的 Lessons 一节 |
**用它的渲染器做交叉验证**:两个渲染器各有一套校验,能互相抓错。
```bash
# 上游(需要 python + pillow + numpy),会报包围盒、贴边、孤立像素
python -I common_skill/pixel-art-upstream/tools/grid.py render sprite.txt out/ --size 16x16
# 本仓(零依赖),会报孤立像素、半透明、尺寸不符
node common_skill/mc-texture/tools/render-pixels.mjs sprite.txt --out out/sprite.png --size 16x16
```
⚠️ 上游 `grid.py` 用 `Path.read_text()` 读输入,**默认编码不是 UTF-8 时会报 UnicodeDecodeError** ⇒ 给它的 `.txt` 里不要写中文注释,注释单独放或改英文。
⚠️ 上游的 `styles/eastward*` 是 Eastward(Pixpil)风格卡,上游 README 自述「授权使用」但**未给 CC 许可** ⇒ **不要搬进工程、不要当素材分发**,只在本机当参考。
**冲突时以本文为准**:上游面向通用像素画(RPG 立绘、角色 48×64),本文面向 MC(16×16 方块必须可平铺、材质要符合游戏内逻辑)。上游讲「人物身高 52px」「头身比」的那些数值,与 MC 方块贴图无关,别硬套。
## 第 0 步 · 先问,再画
动手前必须确认(用户已明确的跳过,**不要重复问**):
| 问什么 | 用户没说时的 MC 默认 |
|---|---|
| 尺寸 | 方块/物品 16×16;实体 64×64 起 |
| 风格 | 原版像素质感,硬边、无渐变 |
| 色数 | 方块物品 4–8 色;实体 ≤16 色 |
| 描边 | 深色同系描边(不用纯黑) |
| 光源 | 左上 |
| 材质的游戏内状态 | 按该材质的 MC 逻辑,见第 6 步 |
**用户的要求和 MC 惯例冲突时,永远听用户的**,并在交付时一句话说明哪里偏离了原版(用户可能是故意的)。
## 第 1 步 · 定尺寸与落点
| 用途 | 尺寸 | 落点 |
|---|---|---|
| 方块纹理 | 16×16,**必须可平铺** | 资源包 textures/block/ 下的 `<name>.png` |
| 物品图标 | 16×16 | 资源包 textures/item/ 下的 `<name>.png` |
| 粒子 | 8×8 / 16×16 | 图集行优先 |
| GUI | 256×256 图集内切 | 按控件格子对齐 |
| 实体 | 64×64 起 | 按 UV 展开 |
| 纹理动画 | 帧宽 × (帧数 × 帧高) | 竖排图集 + `.png.mcmeta` |
**可平铺**是方块纹理的隐藏要求:把图往右、往下各复制一份拼起来,接缝不能看得出。做法是让纹理元素跨过边缘时从对侧接回来。
## 第 2 步 · 规划(先写版式再画像素)
按**单位格**想,不要直接想像素坐标。16×16 的方块可以当 8×8 个 2px 格来布局。
写下来再动手:
```
画布: 16×16
主体占位: 主体 x2-13, y2-13
对称轴: x=7 (或「不对称」)
大形体坐标: ...
```
**对称的东西只画一半**,另一半用镜像生成:像素 x 的镜像是 `W-1-x`,宽 `w` 位于 `x` 的部件镜像到 `W-x-w`。奇数宽最容易错一格。
## 第 3 步 · 调色板
**色数就是质量上限。** 每种材质只要 2 档(主体 + 暗部),最多给 1–3 种材质配亮部。所有材质都配三档 = 花。
**色阶生成法**(比单纯调明暗有质感):暗部往**冷**偏(加蓝紫),亮部往**暖**偏(加黄)。手动做两三个纯色即可,逐像素渐变交给抖动或换色阶。
**明度要分散**:别让所有暗色落在同一行,否则远看是一条糊带。
## 第 4 步 · 分层画(顺序不能乱)
这是保证「不是一堆气球拼起来」的关键。**严格按这个顺序**:
```
1. 平涂剪影 只用底色,确认形状对不对
2. 打光 上部/左侧提亮,下部/右侧压暗(一个光源,全局统一)
3. 内部细节 花纹、铆钉、锈斑 —— 必须在打光之后,否则会被当成洞而围一圈阴影
4. 描边 1px 深色同系描边
5. 特殊标记 眼睛、高光点、文字等,放最后保持干净
```
**线只画在形体交叠处**(甲片压住底板、帽檐压住头)。同一块材质内部不描线,否则就是「每块零件各自描边」的气球拼装。
**小图形用字符画**:4–6px 的圆用字符画比几何原语好看(原语在这个尺寸会画成方块)。
## 第 5 步 · 画法守则
- **剪影不对称**:1× 下涂黑成一块还能看出是什么。左右完全对称 = 认不出
- **特征比你以为的更大更黑**:16×16 里眼睛/铆钉至少 1–2px;单个 1px 点 = 噪点
- **一个光源**:暗部只在背光一侧,全图统一打光
- **禁渐变、禁抗锯齿**:透明度只 0 或 255。要有明暗就用纯色块分区
- **别做「半尺寸再放大」**:缩放器会把边缘磨圆,细节永远回不来
## 第 6 步 · 符合 MC 逻辑(用户没指定时)
纹理要让人一眼认出「这是 MC 里的这东西」。**先看该材质在游戏里的实际状态**,再决定画成什么样。
### 6.1 材质逻辑表
| 材质 | 画法要点 |
|---|---|
| 石头 / 矿石 | 以噪点为主,无方向性;矿石 = 石底 + 矿斑,矿斑要亮于石底 |
| 木头 | **有纹理方向**,年轮/木纹沿一个轴;六个面各不相同(顶底是年轮) |
| 金属(铁/金) | 平整,靠**大面积高光块**表现,不是噪点 |
| 铜 | **有四级氧化过程**,见下 |
| 玻璃 | 几乎全透明,只有边框和几条高光 |
| 植物 | 明度变化大,边缘参差,不用描边 |
| 羊毛 | 细密均匀的噪点,无高光 |
### 6.2 铜的锈蚀是**过程**,不是「铜块加一块绿」
这是最容易画错的一条。MC 的铜有**四个阶段**,是绿色逐渐覆盖铜面的过程:
| 阶段 | 外观 | 英文 |
|---|---|---|
| 0 | 纯铜色,无绿 | Unoxidized |
| 1 | 铜色为主,**绿色斑点开始出现** | Exposed |
| 2 | **绿色为主,棕色斑点** | Weathered |
| 3 | **蓝绿色为主,还有几处绿斑** | Oxidized |
要点:
- **绿色从边角开始**向外扩散(游戏里氧化从棱角开始蔓延)
- 阶段 1 的绿是**零星几点**,不是一大块
- 阶段 2 已经**反过来**了:底色变绿,剩铜色斑点
- 阶段 3 是**青绿(teal)**,不是草绿
- 涂蜡(waxed)不氧化;斧头可以刮回上一个阶段
画铜之前先问用户要**哪一阶段**。画「半铜半绿、绿占三分之一」这种中间态,游戏里没有对应物。
**成套交付**:如果这个铜件会跟着方块一起氧化,就该把四张都做出来(原版铜块/楼梯/门都是四套贴图)。只画一张然后说「这是铜」,等于没做。
其它材质同理,先问「它在游戏里是什么状态」,再决定画哪一档:
| 材质 | 游戏内状态 | 画法差别 |
|---|---|---|
| 铜 | 四级锈蚀 | 四套全做,绿色占比逐级上升 |
| 木头 | 原木 / 去皮 / 木板 | 原木带树皮纹、去皮是裸木、木板是平行板缝 |
| 石头 | 石头 / 圆石 / 石砖 | 圆石是不规则块面,石砖是规整分隔 |
| 泥土 | 普通 / 耕地 / 湿润 | 湿润明显更深 |
| 作物 | 生长阶段 | 每阶段高度和颜色不同 |
### 6.3 其它要遵守的 MC 惯例
- 方块纹理 16×16 且可平铺
- 同一套资源内不要混用 16 和 32 分辨率
- 发光类(荧石、海晶灯)用亮块 + 少量暗点,不要画真实光照
- 物品图标留 1px 透明边距,别贴满整格
## 第 7 步 · 渲染与核对(每轮必做)
```bash
node tools/render-pixels.mjs NAME.txt --out out/NAME.png --size 16x16 --scale 16
```
脚本会拦:行宽不一致、未定义色号、尺寸不符;并警告孤立像素与非 0/255 半透明。
**渲染后四问**,任何一问不过就回第 4 步:
1. **1× 认得出吗?** 把 1× 当真图标看,能不能立刻说出这是什么
2. **剪影清楚吗?** 全涂黑还有形状吗
3. **色数对吗?** 超预算就合并相邻档
4. **光照一致吗?** 暗部是不是都在同一侧
### 反向指认(最有效的一招)
遮住标题,问自己——或**让另一个 agent 只看图、不给任何描述**,问「这是什么?每个部件是什么?」。它可能读成完全别的东西。规则检查不出来的误读,只有这一招能抓。
### 读不到图时
把图打印成字符回读,一样能看出轮廓、明暗分区和五官位置。
## 第 8 步 · 纹理动画
MC 的纹理动画 = **竖排图集 + 同名 `.png.mcmeta`**,不是 GIF。
### 8.1 用参数生成每帧,不要手画
身体/主体只画一次,变化量做成参数:
```
frame(n) = base + 参数(n) // 参数可以是呼吸幅度、帧序、缓动值
```
这样所有帧天然同尺寸、同锚点、风格一致。**手画每一帧一定会跳。**
### 8.2 排图集
每帧尺寸完全相同,总高必须能被帧高整除,否则游戏显示紫黑缺失纹理。竖排最不容易错:`16×48` = 3 帧 16×16。
### 8.3 写 mcmeta
`assets/<modid>/textures/block/<name>.png.mcmeta`(文件名**带 `.png`**):
```json
{
"animation": {
"frametime": 2,
"interpolate": false,
"frames": [0, 1, 2, 1]
}
}
```
| 字段 | 含义 | 默认 |
|---|---|---|
| `frametime` | 每帧几 game tick | 1 |
| `interpolate` | 自动补中间帧 | false |
| `frames` | 帧序;可只列部分帧,或用 `{"index":N,"time":T}` 定时 | 全部按序 |
| `width` / `height` | 单帧宽高;不写按最短边推断 | 自动 |
**`frames: [0,1,2,1]` 做来回循环**,比画 4 帧省事——最常用的省帧技巧。
### 8.4 动画守则
- 帧间**只改该动的部分**,别整张重画(会闪)
- 相邻帧位移 ≤ 1–2px,否则跳
- 首尾要接得上:末帧的下一帧就是首帧
- 做呼吸/起伏时,附属部件(天线、挂饰)**滞后一拍**
- 帧数够再用 `interpolate`——2 帧补间只会变糊
### 8.5 特效类动画:用规则算,不要手画
爆炸、火焰、烟花、烟雾、魔法这种**粒子特效**,每一帧都是「同一套规则在不同时刻的结果」。手画每一帧必然跳,正确做法是写规则:
```
1. 拆元素 每个元素记 t0(出生时间)、life(寿命)、初速度、方向
2. 生成 随机必须带种子(mulberry32),否则每次跑都不一样、没法逐版对比
3. 积分 固定步长推进:速度受阻力衰减,烟受浮力上升,火花受重力下坠
4. 着色 按「已飞出的距离」或「剩余寿命」取调色板一档,不是按帧号
5. 清理 只对烟团层做开运算/去碎块;1px 的火花和余烬不能一起清理
```
**四个必踩的坑**(都是上游实测出来的):
| 症状 | 病因 | 修法 |
|---|---|---|
| 火球歪成一条斜线 / 粒子叠成一个点 | 方向完全随机会扎堆;同方向粒子速度相同会重叠 | 方向用分层抽样(黄金角或等分),同方向必须**分壳层给不同距离** |
| 中心是空的,像个环 | 按年龄着色,但粒子都从外壳出发 | 改按**已飞出距离**着色,并给球心一点持续辉光 |
| 冷却时中间一整块红盘 | 用了整体温度场,所有粒子一起变色 | 每个粒子**单独**按自己的热度冷却,时间常数 tau 各不相同 |
| 火球表面有白划痕 | 火花从中心出发却画在火球上面 | 火花画在烟团**后面**(分层绘制) |
**浅色特效必须检查亮背景**:白色、浅黄、青色在浅色背景上会消失。解法是加 1px 深色描边,或让最暗一档足够暗。
**判定要三张图一起看**:
1. 联系表(逐帧并排)——看形状和节奏
2. 三种背景(暗/中/亮)——看可读性
3. 1× 原尺寸——看游戏里的真实大小
### 8.6 MC 烟花:五种形状是确定的
做烟花前先查 MC 的机制,别自己发明。官方语料(`resources_server_loottables_lootfunctions.md`)写明:
```
shape 合法值 = "small_ball" | "large_ball" | "star" | "creeper" | "burst"
缺省 small_ball
另有 trail(拖尾)与 flicker(闪烁)两个附加效果
```
| 形状 | 外观 | 画法 |
|---|---|---|
| `small_ball` | 小球,半径小 | 均匀球面 + 少量粒子 |
| `large_ball` | 大球,半径大 | 同球面,粒子更多、速度更大 |
| `star` | 星芒状,有放射光条 | 角度连续铺满 + 光芒方向拉长(关键:光条是**速度差**做出来的,不是画出来的) |
| `creeper` | 苦力怕脸 | 平面网格分布,正面朝观察者 |
| `burst` | 球状但密度不均 | 球面 + 随机偏置 |
**颜色是染料色**,主色 + 渐隐色(fade)。同一颗炸开的星里颜色要同步渐变,不能各变各的。
本仓库自带生成器:`common_skill/mc-texture/tools/firework.mjs`
```bash
node tools/firework.mjs out/ --shape star --fps 12 --size 32 --trail --flicker
# → firework.png(竖排图集)+ firework.png.mcmeta
```
### 8.7 预览交付:GIF 与逐帧图
图集(`.png`)在编辑器里不会动,验收时必须能看到动画。两种交付物:
```bash
# 1) GIF:给用户直接播放(零依赖,自己编码 GIF89a)
node tools/gif.mjs out/firework.png out/firework.gif --delayCs 8 --scale 6
# 2) 逐帧分解图:所有帧并排,用于核对节奏和跳帧
# (联系表比 GIF 更能看出"哪一帧不对")
```
**GIF 延迟换算**:`delayCs` 单位是 1/100 秒。12 fps ⇒ 每帧 8.33cs,取 8;6 fps ⇒ 16cs。
**预览页要自包含**:交付给用户的 HTML 不要把 GIF 写成相对路径 `<img src="star/firework.gif">`。
本地 `file://` 打开时,浏览器常因安全策略拒绝加载同目录资源,用户看到的是空白框 —— **而且页面不会报错,只是静默空白**。
正确做法是把 GIF 直接 base64 内嵌进 HTML:
```js
const b64 = fs.readFileSync(gifPath).toString("base64");
const img = `<img src="data:image/gif;base64,${b64}">`;
```
代价是文件变大(五个 GIF 约 108 KB),换来「双击就能看、换机器也不坏」。
**注意**:GIF 在部分聊天/文档宿主里只会显示首帧(被转成静态图)。要让人真看到动画,
**同时交付 GIF + 逐帧分解图**,或做一个 HTML 播放器页。
### 8.8 拼图集
```bash
node tools/render-pixels.mjs f0.txt f1.txt f2.txt --atlas out/x.png --frametime 3
```
自动校验每帧尺寸、竖排拼接、写出 `.png.mcmeta`。
## 第 9 步 · 接线
文件放 `assets/<modid>/textures/block/` 或 `assets/<modid>/textures/item/`,命名与模型里 `textures` 引用**逐字对齐**(大小写敏感,不要空格)。
- 引用完整性 → `audit_resources`
- 模型 JSON / blockstate → `mc-model`
- 目录与 `pack.mcmeta` → `mc-resourcepack`
## 常见翻车
| 症状 | 病因 | 修法 |
|---|---|---|
| 糊 | 生成图缩小的,不是真像素画 | 回第 4 步网格法重画 |
| 透明处有黑边 | 混了半透明像素 | 全透明或全不透明 |
| 游戏里偏色 | 非 sRGB / 带色彩配置 | 存标准 8 位 sRGB PNG |
| 紫黑格 | 图集总高不能被帧高整除 | 核 `.mcmeta` 与图集尺寸 |
| 动画不播 | mcmeta 文件名少了 `.png` | 应为 `<name>.png.mcmeta` |
| 像别的游戏 | 色数过多、加了渐变 | 压到 8 色、去渐变 |
| 认不出 | 剪影对称、特征太小 | 改剪影、放大特征 1–2px |
| 方块边缘有缝 | 纹理未做平铺 | 让元素跨边接回对侧 |
| 铜不像铜 | 画成「铜块 + 一块绿」 | 按四级锈蚀选一阶段重画 |
## 边界
- 只管**贴图**。模型形状、UV、几何动画走 `mc-model`。
- 生成式出图只当草稿,商用许可去站点条款确认,确认不了就别用。
- **风格归属由用户拍板**,本文的 MC 惯例只是用户没说时的默认值。