dsh-project-based-learning 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/CONTRIBUTING.md +80 -0
  3. package/LICENSE +21 -0
  4. package/README.md +120 -0
  5. package/README.zh.md +118 -0
  6. package/cordis.patch.yml +15 -0
  7. package/docs/DESIGN-AUDIT.md +505 -0
  8. package/docs/ENGINE-REVISION-2.zh.md +487 -0
  9. package/docs/installing.zh.md +105 -0
  10. package/docs/original-workflow.zh.md +379 -0
  11. package/docs/releasing.zh.md +85 -0
  12. package/docs/review-round1-A-edu.zh.md +66 -0
  13. package/docs/review-round1-B-eng.zh.md +60 -0
  14. package/docs/review-round1-C-bounded.zh.md +55 -0
  15. package/docs/zero-knowledge-path.zh.md +60 -0
  16. package/examples/PROGRESS.demo.md +72 -0
  17. package/examples/state.demo.json +185 -0
  18. package/examples/state.selftest-invalid.json +58 -0
  19. package/lib/index.js +64 -0
  20. package/package.json +77 -0
  21. package/skills/dsh-coach/SKILL.md +108 -0
  22. package/skills/dsh-coach/assets/review-report.md +40 -0
  23. package/skills/dsh-coach/assets/stage-acceptance.md +51 -0
  24. package/skills/dsh-coach/assets/state.template.json +59 -0
  25. package/skills/dsh-coach/assets/task-card.md +29 -0
  26. package/skills/dsh-coach/references/domains/unity-csharp/archetypes.md +306 -0
  27. package/skills/dsh-coach/references/domains/unity-csharp/diagnosis-bank.md +978 -0
  28. package/skills/dsh-coach/references/domains/unity-csharp/example.md +356 -0
  29. package/skills/dsh-coach/references/domains/unity-csharp/glossary.md +110 -0
  30. package/skills/dsh-coach/references/domains/unity-csharp/manifest.yml +14 -0
  31. package/skills/dsh-coach/references/domains/unity-csharp/pitfalls.md +400 -0
  32. package/skills/dsh-coach/references/domains/unity-csharp/verification.md +308 -0
  33. package/skills/dsh-coach/references/engine/adapt.md +48 -0
  34. package/skills/dsh-coach/references/engine/diagnosis.md +76 -0
  35. package/skills/dsh-coach/references/engine/domain-contract.md +73 -0
  36. package/skills/dsh-coach/references/engine/intake.md +63 -0
  37. package/skills/dsh-coach/references/engine/permissions.md +44 -0
  38. package/skills/dsh-coach/references/engine/review-acceptance.md +67 -0
  39. package/skills/dsh-coach/references/engine/route.md +51 -0
  40. package/skills/dsh-coach/references/engine/state.md +116 -0
  41. package/skills/dsh-coach/references/engine/task-loop.md +68 -0
  42. package/skills/dsh-coach/scripts/coach-install.mjs +98 -0
  43. package/skills/dsh-coach/scripts/coach-selftest.mjs +205 -0
  44. package/skills/dsh-coach/scripts/coach-validate.mjs +817 -0
@@ -0,0 +1,40 @@
1
+ # 审阅报告
2
+
3
+ > 每条反馈必须完整填写七项。**无法核对的判断降级为提问**,不得作为断言给出。
4
+
5
+ ## 概览
6
+
7
+ - 审阅对象:
8
+ - 用户提交的证据 id:
9
+ - 结论摘要(一句话):
10
+
11
+ ## 问题清单
12
+
13
+ ### 阻塞
14
+
15
+ | # | 位置 | 发生机制 | 实际影响 | 最小修复方向 | 修复后如何验证 | 依据 | 核对状态 |
16
+ |---|---|---|---|---|---|---|---|
17
+ | 1 | | | | | | | 已核对 / 推测 |
18
+
19
+ ### 重要
20
+
21
+ | # | 位置 | 发生机制 | 实际影响 | 最小修复方向 | 修复后如何验证 | 依据 | 核对状态 |
22
+ |---|---|---|---|---|---|---|---|
23
+ | 1 | | | | | | | |
24
+
25
+ ### 建议
26
+
27
+ | # | 位置 | 发生机制 | 实际影响 | 最小修复方向 | 修复后如何验证 | 依据 | 核对状态 |
28
+ |---|---|---|---|---|---|---|---|
29
+ | 1 | | | | | | | |
30
+
31
+ ## 依据类型说明
32
+
33
+ - 文件路径与行号、日志片段、命令输出、截图位置、可复现步骤。
34
+ - `已核对`:教练确实查看过该材料;`推测`:未核对,需用户确认或补充材料。
35
+
36
+ ## 处理约定
37
+
38
+ - 优先由用户修复,不默认重写全部成果。
39
+ - 禁止只问"明白了吗":要求用户预测行为、处理变化、复现问题,或解释为什么修复有效。
40
+ - 用户选择"直接答案"时,按 `task-loop.md` 记录到 `state.directAnswers`。
@@ -0,0 +1,51 @@
1
+ # 阶段验收记录
2
+
3
+ > 结论写入 `state.current.stageStatus`、`state.evidence[]`、`state.open[]`、`state.retrievalRecap`,然后运行校验器。
4
+
5
+ - 阶段号与名称:
6
+ - 验收时间:
7
+ - 阶段可交付成果:
8
+
9
+ ## 1. 用户必须亲自完成的部分(逐项核对)
10
+
11
+ | # | userOnly 条目 | 是否亲手完成 | 依据 |
12
+ |---|---|---|---|
13
+ | 1 | | 是 / 否 | |
14
+
15
+ > 存在"否"的条目时,结论不得为"通过"。
16
+
17
+ ## 2. 验收标准逐项核对
18
+
19
+ | # | 验收标准 | 证据 id | 是否满足 |
20
+ |---|---|---|---|
21
+ | 1 | | | 是 / 否 |
22
+
23
+ ## 3. 证据充分性(三条同时满足才可用于"通过")
24
+
25
+ - [ ] **可复现**:他人按提交材料能得到同一结果
26
+ - [ ] **可解释**:用户能说明机制与取舍,而非"跑通了"
27
+ - [ ] **可修改**:用户能按新要求改动并说明后果
28
+
29
+ > 缺任意一条 → 最高只能判"有条件通过"。
30
+
31
+ ## 4. AI 参与度确认
32
+
33
+ - 本次成果中 AI 生成或帮助生成的部分:
34
+ - 用户是否已能解释、修改、验证:是 / 否
35
+ - 未满足时,该成果不计入能力证据。
36
+
37
+ ## 5. 检索式复述(限时 5 分钟,不看资料)
38
+
39
+ - 请用户复述:本阶段核心机制 / 一次关键取舍 / 一个失败模式
40
+ - 复述结论(写入 `retrievalRecap`):
41
+
42
+ ## 6. 结论
43
+
44
+ - [ ] **通过**:要求均有充分证据支持
45
+ - [ ] **有条件通过**:核心成果成立,但仍有明确待修项
46
+ - [ ] **未通过**:核心目标尚未实现或证据不足
47
+
48
+ - 理由:
49
+ - 待修项(写入 `state.open`,标注严重程度):
50
+ - 通过后进入:
51
+ - 需要调整的路线(如有,写入 `state.routeChanges` 并说明原因):
@@ -0,0 +1,59 @@
1
+ {
2
+ "schemaVersion": "1.0",
3
+ "engineVersion": "1.1.0",
4
+ "domain": "unity-csharp",
5
+ "domainVersion": "1.1.0",
6
+ "assumeGoal": true,
7
+ "revision": 1,
8
+ "updatedAt": "1970-01-01T00:00:00.000Z",
9
+ "goal": {
10
+ "statement": "",
11
+ "deliverable": "",
12
+ "why": "",
13
+ "doneCriteria": [],
14
+ "constraints": {
15
+ "time": "",
16
+ "tools": "",
17
+ "environment": "",
18
+ "permissions": ""
19
+ },
20
+ "nonGoals": []
21
+ },
22
+ "capability": [
23
+ { "dimension": "基础知识", "level": 1, "status": "待验证", "evidence": [], "gap": "", "impact": "" },
24
+ { "dimension": "实际应用", "level": 1, "status": "待验证", "evidence": [], "gap": "", "impact": "" },
25
+ { "dimension": "问题拆解", "level": 1, "status": "待验证", "evidence": [], "gap": "", "impact": "" },
26
+ { "dimension": "调试与纠错", "level": 1, "status": "待验证", "evidence": [], "gap": "", "impact": "" },
27
+ { "dimension": "结构与质量", "level": 1, "status": "待验证", "evidence": [], "gap": "", "impact": "" },
28
+ { "dimension": "独立程度", "level": 1, "status": "待验证", "evidence": [], "gap": "", "impact": "" },
29
+ { "dimension": "解释与迁移", "level": 1, "status": "待验证", "evidence": [], "gap": "", "impact": "" }
30
+ ],
31
+ "strategy": {
32
+ "deferred": [],
33
+ "immediate": [],
34
+ "practice": [],
35
+ "assumptions": []
36
+ },
37
+ "route": [],
38
+ "current": {
39
+ "stage": 1,
40
+ "stageStatus": "未开始",
41
+ "task": {
42
+ "title": "",
43
+ "deliverable": "",
44
+ "criteria": [],
45
+ "limits": [],
46
+ "nonGoals": [],
47
+ "estimateMin": 45,
48
+ "state": "未开始"
49
+ }
50
+ },
51
+ "evidence": [],
52
+ "open": [],
53
+ "routeChanges": [],
54
+ "directAnswers": [],
55
+ "authorizations": [],
56
+ "completedTasks": [],
57
+ "retrievalRecap": "",
58
+ "nextTask": ""
59
+ }
@@ -0,0 +1,29 @@
1
+ # 任务卡
2
+
3
+ > 由教练填写并随任务一起给出;字段与 `state.current.task` 对应。
4
+
5
+ - **本次成果**:
6
+ - **完成标准**(可判定,逐条写出):
7
+ - **限制**(时间、工具、环境、权限):
8
+ - **本次不做**:
9
+ - **预计时长**:__ 分钟(领域包默认区间内;超出上限必须拆成多个可独立验收的子任务)
10
+ - **领域包配方**(如有):`references/domains/<domain>/verification.md` 中对应条目
11
+ - **风险提示**(来自 `pitfalls`):
12
+
13
+ ## 用户的下一步(不含答案)
14
+
15
+ 要求用户先给出:思路 / 步骤 / 伪代码 / 结果预测 / 当前尝试。
16
+
17
+ ## 提示级别记录
18
+
19
+ | 轮次 | 用户请求的级别 | 实际给出的级别 | 内容摘要 |
20
+ |---|---:|---:|---|
21
+ | 1 | | | |
22
+
23
+ > 默认停在能推进的最低级别;未经用户请求不得跳到第 5 级。
24
+
25
+ ## 提交的证据
26
+
27
+ | 证据 id | 支持结论(claim) | 材料(artifact) | 强度 |
28
+ |---|---|---|---|
29
+ | | | | |
@@ -0,0 +1,306 @@
1
+ # Unity / C# 项目原型库
2
+
3
+ > 用途:intake 阶段先把学员想做的东西归到下面 6 个原型之一,再据此选阶段、出任务、定验收。
4
+ > 若学员的诉求横跨多个原型,选「能在 60–90 分钟内做出最小可验证成果」的那一个作为主线,其余降级为扩展方向。
5
+ > 每个原型的「最小可验证成果」都写成**可观察表现**——能在编辑器里当场看到、且有明确的通过/不通过判据,而不是「实现了 XX 功能」这类无法验收的说法。
6
+
7
+ ---
8
+
9
+ ## 原型 1:2D 玩法原型
10
+
11
+ **适用起点**
12
+ - 会用 Unity Hub 建 2D 工程,能在 Hierarchy 里拖出 Sprite,能写一个挂在对象上的脚本。
13
+ - 知道 `Update` 每帧执行,但对「输入/物理/动画各自该放哪儿」没概念。
14
+ - 有想法但没做过完整循环(输入 → 反馈 → 状态变化 → 结束条件)。
15
+
16
+ **最小可验证成果(MVP 的确切可观察表现)**
17
+ 1. 进入 Play 模式后,用键盘(如 A/D 或左右方向键)能把角色左右移动,松开即停;
18
+ 2. 角色与场景中至少一个标记为 Trigger 的区域重叠时,Console 打印一行带该区域名字的日志,且重叠期间不重复刷屏;
19
+ 3. 角色撞到实心墙壁时不会穿过去;
20
+ 4. 按 R 键可把角色位置与速度恢复到初始状态。
21
+ 以上 4 条同时成立即通过。任何一条说了「大概可以」即未通过。
22
+
23
+ **核心能力点**
24
+ - 区分 `Update`(逐帧逻辑、读输入)与 `FixedUpdate`(物理位移,用 `Rigidbody2D`)。
25
+ - 用 `Rigidbody2D.linearVelocity`(旧版本为 `velocity`)而不是直接改 `transform.position` 做物理移动。
26
+ - 输入读取方式统一(`Input.GetAxisRaw` 或 `InputSystem`,二选一,不混用)。
27
+ - 触发器回调 `OnTriggerEnter2D` 的触发条件与去重写法。
28
+ - 把可调数值(速度、跳跃力)暴露成 `[SerializeField]` 而不是散落在代码里的魔法数字。
29
+
30
+ **典型扩展方向**
31
+ - 加生命值/得分 UI,用 `TextMeshPro` 或 `UnityEngine.UI` 的 `Text`。
32
+ - 加一个对象池管理子弹/敌人,避免频繁 `Instantiate`/`Destroy`。
33
+ - 加简单的状态机(待机/移动/受击),用 `enum` + `switch` 起步。
34
+ - 加关卡数据配置:把每关参数存成 `ScriptableObject` 资源。
35
+ - 加音效与受击闪白,体会「反馈层次」。
36
+
37
+ **阶段切分建议(2–4 阶段)**
38
+ - 阶段 A(30 分钟):让角色动起来。只做「键盘输入 → `Rigidbody2D` 速度 → 屏幕上看得见移动」。验收:移动方向与按键一致,松手停下。
39
+ - 阶段 B(30–45 分钟):加碰撞与触发。验收:撞墙不穿模;进入 Trigger 区域打印一次日志。
40
+ - 阶段 C(45–90 分钟):加重置与一个可调参数面板。验收:R 键复位;Inspector 中改速度值立刻影响手感。
41
+ - 阶段 D(可选,90 分钟以上):加得分 UI 或对象池。
42
+
43
+ **验收要点**
44
+ - 逐条跑上面 4 条可观察表现,每条给出「通过 / 不通过 / 未验证」。
45
+ - 检查移动逻辑是否在 `FixedUpdate` 且用刚体 API;直接改 `transform.position` 视为未达标。
46
+ - 检查触发日志是否去重;重叠期间每帧刷屏视为未达标。
47
+ - 检查是否有魔法数字残留;速度值是否可在 Inspector 里调。
48
+ - 让学员口头复述「为什么移动写在 FixedUpdate」,答不出则阶段 C 之前要补。
49
+
50
+ **常见失败模式**
51
+ - **穿模**:碰撞体没配、刚体设为 `Kinematic`、或高速物体没开连续碰撞检测。
52
+ - **抖动**:在 `Update` 里改物理位置,或在 `FixedUpdate` 里读输入导致丢帧。
53
+ - **触发器不触发**:双方都没刚体;或只加了 `Collider2D` 忘了勾 `isTrigger`。
54
+ - **输入失灵**:混用新旧输入系统,或工程里 Input System Package 已启用但代码仍用旧 API。
55
+ - **一改数值就没手感**:数值是硬编码的;或改了 Inspector 里的值但运行时被代码在 `Awake` 里覆盖。
56
+ - **越写越乱**:一个脚本同时管输入、物理、UI、音效,超过 200 行后无法定位问题。
57
+
58
+ ---
59
+
60
+ ## 原型 2:3D 移动与相机
61
+
62
+ **适用起点**
63
+ - 已完成过至少一次 2D 或简单 3D 移动练习,知道 `Update`/`FixedUpdate` 的区别。
64
+ - 想做的核心是「角色在 3D 空间里移动 + 相机跟随」,而不是关卡设计。
65
+
66
+ **最小可验证成果(MVP 的确切可观察表现)**
67
+ 1. 用 WASD 让角色在水平面上移动,移动方向相对于相机朝向(按 W 总是往「屏幕里」走);
68
+ 2. 角色始终贴合地面,走下坡道不会浮空或陷进去;
69
+ 3. 相机在角色身后平滑跟随,转向时相机跟得上但不过冲抖动;
70
+ 4. 用鼠标(或方向键)可环绕角色转动相机,相机不会穿进墙体。
71
+ 以上 4 条同时成立即通过。
72
+
73
+ **核心能力点**
74
+ - 相机相对方向换算:把输入向量从相机空间转到世界空间(`Transform.TransformDirection` 或相机 forward/right 投影到水平面)。
75
+ - 3D 物理用 `Rigidbody` + `CapsuleCollider`,移动用刚体速度或 `MovePosition`。
76
+ - 相机跟随写在 `LateUpdate`,避免与角色同帧更新造成抖动。
77
+ - 相机碰撞处理(射线检测或 `Cinemachine` 的碰撞模块),理解「为什么不穿墙需要额外代码」。
78
+ - 欧拉角与 `Quaternion` 的取舍:累积旋转不要用 `transform.eulerAngles` 直接加减。
79
+
80
+ **典型扩展方向**
81
+ - 接入 `Cinemachine` 做虚拟相机、跟随与混合。
82
+ - 加冲刺/跳跃,配地面检测(`Physics.CheckSphere` 或 `Raycast`)。
83
+ - 加相机灵敏度设置并持久化到 `PlayerPrefs`。
84
+ - 加第一人称/第三人称切换。
85
+ - 用 `CharacterController` 替换刚体方案,对比两者手感差异。
86
+
87
+ **阶段切分建议(2–4 阶段)**
88
+ - 阶段 A(30 分钟):相机相对移动。验收:按 W 永远朝屏幕里走,转相机后仍成立。
89
+ - 阶段 B(30 分钟):相机跟随。验收:跟随写在 `LateUpdate`,快速转向时画面不抖。
90
+ - 阶段 C(30–45 分钟):相机防穿墙。验收:贴墙转身,相机不进入墙体内部。
91
+ - 阶段 D(可选):跳跃与地面检测。
92
+
93
+ **验收要点**
94
+ - 让学员转 90° 相机后再按 W,观察是否仍「朝屏幕里走」。
95
+ - 检查跟随代码所在回调;写在 `Update` 里要求给出不抖的理由,否则改到 `LateUpdate`。
96
+ - 检查是否用欧拉角累积旋转;若有,要求改为 `Quaternion` 相乘。
97
+ - 检查相机防穿墙是否真的用了检测;「把相机拉远一点」不算实现。
98
+
99
+ **常见失败模式**
100
+ - **相机一转就晕**:直接对 `transform.eulerAngles.x` 累加,导致角度跳变。
101
+ - **抖动**:相机跟随与角色移动在同一帧、同一回调里更新。
102
+ - **移动方向错**:用世界坐标方向而不是相机相对方向,相机转动后操作反直觉。
103
+ - **贴地失败**:用 `transform.position.y` 硬设高度而不是物理或地面检测。
104
+ - **相机穿墙**:只做了跟随没做碰撞;或用了碰撞但没设 `LayerMask`,被角色自身碰撞体挡住。
105
+
106
+ ---
107
+
108
+ ## 原型 3:编辑器工具 / 资源管线
109
+
110
+ **适用起点**
111
+ - 能写 `MonoBehaviour`,知道 `Editor` 文件夹的存在但没写过编辑器脚本。
112
+ - 面对「手动改 200 个预制体」「每次导入贴图都要改设置」这类重复劳动,想自动化。
113
+
114
+ **最小可验证成果(MVP 的确切可观察表现)**
115
+ 1. 顶部菜单栏出现一个自定义菜单项(如 `Tools/我的工具`),点击后弹出一个 `EditorWindow`;
116
+ 2. 窗口里有至少一个输入框或按钮;点击按钮后,对 Project 窗口中选中的资源执行一次批量修改(如统一修改贴图的 `Filter Mode`);
117
+ 3. 修改后资源的 Inspector 里数值确实变了,且 Console 打印出「处理了 N 个资源」;
118
+ 4. 全程无需手动逐个点开资源。
119
+ 以上 4 条同时成立即通过。
120
+
121
+ **核心能力点**
122
+ - 编辑器代码的物理隔离:脚本放在 `Editor` 文件夹或标记为 Editor 平台的 asmdef,否则打包失败。
123
+ - `[MenuItem]` 入口与 `EditorWindow` 的 `GetWindow`/`OnGUI`(或 UI Toolkit 的 `CreateGUI`)两条路线。
124
+ - `Selection.objects` / `Selection.activeObject` 读取用户当前选择。
125
+ - `AssetDatabase.SaveAssets()` / `AssetDatabase.Refresh()` 的调用时机,理解「改了内存里的资源」与「落盘」的区别。
126
+ - `Undo.RecordObject` 让操作可撤销(编辑器工具的基本礼貌)。
127
+
128
+ **典型扩展方向**
129
+ - 用 `AssetPostprocessor` 在导入时自动套用设置,取代手动跑工具。
130
+ - 改为 UI Toolkit(`VisualElement` + UXML),对比 IMGUI 的写法差异。
131
+ - 加进度条(`EditorUtility.DisplayProgressBar`),处理上千个资源时体验更好。
132
+ - 加「预览」模式:先列出将被修改的资源,用户确认后再执行。
133
+ - 把工具做成 UPM 包,供其它工程复用。
134
+
135
+ **阶段切分建议(2–4 阶段)**
136
+ - 阶段 A(30 分钟):菜单项 + 空窗口。验收:点菜单能弹窗,Console 有日志。
137
+ - 阶段 B(30 分钟):对选中资源做一次真实修改。验收:Inspector 数值变化可见。
138
+ - 阶段 C(30 分钟):加撤销与落盘。验收:Ctrl+Z 能撤销;重启编辑器后修改仍在。
139
+ - 阶段 D(可选):改成 `AssetPostprocessor` 自动触发。
140
+
141
+ **验收要点**
142
+ - 关掉编辑器重开,改动是否还在(区分「改了内存」与「已落盘」)。
143
+ - 按 Ctrl+Z 是否可撤销(`Undo.RecordObject` 是否调用)。
144
+ - 把工程切换构建目标到非 Editor 平台并触发编译,确认没有 `UnityEditor` 引用泄漏到运行时程序集。
145
+ - 检查是否硬编码资源路径;路径变了工具是否立刻失效。
146
+ - 让学员说明 `AssetDatabase.Refresh()` 不做会怎样。
147
+
148
+ **常见失败模式**
149
+ - **打包报错**:编辑器脚本没放在 `Editor` 文件夹,运行时程序集引用了 `UnityEditor` 命名空间。
150
+ - **改了没保存**:只改了内存对象没调 `AssetDatabase.SaveAssets()`,重启后白干。
151
+ - **不可撤销**:漏调 `Undo.RecordObject`,用户误操作无法回退。
152
+ - **误伤**:没做选择校验(`Selection.objects` 为空时直接遍历),抛空引用。
153
+ - **工具只对自己的工程有效**:写死了绝对路径或具体资源 GUID。
154
+
155
+ ---
156
+
157
+ ## 原型 4:数据驱动 UI 与存档
158
+
159
+ **适用起点**
160
+ - 能做出 UI 界面并让按钮点得动,但数据是散落在各脚本里的 `public` 字段。
161
+ - 目标是「把内容与代码分开」,让策划/自己改数值不用改代码。
162
+
163
+ **最小可验证成果(MVP 的确切可观察表现)**
164
+ 1. 在 Project 窗口右键能创建一个配置资源(`ScriptableObject`),里面至少有 3 个可编辑字段;
165
+ 2. 场景里有一个列表 UI,进入 Play 模式后按配置文件的内容生成 N 个条目(条数由配置决定,改配置不需要改代码);
166
+ 3. 点击某个条目后,UI 上显示该条目的详情;
167
+ 4. 按保存键写入存档,退出 Play 模式再进入,读取后 UI 显示上次的数据;
168
+ 5. 删掉存档文件后重新进入,程序不报错、回到默认状态。
169
+ 以上 5 条同时成立即通过。
170
+
171
+ **核心能力点**
172
+ - `ScriptableObject` 的 `[CreateAssetMenu]` 创建入口与运行时读取方式。
173
+ - 区分「配置数据」(`ScriptableObject` 资源,只读)与「运行时存档数据」(普通 C# 类 + 序列化)。
174
+ - 序列化限制:接口、泛型字典、`null` 的 `UnityEngine.Object` 都不适合直接序列化。
175
+ - 存档路径用 `Application.persistentDataPath`,不要用工程内相对路径。
176
+ - 存档读写的健壮性:文件不存在、内容损坏、字段新增后的兼容处理。
177
+ - UI 事件订阅与解绑(`onClick.AddListener` 要配 `RemoveListener`,或改用代码绑定生命周期)。
178
+
179
+ **典型扩展方向**
180
+ - 用 JSON(`JsonUtility` 或第三方库)替代 `PlayerPrefs`,支持嵌套结构。
181
+ - 加存档版本号与迁移逻辑。
182
+ - 用 `ScriptableObject` 做事件通道,解耦 UI 与逻辑。
183
+ - 加多存档槽与删除存档。
184
+ - 把列表 UI 改为对象池复用的滚动列表,处理 200 条以上数据。
185
+
186
+ **阶段切分建议(2–4 阶段)**
187
+ - 阶段 A(30 分钟):做出配置资源 + 读它生成 3 个 UI 条目。验收:改配置条数,UI 条数跟着变。
188
+ - 阶段 B(30 分钟):点条目显示详情。验收:点不同条目显示不同内容。
189
+ - 阶段 C(45 分钟):加入存档读写。验收:写入后重启仍在;删文件后能恢复默认。
190
+ - 阶段 D(可选):加版本号与迁移。
191
+
192
+ **验收要点**
193
+ - 只改 `ScriptableObject` 资源、不改任何 `.cs`,UI 内容是否随之变化(这是本原型的核心判据)。
194
+ - 存档文件位置是否在 `Application.persistentDataPath` 下(让学员打印并指出实际路径)。
195
+ - 手动把存档文件内容改成乱码,程序是否优雅降级而不是抛异常崩掉。
196
+ - 检查 UI 事件是否有对应的解绑;反复进出 Play 模式观察监听器是否累积。
197
+ - 关闭 Domain Reload 后测试一次,确认没有依赖静态字段存活。
198
+
199
+ **常见失败模式**
200
+ - **改配置没用**:运行时又用代码覆盖了配置值,或读的是另一份硬编码默认值。
201
+ - **存档写到工程里**:用相对路径导致打包后无写权限。
202
+ - **存档一改结构就崩**:没有版本号与容错,新增字段直接反序列化失败。
203
+ - **UI 重复绑定**:每次打开面板都 `AddListener` 却不解绑,点击一次触发多次。
204
+ - **ScriptableObject 当运行时数据用**:在编辑器里改了资源,Play 模式一结束改动残留,污染源数据。
205
+ - **泛型字典存不进档**:`JsonUtility` 不支持 `Dictionary`,却不自知。
206
+
207
+ ---
208
+
209
+ ## 原型 5:小规模网络同步
210
+
211
+ **适用起点**
212
+ - 已完成至少一个单机玩法原型,了解 `Update` 与协程。
213
+ - 想验证「两个客户端能看到彼此」,目标是小规模(2–4 人、局域网或同一房间),不是商业级联机。
214
+
215
+ **最小可验证成果(MVP 的确切可观察表现)**
216
+ 1. 启动第一个实例并「创建房间」,启动第二个实例并加入,两端 Console 都打印出参与者数量 2;
217
+ 2. 在任一实例里移动角色,另一个实例在同一次会话中看到该角色同步移动,位置偏差肉眼不可辨;
218
+ 3. 断开其中一个实例,另一个实例能收到「有人离开」的提示,且不报错、不卡死;
219
+ 4. 同步逻辑与本地表现解耦:本地输入立刻响应(不走服务器往返)也能看到同步结果。
220
+ 以上 4 条同时成立即通过。
221
+
222
+ **核心能力点**
223
+ - 客户端-服务器模型:谁是权威(host/authority),谁只是表现层。
224
+ - 本地输入与网络同步分离:本地先动(客户端预测的最简形式),再发状态。
225
+ - 同步频率与插值:不要在 `Update` 里每帧发网络包,用固定间隔 + 接收端插值平滑。
226
+ - 只同步必要字段:位置/朝向/状态枚举,而不是整个对象。
227
+ - 生命周期:加入/离开/重连的回调,以及对象销毁与网络对象解绑。
228
+
229
+ **典型扩展方向**
230
+ - 接入官方联网方案(如 Netcode for GameObjects 或 Mirror 一类框架),对比手写 RPC 的差异。
231
+ - 加插值/外推,让高延迟下的移动更平滑。
232
+ - 加权威服务器校验,防止客户端乱发位置。
233
+ - 同一实例多开测试自动化,减少手动开窗口的重复劳动。
234
+ - 用 `Unity Transport` 观察丢包与延迟参数对表现的影响。
235
+
236
+ **阶段切分建议(2–4 阶段)**
237
+ - 阶段 A(30–45 分钟):两端能连上并互相看到「已连接」。验收:双实例 Console 都显示 2 人。
238
+ - 阶段 B(45 分钟):同步位置。验收:A 动,B 看得见,且不抖。
239
+ - 阶段 C(45 分钟):处理离开与异常断开。验收:关掉一个实例,另一个不崩且有提示。
240
+ - 阶段 D(可选):加插值与延迟容错。
241
+
242
+ **验收要点**
243
+ - 必须双实例实测,不接受「单实例看起来正常」。
244
+ - 检查是否每帧发包;用日志统计每秒发送次数,超过每秒 30 次要求说明理由。
245
+ - 检查同步的是否为必要字段;把整个 `Transform` 或大对象序列化发出视为未达标。
246
+ - 断线测试:直接关闭进程(非优雅退出),对端是否处理。
247
+ - 让学员说明「谁说了算」;答不出权威方则阶段 C 之前要补。
248
+
249
+ **常见失败模式**
250
+ - **只有一台机器能测**:没写双实例启动方式,导致验收无法进行。
251
+ - **本地手感变差**:把本地输入改成等服务器确认再动,操作明显延迟。
252
+ - **抖动**:接收端直接 `transform.position = 收到的值`,没有插值。
253
+ - **断线崩溃**:离开回调里访问已销毁对象,抛空引用。
254
+ - **带宽失控**:每帧全字段同步,两人测试就有明显延迟。
255
+ - **网络代码与玩法代码耦合**:同步逻辑写进玩法脚本,单机模式跑不起来。
256
+
257
+ ---
258
+
259
+ ## 原型 6:性能优化专项
260
+
261
+ **适用起点**
262
+ - 已有一个能跑的原型或小工程,现在「帧率不够 / 手机发烫 / 加载很慢」。
263
+ - 会用 Profiler 打开但看不懂数据,习惯靠猜来优化。
264
+
265
+ **最小可验证成果(MVP 的确切可观察表现)**
266
+ 1. 用 Profiler 采集一段固定时长的数据(如 30 秒稳定玩法),记录一个基线数值(CPU 主线程毫秒数或某方法的 GC 分配字节数);
267
+ 2. 定位到**至少一个**可量化的热点(有具体方法名 + 具体数值);
268
+ 3. 针对该热点做一处修改;
269
+ 4. 用同一段场景、同样时长重新采集,给出修改前后的数字对比;
270
+ 5. 重跑功能测试,确认玩法行为没有改变。
271
+ 以上 5 条同时成立即通过。「感觉快了」不算通过。
272
+
273
+ **核心能力点**
274
+ - 用 Profiler 而不是猜测定位瓶颈;区分 CPU 主线程、GC、渲染、物理各模块。
275
+ - 看懂 GC Alloc 列:谁在分配,分配了多少字节,是否每帧分配。
276
+ - 区分「一次性的慢」与「每帧的慢」,优先级完全不同。
277
+ - 常见优化手段的原理与代价:缓存引用、避免装箱、复用集合、对象池、减少 `GetComponent`/`Find` 调用。
278
+ - 优化必须可回退:改前留基线、改后做对比、行为不变才算成功。
279
+
280
+ **典型扩展方向**
281
+ - 用 Profiler 的 Deep Profile 或自定义 `ProfilerMarker` 精确到方法。
282
+ - 把热点从 `Update` 迁移到事件驱动或分帧处理。
283
+ - 用 Jobs/Burst 处理大批量纯计算(注意它要求的是无托管引用的数据结构)。
284
+ - 加自动化的性能回归测试,防止优化被后续提交破坏。
285
+ - 分析内存快照,找贴图/网格的常驻占用。
286
+
287
+ **阶段切分建议(2–4 阶段)**
288
+ - 阶段 A(30 分钟):建立基线。验收:拿出一个带时间戳的数值,能说清是怎么测的。
289
+ - 阶段 B(30–45 分钟):定位热点。验收:能指出具体方法名与它占用的毫秒/字节。
290
+ - 阶段 C(30–45 分钟):改一处并复测。验收:给出前后数字对比且功能未变。
291
+ - 阶段 D(可选):建立防止回退的检查手段。
292
+
293
+ **验收要点**
294
+ - 要求「基线与复测」用完全相同的场景与时长;条件不同则结果作废。
295
+ - 检查是否只改了声称的那一处;顺手大改会污染因果判断。
296
+ - 复测必须包含功能回归——优化后行为变了等于换了个 bug。
297
+ - 让学员解释「为什么这处是瓶颈」;只会背「不要用 GetComponent」而不看数据,视为未达标。
298
+ - 记录优化的代价(可读性下降、内存上升),诚实列出。
299
+
300
+ **常见失败模式**
301
+ - **凭感觉优化**:不测就改,改完也不知道有没有用。
302
+ - **测法不可比**:基线和复测场景不同、时长不同,数字没有意义。
303
+ - **优化错对象**:花时间优化只占 2% 的代码,真正的瓶颈在渲染或物理。
304
+ - **引入新 bug**:为省分配把集合改成静态复用,结果数据在对象间串了。
305
+ - **只看编辑器数据**:编辑器开销本身就高,结论不能直接外推到真机。
306
+ - **过度优化**:把可读性牺牲在没有实测收益的地方。