15.2 设计案装配

把前十四章的产出订成一本不是设计案,装配的关键在「产出物到去向」的映射。本节用一份装配清单逐章登记每章的产出物与归属位置,并给出六个部分的骨架,再用一段 S 层脚本断言十五章各三节、四十五个节 slug 的命名与章号一致,让设计案的结构可被机器核对。

本节目标:把前十四章的产出装配成一份有骨架、可导航、可被脚本核对结构的设计案,而不是把十四份文档订在一起。

上一节说到站内那批 PRD 原型文档 是单品的可交互原型口径;本节继续把卷一前十四章的产出装配成一份可评审、可验收、可交付的设计案。区别在于:原型文档关心「单个产品长什么样」,设计案关心「这十四份结论怎么组织成一本能被开发、评审、老板各自读得进去的东西」。

材料清单解决的是「评审要什么」,设计案解决的是「这些东西怎么摆」。这两件事经常被混为一谈,结果就是评审材料很全、设计案却是一盘散沙。

15.2 设计案装配

15.2.1 设计案不是文档合集,是装配关系

一个常见的错误做法是:把前十四章各自的文档按章号订在一起,加一个封面,叫它「设计案」。这样的东西有三个问题——没有读者的入口(老板只想看一页纸,却要翻到第十一章)、没有去重(收益假设在第十、十一、十三章各写了一遍,口径还可能不一致)、没有去向(关卡校准参数到底放进正文还是附录,没人规定)。

装配要解决的正是这三件事,它的核心是一张**「产出物 → 去向」的映射表**:

  • 每章的产出物有哪些(一件还是三件);
  • 每件产出物在整本设计案里归到哪一部分、哪一页;
  • 哪些进正文、哪些进附录。

把这张表显式写出来,设计案就从「文档的堆叠」变成了「有结构的装配」。结构一旦确定,装配就是机械的:按去向把产出物放进对应位置即可。

15.2.2 装配清单:逐章登记产出物与去向

下面这份是 D 层配置:它把设计案分成六个部分,逐章登记每章的三个节 slug 与产出物去向。parts 定义骨架,chapters 定义填充物,outputs 里的 item 是产出物、dest 是它的去向。

{
  "version": 1,
  "game": "merge-island",
  "note": "卷一设计案装配清单(D 层)。逐章登记产出物与去向,并把 45 个节 slug 写成可断言的命名规则基线。",
  "docTitle": "《合成小岛》产品设计案(卷一装配)",
  "parts": [
    {"id": "P1", "name": "第一部分 机会与品类", "chapters": [1, 2, 3]},
    {"id": "P2", "name": "第二部分 约束与用户", "chapters": [4, 5]},
    {"id": "P3", "name": "第三部分 玩法与成长", "chapters": [6, 7, 8, 9]},
    {"id": "P4", "name": "第四部分 变现与商业", "chapters": [10, 11, 13]},
    {"id": "P5", "name": "第五部分 原型与资源", "chapters": [12, 14]},
    {"id": "P6", "name": "第六部分 评审与交付", "chapters": [15]}
  ],
  "chapters": [
    {"ch": 1, "title": "小游戏市场全景", "sections": ["mg-design-01-1-platform-landscape", "mg-design-01-2-distribution-mechanism", "mg-design-01-3-market-size-growth"], "outputs": [{"item": "平台分发机制对照表", "dest": "设计案 §1 机会与品类 · 背景页"}, {"item": "市场规模与增速情景", "dest": "设计案附录 · 数据表 A"}]},
    {"ch": 2, "title": "榜单与竞品拆解", "sections": ["mg-design-02-1-rank-structure-genre-share", "mg-design-02-2-competitor-teardown-method", "mg-design-02-3-differentiation-opportunity"], "outputs": [{"item": "榜单结构快照与品类占比", "dest": "设计案 §1 · 竞品页"}, {"item": "差异化机会清单", "dest": "设计案 §1 · 结论页"}]},
    {"ch": 3, "title": "品类图谱与选择模型", "sections": ["mg-design-03-1-genre-taxonomy", "mg-design-03-2-genre-business-matrix", "mg-design-03-3-genre-scoring-sensitivity"], "outputs": [{"item": "品类加权评分表与敏感度结论", "dest": "设计案 §1 · 选型页"}]},
    {"ch": 4, "title": "平台规则与设计约束", "sections": ["mg-design-04-1-package-size-budget", "mg-design-04-2-first-load-cold-start", "mg-design-04-3-platform-capability-review"], "outputs": [{"item": "包体预算表", "dest": "设计案 §2 · 约束页"}, {"item": "首屏冷启动预算", "dest": "设计案 §2 · 约束页"}, {"item": "平台能力与审核红线清单", "dest": "设计案附录 · 清单 B"}]},
    {"ch": 5, "title": "目标用户、动机与生命周期", "sections": ["mg-design-05-1-user-persona-scenario", "mg-design-05-2-motivation-payer-tier", "mg-design-05-3-lifecycle-retention-structure"], "outputs": [{"item": "用户画像与场景卡", "dest": "设计案 §2 · 用户页"}, {"item": "留存结构假设表", "dest": "设计案 §2 · 用户页"}]},
    {"ch": 6, "title": "核心玩法与核心循环", "sections": ["mg-design-06-1-core-loop-breakdown", "mg-design-06-2-loop-duration-feedback", "mg-design-06-3-minimal-playable-prototype"], "outputs": [{"item": "核心循环拆解图与动作表", "dest": "设计案 §3 · 玩法页"}, {"item": "最小可玩原型范围说明", "dest": "设计案 §5 · 原型页"}]},
    {"ch": 7, "title": "关卡与难度曲线", "sections": ["mg-design-07-1-level-structure", "mg-design-07-2-difficulty-flow-curve", "mg-design-07-3-level-calibration-generation"], "outputs": [{"item": "关卡结构与难度曲线", "dest": "设计案 §3 · 关卡页"}, {"item": "关卡校准参数表", "dest": "设计案附录 · 数据表 A"}]},
    {"ch": 8, "title": "成长、进度与放置收益", "sections": ["mg-design-08-1-progression-curve", "mg-design-08-2-progress-one-more-run", "mg-design-08-3-offline-earnings-return"], "outputs": [{"item": "成长曲线与锚点表", "dest": "设计案 §3 · 成长页"}, {"item": "离线收益规则", "dest": "设计案 §3 · 成长页"}]},
    {"ch": 9, "title": "社交与裂变机制", "sections": ["mg-design-09-1-social-surface", "mg-design-09-2-viral-k-factor", "mg-design-09-3-social-pressure-protection"], "outputs": [{"item": "社交面清单与 K 因子假设", "dest": "设计案 §3 · 社交页"}]},
    {"ch": 10, "title": "变现设计前置", "sections": ["mg-design-10-1-iaa-ad-format-placement", "mg-design-10-2-iap-hybrid-structure", "mg-design-10-3-monetization-experience-balance"], "outputs": [{"item": "广告形态与场景清单", "dest": "设计案 §4 · 变现页"}, {"item": "内购点结构表", "dest": "设计案 §4 · 变现页"}]},
    {"ch": 11, "title": "产品文档、预算与立项决策", "sections": ["mg-design-11-1-one-pager-greenlight-doc", "mg-design-11-2-budget-schedule-estimate", "mg-design-11-3-greenlight-stop-loss"], "outputs": [{"item": "一页纸立项文档", "dest": "设计案扉页 · 摘要"}, {"item": "止损线与决策条件", "dest": "设计案 §6 · 评审页"}]},
    {"ch": 12, "title": "玩法原型与可玩性验证", "sections": ["mg-design-12-1-prototype-scope-goal", "mg-design-12-2-playability-metric-test", "mg-design-12-3-prototype-iteration-decision"], "outputs": [{"item": "可玩性指标与验证结论", "dest": "设计案 §5 · 原型页"}]},
    {"ch": 13, "title": "商业化模型与收入预测", "sections": ["mg-design-13-1-revenue-dau-arpdau", "mg-design-13-2-hybrid-revenue-structure", "mg-design-13-3-revenue-forecast-scenario"], "outputs": [{"item": "收益模型与三情景预测", "dest": "设计案 §4 · 商业页"}]},
    {"ch": 14, "title": "开发预算与资源计划", "sections": ["mg-design-14-1-staffing-effort-estimate", "mg-design-14-2-outsourcing-asset-cost", "mg-design-14-3-milestone-risk-buffer"], "outputs": [{"item": "人力工时与外包成本表", "dest": "设计案 §5 · 资源页"}, {"item": "里程碑与风险缓冲表", "dest": "设计案 §5 · 资源页"}]},
    {"ch": 15, "title": "立项评审与设计案交付", "sections": ["mg-design-15-1-review-material-process", "mg-design-15-2-design-doc-assembly", "mg-design-15-3-deliverable-acceptance-handoff"], "outputs": [{"item": "评审材料清单与流程", "dest": "设计案 §6 · 评审页"}, {"item": "设计案装配清单", "dest": "设计案 §6 · 评审页"}, {"item": "交付验收报告与卷二衔接", "dest": "设计案 §6 · 交付页"}]}
  ]
}

清单里每章都登记了三个 sections——它们就是卷一该章的三个节 slug。把 slug 写进配置不只是为了记录,更是为了让命名规则可被断言:只要规则定了,任何一次装配都能被机器核对。

15.2.3 校验脚本:四十五个节 slug 的命名规则

装配清单最容易出的错,是节 slug 的命名与章号对不上——手抄时把 07-2 写成 06-2,或者漏掉一节。这类错肉眼极难发现,但规则是死的:卷一的节 slug 必须形如 mg-design-NN-S-name,其中 NN 等于该节所属章号、S 取 1/2/3。

下面这段是 S 层校验脚本,它读装配清单,断言十五个章号齐全、每章恰好三节、合计四十五个 slug、每个 slug 的章号与所在章一致、每章至少一条产出物。任何一条不成立,脚本以非 0 退出。

#!/usr/bin/env python3
# file: sim/ch15_check_assembly.py
# tier: S
# verify: /opt/homebrew/bin/python3 sim/ch15_check_assembly.py --config config/ --out out/
"""设计案装配清单校验:读 config/ch15_design_doc.json,断言
(1)15 章齐全、每章恰好 3 节、合计 45 个节 slug;
(2)每个节 slug 形如 mg-design-NN-S-name,且 NN 与该节所属章号一致、S 在 1–3;
(3)每章至少一条产出物,且产出物条目有 item 与 dest。
任一断言不成立即非 0 退出。

产出 out/ch15_design_doc.json(装配摘要)+ out/ch15_design_doc.csv(逐章一行)。
"""

from __future__ import annotations

import argparse
import csv
import json
import os
import re
import sys

SLUG_RE = re.compile(r"^mg-design-(\d{2})-([1-3])-([a-z0-9]+(?:-[a-z0-9]+)*)$")
CHAPTER_MAX = 15


def load(cfg_dir):
    with open(os.path.join(cfg_dir, "ch15_design_doc.json"), encoding="utf-8") as f:
        return json.load(f)


def check_chapter(ch, seen_slugs):
    """返回该章的缺项清单(空 = 通过),并把其节 slug 记入 seen_slugs。"""
    problems = []
    sections = ch.get("sections") or []
    if len(sections) != 3:
        problems.append(f"第 {ch.get('ch')} 章节数 {len(sections)} != 3")
    for slug in sections:
        if slug in seen_slugs:
            problems.append(f"节 slug 重复:{slug}")
        seen_slugs.add(slug)
        m = SLUG_RE.match(slug)
        if not m:
            problems.append(f"节 slug 命名不合规:{slug}")
            continue
        nn, sec = int(m.group(1)), int(m.group(2))
        if nn != ch.get("ch"):
            problems.append(f"节 slug 章号错位:{slug} 属第 {nn} 章,却挂在第 {ch.get('ch')} 章")
        if sec not in (1, 2, 3):
            problems.append(f"节号越界:{slug}")
    outputs = ch.get("outputs") or []
    if not outputs:
        problems.append(f"第 {ch.get('ch')} 章无产出物条目")
    for o in outputs:
        if not (isinstance(o.get("item"), str) and o["item"].strip()):
            problems.append(f"第 {ch.get('ch')} 章产出物缺 item")
        if not (isinstance(o.get("dest"), str) and o["dest"].strip()):
            problems.append(f"第 {ch.get('ch')} 章产出物缺 dest")
    return problems


def main() -> int:
    ap = argparse.ArgumentParser()
    ap.add_argument("--config", default="config/")
    ap.add_argument("--out", default="out/")
    args = ap.parse_args()

    cfg = load(args.config)
    chapters = cfg["chapters"]
    problems = []

    ch_nums = [c.get("ch") for c in chapters]
    if sorted(ch_nums) != list(range(1, CHAPTER_MAX + 1)):
        problems.append(f"章号不是 1..{CHAPTER_MAX} 的排列:{sorted(ch_nums)}")

    seen = set()
    rows = []
    for ch in sorted(chapters, key=lambda c: c.get("ch", 0)):
        ch_probs = check_chapter(ch, seen)
        problems.extend(ch_probs)
        rows.append({
            "ch": ch.get("ch"),
            "title": ch.get("title"),
            "sectionCount": len(ch.get("sections") or []),
            "outputCount": len(ch.get("outputs") or []),
            "pass": not ch_probs,
        })

    if len(seen) != CHAPTER_MAX * 3:
        problems.append(f"节 slug 合计 {len(seen)} != {CHAPTER_MAX * 3}")

    parts = cfg.get("parts") or []
    part_chapters = sorted({c for p in parts for c in p.get("chapters", [])})
    if part_chapters != list(range(1, CHAPTER_MAX + 1)):
        problems.append(f"parts 覆盖章号不完整:{part_chapters}")

    os.makedirs(args.out, exist_ok=True)
    report = {
        "source": "sim/ch15_check_assembly.py",
        "docTitle": cfg["docTitle"],
        "chapterCount": len(chapters),
        "sectionCount": len(seen),
        "partCount": len(parts),
        "chapters": rows,
        "problems": problems,
        "ok": not problems,
    }
    with open(os.path.join(args.out, "ch15_design_doc.json"), "w", encoding="utf-8") as f:
        json.dump(report, f, ensure_ascii=False, indent=2)
    with open(os.path.join(args.out, "ch15_design_doc.csv"), "w", encoding="utf-8", newline="") as f:
        w = csv.writer(f)
        w.writerow(["ch", "title", "sectionCount", "outputCount", "pass"])
        for r in rows:
            w.writerow([r["ch"], r["title"], r["sectionCount"], r["outputCount"], str(r["pass"]).lower()])

    print(f"doc={cfg['docTitle']}")
    print(f"章数={len(chapters)} 节 slug 数={len(seen)} 分部数={len(parts)}")
    for r in rows:
        mark = "PASS" if r["pass"] else "FAIL"
        print(f"  {mark} 第 {r['ch']:>2} 章 {r['title']:<16} 节={r['sectionCount']} 产出物={r['outputCount']}")
    for p in problems:
        print(f"  FAIL {p}")
    print("== 设计案装配:OK" if report["ok"] else "== 设计案装配:FAIL")
    return 0 if report["ok"] else 1


if __name__ == "__main__":
    sys.exit(main())

本机实跑输出:

doc=《合成小岛》产品设计案(卷一装配)
章数=15 节 slug 数=45 分部数=6
  PASS 第  1 章 小游戏市场全景          节=3 产出物=2
  PASS 第  2 章 榜单与竞品拆解          节=3 产出物=2
  PASS 第  3 章 品类图谱与选择模型        节=3 产出物=1
  PASS 第  4 章 平台规则与设计约束        节=3 产出物=3
  PASS 第  5 章 目标用户、动机与生命周期     节=3 产出物=2
  PASS 第  6 章 核心玩法与核心循环        节=3 产出物=2
  PASS 第  7 章 关卡与难度曲线          节=3 产出物=2
  PASS 第  8 章 成长、进度与放置收益       节=3 产出物=2
  PASS 第  9 章 社交与裂变机制          节=3 产出物=1
  PASS 第 10 章 变现设计前置           节=3 产出物=2
  PASS 第 11 章 产品文档、预算与立项决策     节=3 产出物=2
  PASS 第 12 章 玩法原型与可玩性验证       节=3 产出物=1
  PASS 第 13 章 商业化模型与收入预测       节=3 产出物=1
  PASS 第 14 章 开发预算与资源计划        节=3 产出物=2
  PASS 第 15 章 立项评审与设计案交付       节=3 产出物=3
== 设计案装配:OK

十五个章号全 PASS,合计四十五个节 slug。这份输出还顺带回答了一个装配问题:每章产出物数量并不均匀——第 3、9、12、13 章各只有一件产出物,第 4、15 章各三件。这不奇怪,产出物多寡取决于那一章的结论有多「可交付」:品类评分是一张表,社交机制是一段假设,而平台约束既出预算表又出冷启动预算还出红线清单。装配清单允许这种不均匀,它只要求每章至少一件,剩下的交给去向映射。

15.2.4 六个部分:设计案的骨架

装配清单把设计案分成六个部分,每个部分回答一组问题、面向一类读者:

部分覆盖章回答的问题主要读者
§1 机会与品类1、2、3做什么方向、凭什么老板、发行
§2 约束与用户4、5平台允许做什么、给谁做主程、主策
§3 玩法与成长6、7、8、9玩起来是什么样、怎么留住人主策、关卡、数值
§4 变现与商业10、11、13怎么赚钱、值不值得做发行、财务
§5 原型与资源12、14先验证什么、要多少人多少时间制作人、主程
§6 评审与交付15怎么评审、怎么验收全体

注意 §4 把第 11 章(一页纸与止损线)和第 13 章(收益模型)放在一起,而不是按章号顺序。这正是按问题装配而非按章号堆叠的价值:变现和「值不值得做」是同一个问题的两面,放在一起读者才能连着读完。骨架一旦这样定下来,第 10、11、13 章的内容就被「拉」到了 §4,而不是各自待在自己的章里。

15.2.5 装配时的三处取舍

装配清单落地的过程中,有三处反复出现的取舍值得写下来:

  1. 一页纸进扉页,不进正文。第 11 章的一页纸立项文档去向是「扉页 · 摘要」。老板翻开设计案先看这一页,如果它埋在第十一章正文里,等于没有。
  2. 数据表进附录,正文只放结论。包体预算表、关卡校准参数表、市场规模情景都归到附录。正文写「主包 920KB 达标」,附录放逐资产的明细。正文可读,附录可查。
  3. 重复引用不复制内容。收益假设只在第 13 章的产出物里写一次,第 10、11 章需要时引用它而不是重抄。装配清单的 dest 字段就是这种引用的锚点——同一份产出物只装配一次。

这三条合起来是一句话:装配是「搬运 + 引用」,不是「复制 + 粘贴」。清单只登记产出物与去向,不登记内容本身,正是为了让「一份结论只有一个家」。

小结

  • 设计案不是文档合集,是装配关系;核心是一张「产出物 → 去向」的映射表。
  • D 层配置用 parts 定骨架、chapters 定填充,逐章登记三个节 slug 与产出物去向;S 层脚本断言十五章各三节、四十五个节 slug 命名与章号一致。
  • 本机实跑:十五个章号全 PASS,节 slug 合计 45;产出物数量按章不均匀,脚本只要求每章至少一件。
  • 六个部分按问题而非章号装配:§4 把变现与商业结论拉到一起,读者才能连着读。
  • 三处取舍:一页纸进扉页、数据表进附录、重复引用不复制——一份结论只有一个家。

装配清单定义了设计案的结构,但结构对不对、交付物齐不齐,还需要一次总验收。下一节是卷一最后一节,把评审、装配、交付三份配置串起来做一次端到端核对,并把接力棒交给卷二。

阅读导航:上一节:立项评审材料与流程 · 下一节:交付物验收与下一卷衔接 。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「minigame」更多文章

  1. 附录 F:全书公式、参数与数据集索引
  2. 9.3 同意管理与数据出境
  3. 9.2 COPPA 与儿童向游戏的限制