Axıs
技能目录

06 · 美术工具与流程开发

06 · 美术工具与流程开发

这是独立游戏 TA 回报率最高的领域,也是最容易自我陶醉、过度投入的领域。 一句话:用 ROI 算,不要用爽感算。

1. ROI 公式(写在你的显示器上)

ROI = (单次节省时间 × 预估使用次数) / (开发人天 × 8 小时)
ROI 判决
≥ 10 立刻做
3–10 做,但先做最小可用版本
1–3 排到后面;工期紧就砍
< 1 绝对不做

示例

  • 导入预设:单次省 2 分钟 × 500 次 = 16.6 小时;开发 1 天(8h)→ ROI ≈ 2.1 … 看起来一般,但它同时消除了 500 次人为错误,实际价值远高于此。
  • 批量 LOD 生成:单次省 15 分钟 × 200 次 = 50 小时;开发 1 天 → ROI ≈ 6.3 ✅
  • "通用场景编辑器":开发 20 天 = 160h;使用 5 次 × 省 2 小时 = 10h → ROI ≈ 0.06 ❌

修正项:除了时间节省,还要计入

  • 错误消除价值(校验器价值被严重低估)
  • 一致性价值(预设保证风格统一)
  • 可教授价值(新人/外包用模板不会跑偏)

2. 独立游戏该建的 12 个工具(按 ROI 排序)

# 工具 人天 ROI 说明
1 导入预设脚本 0.5–1 高 纹理/模型导入即正确
2 资产校验器 1–2 高 防脏数据(错误消除价值极高)
3 批量 LOD 生成 0.5–1 高
4 批量导出脚本(DCC→引擎) 0.5 高 美术不用学引擎
5 材质实例批量生成 0.5 高 按贴图自动建实例
6 场景统计报告 0.5 中高 每周审计(面数/材质/内存)
7 散布/刷取工具 1–3 中高 植被碎石
8 未使用资产扫描 0.5 中 减小包体
9 色板吸管检查器 0.5 中 防风格漂移
10 性能快照工具 0.5–1 中高 一键输出关键指标
11 动画事件/伤害窗口编辑器 1–2 中 打击感调参
12 截图/宣发素材录制工具 0.5–1 中 固定机位、隐藏 UI、高分辨率

前 6 个是必做的(合计约 4–6 人天),能省下数百小时。

3. 不该做的工具

想法 为什么不做
自研关卡编辑器 引擎已有;维护成本无限
自研资产管理数据库 用文件结构 + 命名规范 + Excel 就够
通用"美术工具平台" 你会花 3 周做框架,然后没时间做功能
自研版本控制 用 Git/Perforce
复杂的 GUI 工具 命令行 + 一个按钮 就够
跨引擎的通用工具 你只有一个引擎
为 1 个资产做的专用工具 手工做更快

4. 工具的形式选择

形式 适用 成本
编辑器菜单项(一键执行) 批处理、检查 最低 ✅ 首选
编辑器窗口(Editor Window) 需要交互/参数 低
Inspector 扩展 资产属性增强 低
导入后处理器 自动化设置 最低 ✅ 首选
运行时调试 Overlay 性能/调试显示 低
外部脚本(Python/C# CLI) DCC 侧批处理、CI 中
DCC 插件(Blender/Maya) 导出、检查 中
独立应用 几乎不需要 高 ❌

原则:能做成一个按钮的,不要做成窗口;能做成窗口的,不要做成应用。

5. 美术友好的工具设计原则

美术 ≠ 程序员。你的工具是给美术用的,不是给你的同行看的。

原则 说明
一个按钮解决 不要要求美术填 8 个参数
默认值 90% 正确 美术不需要理解参数含义
命名用人话 "检查全部资产" 而不是 "RunAssetValidationPipeline"
即时反馈 报错要说清"哪个文件、什么问题、怎么修"
可撤销 批处理必须能撤销(或先备份)
不要打断工作流 校验在后台/保存时跑,不要弹窗挡住
失败不要吓人 警告用黄色,错误用红色但给出修复按钮
不要要求美术写代码/改 JSON
放在他正在用的地方 美术在 Blender 里,工具就在 Blender 里

报错信息模板

❌ T_Env_Rock_01_BC.png
   问题:压缩格式错误(当前:RGBA32,应为:BC7)
   原因:导入预设未生效(可能是手动改过)
   修复:[一键修复] [跳过] [查看详情]

而不是:

Validation failed: asset 0x3F2A compression mismatch (err 47)

6. 工具的分类与生命周期

一次性脚本 vs 长期工具

一次性 长期
特征 只跑一次(如历史数据清洗) 每天/每周用
质量要求 能跑通就行 要健壮、有文档、有错误处理
存放 /Tools/_oneoff/ /Tools/
是否删除 跑完即删(避免误解为在用) 保留 + 维护

警告:一次性脚本跑完一定要删或标记,否则半年后有人以为它是正式工具并依赖它。

工具的维护债

每个工具都有维护成本(引擎升级、需求变化)。 建议:每 3 个月审计一次工具列表,删掉 3 个月没用过的。

7. 开发与交付流程

① 收到需求(美术抱怨某件事重复/容易错)
      ↓
② 先手动做一次,记录:步骤数、耗时、出错点
      ↓
③ 算 ROI,ROI < 3 就手工做或换个更简单的方案
      ↓
④ 写最小可用版本(能跑通主流程即可,不做 GUI 美化)
      ↓
⑤ 找一个真实美术试用,观察他卡在哪(不要解释,看他操作)
      ↓
⑥ 迭代 1–2 轮
      ↓
⑦ 写 5 行说明(做什么、在哪、怎么用、注意什么)
      ↓
⑧ 登记进工具清单(名字/位置/负责人/ROI)

第 ⑤ 步最容易被跳过,也最重要。 你看美术用你的工具,会发现你想象不到的问题。

8. 代码示例:最小可用的资产校验器(Unity)

#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
using System.Text;

public static class ArtValidator
{
    const int MAX_TEX = 1024, MAX_TRIS = 8000, MAX_MAT_SLOTS = 2;

    [MenuItem("TA/Check/Validate All Assets", false, 1)]
    public static void ValidateAll()
    {
        var sb = new StringBuilder();
        int errors = 0, warns = 0;

        foreach (var guid in AssetDatabase.FindAssets("t:Texture", new[]{"Assets/Art"}))
        {
            var path = AssetDatabase.GUIDToAssetPath(guid);
            var tex  = AssetDatabase.LoadAssetAtPath<Texture2D>(path);
            var imp  = AssetDatabase.GetImporterAtPath(path) as TextureImporter;
            if (tex == null || imp == null) continue;

            // 尺寸
            if (tex.width > MAX_TEX || tex.height > MAX_TEX) { Log(sb,path,$"纹理尺寸 {tex.width}x{tex.height} 超过 {MAX_TEX}"); errors++; }
            // 2 的幂
            if (!IsPOT(tex.width) || !IsPOT(tex.height)) { Log(sb, path, "非 2 的幂"); errors++; }
            // 色彩空间
            bool shouldSRGB = path.EndsWith("_BC") || path.Contains("UI");
            if (imp.sRGBTexture != shouldSRGB) { Log(sb, path, $"sRGB 应为 {shouldSRGB}"); errors++; }
            // 压缩
            var pc = imp.GetPlatformTextureSettings("Standalone");
            if (!pc.overridden || pc.format != TextureImporterFormat.BC7) { Log(sb, path, "PC 压缩格式非 BC7"); warns++; }
            // 命名
            if (!System.Text.RegularExpressions.Regex.IsMatch(
                    System.IO.Path.GetFileNameWithoutExtension(path), @"^T_[A-Za-z]+_[A-Za-z0-9]+_\d{2}(_[A-Z]+)?$"))
                { Log(sb, path, "命名不规范"); warns++; }
        }

        foreach (var guid in AssetDatabase.FindAssets("t:Mesh", new[]{"Assets/Art"}))
        {
            var path = AssetDatabase.GUIDToAssetPath(guid);
            var mesh = AssetDatabase.LoadAssetAtPath<Mesh>(path);
            if (mesh == null) continue;
            if (mesh.triangles.Length / 3 > MAX_TRIS) { Log(sb, path, $"面数 {mesh.triangles.Length/3} 超 {MAX_TRIS}"); warns++; }
        }

        sb.Insert(0, $"校验完成 — 错误 {errors} / 警告 {warns}\n\n");
        Debug.Log(errors > 0 ? sb.ToString() : sb.ToString());
        if (errors > 0) Debug.LogError($"存在 {errors} 个 P0 问题,禁止提交。");
    }

    static bool IsPOT(int n) => (n & (n - 1)) == 0;
    static void Log(StringBuilder sb, string path, string msg) => sb.AppendLine($"{msg}\n  → {path}\n");
}
#endif

要点:

  • 一键执行(菜单项)
  • 输出可点击定位
  • P0 错误明确
  • 留好"一键修复"的扩展位

9. 工具清单台账(建议维护)

工具名 位置 做什么 负责人 人天 预估 ROI 近 3 月使用 状态
导入预设 Assets/Editor/ 自动设置纹理/模型 TA 1 高 每天 在用
资产校验器 TA/Check 命名/尺寸/格式 TA 2 高 每天 在用
批量 LOD TA/Gen 生成 LOD1/2 TA 1 高 每周 在用
...

每季度清理一次:3 个月未使用 → 删除或归档。

10. 自检清单

  1. 这个工具的 ROI ≥ 3 吗?
  2. 它消除了什么重复劳动或什么错误?说不清 → 不做。
  3. 最小可用版本能多快做出来?超过 3 天 → 拆分。
  4. 美术试用过吗?你观察过他使用吗?
  5. 报错信息是人话吗?有修复按钮吗?
  6. 有一个按钮就能跑吗?
  7. 能撤销吗?
  8. 有 5 行说明吗?
  9. 登记进工具清单了吗?
  10. 3 个月没用过的工具删了吗?
  11. 有没有工具依赖某个人的口头知识?(有 → 知识风险)
  12. 如果 TA 离职,这些工具还有人会用吗?

11. 一句话总结

好工具的标准不是"功能强大",是**"美术不用问你就会用,用了就不会出错"**。 一个只跑一次的一次性脚本,好过一个你维护三年的平台。 你的目标是让美术忘记你在背后做了什么。