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,356 @@
1
+ # 完整示例:Unity 2D 玩法原型
2
+
3
+ > 本文件是**输出形态锚定示例**,展示引擎与 `unity-csharp` 领域包配合时各环节长什么样:intake → 能力画像 → 阶段定义 → 任务审阅 → 验收结论。
4
+ > 引擎侧的字段名与枚举取自 `references/engine/*`;学科内容取自本包 `archetypes.md`(原型 1)、`diagnosis-bank.md`、`pitfalls.md`、`verification.md`。
5
+ > 示例中的对话均为**示意**,不是真实用户数据。
6
+
7
+ ---
8
+
9
+ ## 一、Intake 结果
10
+
11
+ **教练开场**:`assumeGoal: true`,先用一行复述目标与可交付成果,不追问目标,直接进入诊断。
12
+
13
+ > 我理解你要做的是:一个**能在同学面前演示的 2D 平台跳跃小样**,交付物是一个 Unity 工程加一段 30 秒录屏。下面 12 项一次列完,能答的答,不确定的写「未知」就行。
14
+
15
+ **结构化清单回收结果**
16
+
17
+ | # | 条目 | 学员回答 |
18
+ | --- | --- | --- |
19
+ | 1 | 最终想完成什么 | 参加校内游戏开发比赛,需要先有**一个能演示的完成品** |
20
+ | 2 | 成果形式 | 可运行 + 可演示(编辑器里跑 + 录屏) |
21
+ | 3 | 为什么想做 | 想验证自己能不能独立做出一个「完整的小东西」,而不是一堆半成品 |
22
+ | 4 | 如何判断已完成 | 见下方 `doneCriteria` |
23
+ | 5 | 限制 | 每周 3 次、每次 60–90 分钟;Unity 6 + VS Code;Windows 笔记本、无独显 |
24
+ | 6 | 当前不做(非目标) | 多关卡、存档、美术打磨、移动端打包、联机 |
25
+ | 7 | 已有经验(五级自述) | **3 级**:能在提示下完成 |
26
+ | 8 | 独立完成过的相近任务 | 跟教程做过一个 2D 左右移动的 Demo |
27
+ | 9 | 经常卡住的环节 | 「报错看不懂」「改完不知道对不对」 |
28
+ | 10 | 可投入时间 | 每周 3 次 × 60–90 分钟 |
29
+ | 11 | 偏好 | 希望被指出错误,不怕直接说;喜欢先跑起来再补原理 |
30
+ | 12 | 可审阅材料 | Demo 工程仓库链接 + 30 秒录屏 |
31
+
32
+ **`state.goal`(落盘内容)**
33
+
34
+ ```json
35
+ {
36
+ "statement": "做一个可在编辑器内演示的 2D 平台跳跃小样:角色能左右移动、在平台上跳跃、收集金币计分,并在掉出地图后复位。",
37
+ "deliverable": "一个 Unity 2D 工程(含一个可运行场景)+ 一段 30 秒录屏 + 一份自查清单。",
38
+ "why": "参加校内游戏开发比赛前,先做出一个自己独立完成的、能演示的完整小样。",
39
+ "doneCriteria": [
40
+ "按 A/D 左右移动,松手即停;且在 15fps 与 60fps 下移动相同横向距离。",
41
+ "按空格起跳,仅在地面时生效;空中连按不产生第二次跳跃。",
42
+ "角色进入金币触发器时:金币消失、分数 +1、Console 只打印一次日志(不重复刷屏)。",
43
+ "角色掉出地图下方后:位置与分数回到初始值。",
44
+ "分数显示在屏幕左上角的 UI 上,且随收集即时更新。"
45
+ ],
46
+ "constraints": {
47
+ "time": "每周 3 次,每次 60–90 分钟",
48
+ "tools": "Unity 6、VS Code",
49
+ "environment": "Windows 笔记本,无独立显卡",
50
+ "permissions": "对自己的工程目录可读可写;允许 AI 只读审阅代码与截图"
51
+ },
52
+ "nonGoals": ["多关卡", "存档", "美术打磨", "移动端打包", "联机"]
53
+ }
54
+ ```
55
+
56
+ **`state.strategy` 摘要**
57
+
58
+ ```json
59
+ {
60
+ "deferred": ["多关卡与关卡切换", "存档", "移动端打包"],
61
+ "immediate": ["把「改完不知道对不对」变成可自查的固定动作"],
62
+ "practice": ["每次提交前用 Gizmos 确认碰撞体重叠", "改完数值先固定帧率再对比"],
63
+ "assumptions": [
64
+ "假设:学员能把「物理相关逻辑放 FixedUpdate」这条规则迁移到新代码(待阶段 1 任务 2 验证)",
65
+ "假设:学员的卡点是缺少排查方法而非概念缺失(待第一次审阅验证)"
66
+ ]
67
+ }
68
+ ```
69
+
70
+ ---
71
+
72
+ ## 二、诊断与能力画像
73
+
74
+ **本次诊断按引擎要求凑齐三类最小覆盖**:理解预测用题库索引中「2D 玩法原型」当前推荐的 **Q3-2**,问题定位用 Q4-2,小型实现用 Q6-1。此外,本例特意演示 **R9 讲授路径**:学员在 intake 中自述"没学过生命周期",因此教练**先讲授、再用 Q1-1 作确认题**(Q1-1 属事实性题,按题库约定不得作为首次接触题)。
75
+
76
+ | 顺序 | 题目 | 用途 | 最小诊断类别 | 考察维度 |
77
+ | --- | --- | --- | --- | --- |
78
+ | 1 | Q3-2 帧率变化时物理行为不一致 | 首次诊断 | 理解预测 | 问题拆解 |
79
+ | 2 | Q4-2 2D 触发器「怎么都不触发」 | 首次诊断 | 问题定位 | 调试与纠错 |
80
+ | 3 | Q6-1 写一个最小的对象池 | 首次诊断 | 小型实现 | 独立程度 |
81
+ | 4 | Q1-1 生命周期调用顺序 | **讲授后确认题**(R9:学员自述没学过 → 讲授 → 确认) | 理解预测(确认用) | 基础知识 |
82
+
83
+ **作答记录(示意)**
84
+
85
+ - **Q3-2**:能指出物理更新与帧率无关、但输入采样与渲染受帧率影响,也提到 `FixedUpdate` 的固定步长。
86
+ 但把"帧率越高跳得越高"当成引擎缺陷(实际是自己把位移写进了 `Update`);也未提出可验证假设(例如打印时间步长与刚体速度做对照)。
87
+ → 机制方向正确,**排查手段不足**,且倾向先下结论再找证据。
88
+ - **Q1-1(讲授后确认题)**:教练先按 R9 讲授了生命周期顺序与调用次数(含"`Awake` 绑定的是脚本实例创建时机、不是对象是否激活"),随后用本题确认。
89
+ 学员能按顺序说出 `A B C D`,也答对「`Start` 只一次、`OnEnable` 可多次」。
90
+ 但答错一处:认为 `SetActive(false)` 会触发 `OnDestroy`(实际只触发 `OnDisable`);并认为 `SetActive(true)` 会重新触发 `Start`。
91
+ → 讲授吸收良好,机制仍有一处明确错误;按证据分层**只记"部分验证"**。
92
+ - **Q4-2**:只给出 2 条原因(「两边都要勾 `Is Trigger`」——此条本身错误;「代码拼写」——已排除)。
93
+ 未提及 **Layer 碰撞矩阵**、**刚体类型组合**、**碰撞体尺寸为 0**。也没有提出用 Gizmos 先确认几何重叠。
94
+ → 这是 2D 原型最高频卡点,而排查手段明显不足。
95
+ - **Q6-1**:20 分钟内写出 `Queue<GameObject>` 结构,`Get`/`Release` 正确,主动说明「用 `SetActive` 而不是 `Destroy`」。
96
+ 但**遗漏状态重置**(没意识到复用对象会带回上次的脏状态),也答不出场景切换后池内引用会变成已销毁对象。
97
+ → 主体成立,边界缺失。
98
+
99
+ ### 能力画像(7 维)
100
+
101
+ | 维度 | 等级 | 状态 | 证据 | 主要缺口 | 对当前目标的影响 |
102
+ | --- | --- | --- | --- | --- | --- |
103
+ | 基础知识 | 3 | 部分验证 | E-01 | 混淆 `OnDisable` 与 `OnDestroy`;误以为 `SetActive(true)` 会重跑 `Start` | 可能在对象只是被停用时按「已销毁」处理,写出多余的重建与初始化逻辑 |
104
+ | 实际应用 | 3 | 已验证 | E-04, E-05 | 尚未在无教程指导下独立构建过完整玩法循环 | 阶段 1 任务 1、2 预计可在少量提示下完成 |
105
+ | 问题拆解 | 2 | 部分验证 | E-06 | 能指出受帧率影响的两处,但把"帧率越高跳得越高"归因为引擎缺陷;未提出可验证假设 | 阶段 1 任务 2、3 需先示范一次"先测量再结论"的排查顺序 |
106
+ | 调试与纠错 | 2 | 部分验证 | E-02 | 触发器排查只想到 2 条原因且其中 1 条错误;缺少系统排查顺序 | **直接阻塞阶段 1 任务 3(金币触发)**,需要先补排查方法 |
107
+ | 结构与质量 | 2 | 待验证 | — | 未考察 | 首个阶段影响小;在扩展阶段(对象池、状态机)会显现 |
108
+ | 独立程度 | 3 | 部分验证 | E-03 | 对象池缺状态重置与场景切换处理 | 扩展阶段会出现「复用对象带回脏数据」类问题 |
109
+ | 解释与迁移 | 2 | 待验证 | — | 未考察 | 需要求复述机制,避免「跟着做会、离开教程不会」 |
110
+
111
+ **证据表(`state.evidence[]`)**
112
+
113
+ ```json
114
+ [
115
+ { "id": "E-01", "stage": 0, "claim": "掌握 MonoBehaviour 生命周期回调的基本顺序与调用次数",
116
+ "artifact": "问答记录:Q1-1 作答原文", "strength": "部分验证",
117
+ "note": "顺序与次数正确;但对 `SetActive(false)` 的后果判断错误(以为会触发 `OnDestroy`)。知识类结论:单题正确只判部分验证,需"无提示解释机制+迁移"才可升级" },
118
+ { "id": "E-02", "stage": 0, "claim": "具备 2D 触发器问题的排查能力",
119
+ "artifact": "问答记录:Q4-2 作答原文", "strength": "部分验证",
120
+ "note": "仅给出 2 条原因,其中「两边都要勾 Is Trigger」为错误认知;未提 Layer 矩阵与刚体类型" },
121
+ { "id": "E-03", "stage": 0, "claim": "能独立实现对象池的基本取还逻辑",
122
+ "artifact": "提交的 Pool.cs(约 38 行,见诊断对话附件)", "strength": "部分验证",
123
+ "note": "Queue 结构与 SetActive 处理正确;缺状态重置与场景切换处理" },
124
+ { "id": "E-04", "stage": 0, "claim": "能在提示下完成 2D 刚体移动与触发器逻辑",
125
+ "artifact": "教程跟做 Demo 的仓库链接 + 30 秒录屏", "strength": "部分验证",
126
+ "note": "工程可运行;教练已实际打开场景核对过移动与碰撞体配置" },
127
+ { "id": "E-05", "stage": 0, "claim": "能按新要求改动代码并预测后果",
128
+ "artifact": "操作自述记录:诊断中要求把 moveSpeed 由 5 改为 12 并预测表现(学员原话摘录)", "strength": "部分验证",
129
+ "note": "用于支撑 E-04 的「可修改」条件;属操作自述,无反证时按部分验证接受,不再要求重复实测" },
130
+ { "id": "E-06", "stage": 0, "claim": "能指出帧率对物理表现的影响面,但缺少"先测量再结论"的排查习惯",
131
+ "artifact": "问答记录:Q3-2 作答原文", "strength": "部分验证",
132
+ "note": "知识类结论:单题正确只判部分验证;本条同时暴露排查方法缺口,已写入 问题拆解 维度的 gap" }
133
+ ]
134
+ ```
135
+
136
+ **下一轮待验证的假设**
137
+
138
+ 1. 假设学员能把「物理相关逻辑放 `FixedUpdate`」迁移到新代码 → 阶段 1 任务 2 验证。
139
+ 2. 假设「不进 `FixedUpdate`」是**不知道**而非**知道但图省事** → 第一次审阅时通过追问确认。
140
+ 3. 假设学员的卡点主要是缺少排查方法(而非概念缺失)→ 阶段 1 任务 3 用「触发器不触发」的现场复现验证。
141
+
142
+ **收尾动作**:请学员确认或修正以上结论。学员表示认同「调试与纠错」的 2 级判断,并补充「自己确实一报错就慌」。
143
+
144
+ ---
145
+
146
+ ## 三、阶段定义(`state.route[0]`)
147
+
148
+ ```json
149
+ {
150
+ "n": 1,
151
+ "name": "能跑能跳能收金币",
152
+ "deliverable": "一个可在编辑器内演示的 2D 场景(含移动、跳跃、金币计分、掉出复位)+ 30 秒录屏 + 一份自查清单",
153
+ "nonGoals": ["敌人", "多关卡", "存档", "美术打磨"],
154
+ "skills": [
155
+ "在 Update 记录输入意图、在 FixedUpdate 消费它",
156
+ "用 Rigidbody2D 做移动与跳跃(不直接改 transform.position)",
157
+ "用射线或重叠检测判断着地",
158
+ "OnTriggerEnter2D 的触发条件与去重",
159
+ "用 UI 显示分数并与玩法解耦"
160
+ ],
161
+ "prereq": ["能在 Hierarchy 里创建对象并挂载脚本", "能在 Inspector 里修改字段并理解序列化含义"],
162
+ "tasks": [
163
+ { "title": "左右移动", "estimateMin": 60 },
164
+ { "title": "跳跃与着地检测", "estimateMin": 90 },
165
+ { "title": "金币触发与计分", "estimateMin": 60 },
166
+ { "title": "掉出复位与分数 UI", "estimateMin": 60 }
167
+ ],
168
+ "userOnly": [
169
+ "亲手在 Inspector 里逐项配置 Rigidbody2D 与 Collider2D 的参数,并说明每一项为什么这么设",
170
+ "亲手打开 Gizmos 确认碰撞体轮廓在 Scene 视图中真的重叠",
171
+ "亲手复现一次「触发器不触发」,并独立定位到原因(教练只给到 3 级提示)",
172
+ "不看任何资料,在 5 分钟内复述 FixedUpdate 与 Update 的分工"
173
+ ],
174
+ "acceptance": [
175
+ "A/D 移动且松手即停;把 Application.targetFrameRate 分别设为 15 与 60,两次横向移动距离一致",
176
+ "空格仅在地面时起跳;快速连按不产生第二次跳跃",
177
+ "进入金币触发器:金币消失、分数 +1、Console 只打印一次",
178
+ "掉出地图下方:位置与分数复位到初始值",
179
+ "分数显示在左上角 UI,且收集后即时更新"
180
+ ],
181
+ "risks": [
182
+ "混用新旧输入系统导致按键无响应",
183
+ "用 transform.position.y 判断着地,导致高台上无法起跳",
184
+ "金币误用 OnTriggerStay2D 造成每帧重复加分",
185
+ "UI 直接引用玩法对象,导致后续拆分困难"
186
+ ],
187
+ "estimate": "4 次 × 60–90 分钟",
188
+ "next": "阶段 2:加入一个左右巡逻的敌人,并用对象池管理子弹"
189
+ }
190
+ ```
191
+
192
+ **为什么第一阶段是这个**:`doneCriteria` 的 5 条里有 4 条只依赖「移动 + 跳跃 + 触发 + 复位」,构成一个**最小可验证闭环**——做完就能当场演示,且每条都有可观察判据。敌人、多关卡等被放进 `nonGoals` 与 `deferred`,避免第一阶段铺得太开。
193
+
194
+ ---
195
+
196
+ ## 四、任务审阅报告
197
+
198
+ **审阅对象**:阶段 1 任务 2「跳跃与着地检测」提交的 `PlayerJump.cs`
199
+ **材料来源**:学员在对话中粘贴的完整文件内容(教练据此核对,标注为「已核对」)
200
+
201
+ ### 阻塞
202
+
203
+ **B-1|跳跃位移写在 `Update` 里直接改 `transform.position`**
204
+
205
+ | 要素 | 内容 |
206
+ | --- | --- |
207
+ | 位置 | `PlayerJump.cs` 第 24–31 行(`Update` 方法体内) |
208
+ | 发生机制 | `Update` 按渲染帧执行,物理按固定步长推进,两者调用次数比例不固定;且直接给 `transform.position` 赋值绕过了物理引擎的解算与插值。 |
209
+ | 实际影响 | 跳跃高度随帧率变化——在学员的无独显笔记本上会明显偏低。**直接导致验收标准第 1、2 条无法成立。** |
210
+ | 最小修复方向 | 在 `Update` 里只记录「本帧是否按下跳跃」到 `bool` 字段,把位移与速度赋值移到 `FixedUpdate`,改用 `Rigidbody2D.linearVelocity` 处理。 |
211
+ | 修复后验证方式 | 把 `Application.targetFrameRate` 分别设为 15 与 60,各跳一次并打印最高点 y 值;两次差值应在肉眼/数值上一致。 |
212
+ | 依据 | 学员提交的 `PlayerJump.cs` 第 24–31 行(粘贴于对话,教练已逐行阅读) |
213
+ | 核对状态 | 已核对 |
214
+
215
+ **B-2|着地检测用绝对高度 `transform.position.y <= 0` 判断**
216
+
217
+ | 要素 | 内容 |
218
+ | --- | --- |
219
+ | 位置 | `PlayerJump.cs` 第 38 行 |
220
+ | 发生机制 | 用「角色世界坐标高度」代替「与地面几何接触」的判断。这只在「地面恰好位于 y=0」时成立,本质是把关卡布局硬编码进了逻辑。 |
221
+ | 实际影响 | 只要场景里存在高台,角色站上去后 `isGrounded` 永远为 `false`,在高台上按空格无法起跳。**与验收标准第 2 条冲突**,且后续加平台时必须改代码。 |
222
+ | 最小修复方向 | 改用几何检测:从角色底部向下做 `Physics2D.Raycast`,或用 `Physics2D.OverlapCircle` 检测脚底小圆是否与地面 Layer 相交;把地面 Layer 做成可配置字段。 |
223
+ | 修复后验证方式 | 在场景里加一个 y = 3 的高台,角色站上去后跳跃应可用;再加一个 y = −2 的凹地重复一次。 |
224
+ | 依据 | `PlayerJump.cs` 第 38 行 |
225
+ | 核对状态 | 已核对 |
226
+
227
+ ### 重要
228
+
229
+ **I-1|起跳后未立即清掉着地标记,可以空中二段跳**
230
+
231
+ | 要素 | 内容 |
232
+ | --- | --- |
233
+ | 位置 | `PlayerJump.cs` 第 40–46 行 |
234
+ | 发生机制 | `isGrounded` 每帧重新计算,但起跳当帧到角色真正离开碰撞体之间还有若干帧仍为 `true`;这期间再次按空格就会被判定为合法起跳。 |
235
+ | 实际影响 | 产生非预期二段跳,验收标准第 2 条「空中连按不产生第二次跳跃」不成立。 |
236
+ | 最小修复方向 | 在成功起跳的那一帧立即把 `isGrounded` 置为 `false`,由后续的几何检测重新置位。 |
237
+ | 修复后验证方式 | 落地后以最快手速连按空格 5 次,统计角色实际起跳次数;应为 1。 |
238
+ | 依据 | `PlayerJump.cs` 第 40–46 行 |
239
+ | 核对状态 | 已核对 |
240
+
241
+ **I-2|金币判定用了 `OnTriggerStay2D` 但没有去重**
242
+
243
+ | 要素 | 内容 |
244
+ | --- | --- |
245
+ | 位置 | `Coin.cs` 第 12–16 行(`OnTriggerStay2D`) |
246
+ | 发生机制 | 停留在触发器内的每一帧都会调用 `OnTriggerStay2D`;文件里既没有 `bool` 标记也没有销毁金币,所以加分会被反复执行。 |
247
+ | 实际影响 | 站在金币上一秒会加几十分,分数完全失去意义。对应验收标准第 3 条「只打印一次」。 |
248
+ | 最小修复方向 | 改用 `OnTriggerEnter2D`(重叠开始时只触发一次),并在其中调用 `Destroy(gameObject)` 或置 `bool collected` 做幂等保护。 |
249
+ | 修复后验证方式 | 站在金币位置不动 2 秒,Console 应只出现 1 条日志,分数只 +1。 |
250
+ | 依据 | 学员提交的 `Coin.cs` 第 12–16 行 |
251
+ | 核对状态 | 已核对 |
252
+
253
+ ### 建议
254
+
255
+ **S-1|`jumpForce` 是硬编码的魔法数字**
256
+
257
+ | 要素 | 内容 |
258
+ | --- | --- |
259
+ | 位置 | `PlayerJump.cs` 第 9 行:`private float jumpForce = 9f;` |
260
+ | 发生机制 | 数值写死在代码里,调手感必须改代码并等待重新编译。 |
261
+ | 实际影响 | 不阻塞功能,但会让后续每一次手感微调都变慢。 |
262
+ | 最小修复方向 | 改为 `[SerializeField] private float jumpForce = 9f;`,在 Inspector 里调。 |
263
+ | 修复后验证方式 | 在 Inspector 中把该值由 9 改为 14,跳跃应明显变高,且无需改代码。 |
264
+ | 依据 | `PlayerJump.cs` 第 9 行 |
265
+ | 核对状态 | 已核对 |
266
+
267
+ **S-2|`Update` 里每帧拼接字符串打日志**
268
+
269
+ | 要素 | 内容 |
270
+ | --- | --- |
271
+ | 位置 | `PlayerJump.cs` 第 27 行:`Debug.Log("grounded=" + isGrounded);` |
272
+ | 发生机制 | 字符串拼接在 `Debug.Log` 调用**之前**就完成,每帧产生一个新的 `string`;且该日志在正式包里同样执行。 |
273
+ | 实际影响 | 目前场景简单,看不出问题;但这是后续性能优化时会被反复指出的每帧分配来源(见 `pitfalls.md` P-08)。 |
274
+ | 最小修复方向 | 调试完成后删除,或用条件编译/开关包起来。 |
275
+ | 修复后验证方式 | 打开 Profiler 的 GC Alloc 列,对比删除前后该帧的分配字节数。 |
276
+ | 依据 | `PlayerJump.cs` 第 27 行 |
277
+ | 核对状态 | 已核对 |
278
+
279
+ **处理方式**:以上问题**先由学员自己修**,教练不代为重写。B-1、B-2 给出方向后要求学员说明「为什么改到 `FixedUpdate` 就能让跳跃高度稳定」,确认机制理解后再动手。
280
+
281
+ ---
282
+
283
+ ## 五、验收结论
284
+
285
+ **验收范围**:阶段 1 任务 2「跳跃与着地检测」
286
+ **结论**:**未通过**
287
+
288
+ **理由**
289
+
290
+ 1. 逐项核对 `userOnly`:
291
+ - 「亲手配置 Rigidbody2D 与 Collider2D 并说明理由」— **已完成**(学员在对话中逐项说明了 `Body Type`、`Gravity Scale`、`Collision Detection` 的取值理由)。
292
+ - 「亲手打开 Gizmos 确认碰撞体重叠」— **已完成**(附了 Scene 视图截图,碰撞体轮廓可见且重叠)。
293
+ - 「亲手复现一次触发器不触发并独立定位」— **未开始**,属于任务 3,本轮不适用。
294
+ - 「5 分钟复述 FixedUpdate 与 Update 的分工」— **已完成但表述有偏差**:学员能说出「`FixedUpdate` 是固定时间间隔」,但说不出「为什么帧率不同会导致跳跃高度不同」。已在 `retrievalRecap` 中记为「机制层未到位」。
295
+ 2. 逐项核对验收标准:
296
+ - 第 1 条(移动一致)— **未通过**,因 B-1 未修。
297
+ - 第 2 条(仅地面起跳)— **未通过**,因 B-2 与 I-1 未修。
298
+ - 第 3、4、5 条 — **本轮不适用**(属任务 3、4)。
299
+ 3. 存在 **2 项未清的「阻塞」问题**(B-1、B-2),因此本任务不满足通过条件。
300
+
301
+ **本例为何是「未通过」而不是另外两档**(对照引擎的三档结论判据)
302
+
303
+ - 不是「通过」:要求未获得充分证据支持,且有未清阻塞项。
304
+ - 不是「有条件通过」:核心成果(跳跃行为)本身**不成立**——高度随帧率变、高台上跳不起来;这不是「核心成立但有待修项」,而是核心尚未达标。
305
+ - 判「未通过」不等于否定学员:`userOnly` 前两项已完成,且证据 E-04、E-05 支撑「实际应用」已达 3 级。
306
+
307
+ **`state.retrievalRecap`**
308
+
309
+ > 能说出 `FixedUpdate` 是固定间隔、`Update` 是每渲染帧,但无法解释「为什么帧率变化会导致跳跃高度不同」,也未主动提到「直接改 `transform.position` 会绕过物理」。机制层理解不到位,只记到规则层。
310
+
311
+ **`state.open[]`(未清项)**
312
+
313
+ ```json
314
+ [
315
+ { "id": "O-01", "issue": "跳跃位移写在 Update 并直接改 transform.position(B-1)", "severity": "阻塞", "status": "未解决", "next": "改到 FixedUpdate + Rigidbody2D.linearVelocity,并用 15/60fps 对比验证" },
316
+ { "id": "O-02", "issue": "着地用 transform.position.y <= 0 判断(B-2)", "severity": "阻塞", "status": "未解决", "next": "改用 Physics2D.Raycast / OverlapCircle + 地面 Layer 字段,并在高台与凹地各验一次" },
317
+ { "id": "O-03", "issue": "起跳未清 isGrounded,可二段跳(I-1)", "severity": "重要", "status": "未解决", "next": "起跳帧立即置 false,连按 5 次应只跳 1 次" },
318
+ { "id": "O-04", "issue": "OnTriggerStay2D 未去重,金币会重复加分(I-2)", "severity": "重要", "status": "未解决", "next": "改用 OnTriggerEnter2D + 幂等标记(属任务 3,提前登记)" }
319
+ ]
320
+ ```
321
+
322
+ **`state.current`**
323
+
324
+ ```json
325
+ {
326
+ "stage": 1,
327
+ "stageStatus": "未通过",
328
+ "task": {
329
+ "title": "跳跃与着地检测",
330
+ "deliverable": "可在 15fps 与 60fps 下表现一致的跳跃 + 可在高台起跳",
331
+ "criteria": ["固定帧率下跳跃高度一致", "仅地面可起跳", "高台与凹地均可正常起跳"],
332
+ "limits": ["不引入动画", "不改关卡布局"],
333
+ "nonGoals": ["二段跳", "冲刺"],
334
+ "estimateMin": 90,
335
+ "state": "待修正"
336
+ }
337
+ }
338
+ ```
339
+
340
+ **`nextTask`**
341
+
342
+ > 修 B-1(迁到 `FixedUpdate`)与 B-2(改几何着地检测),两项都需要**自己动手改**,教练不给代码。改完后按 V-c1 的方式把编辑器日志尾部与 15fps / 60fps 两次跳跃的最高点 y 值一起发来,再重新验收。修完这两项后,本任务可判「有条件通过」(I-1、I-2 仍未清),三项全清并复述通过后判「通过」。
343
+
344
+ ---
345
+
346
+ ## 六、本例用到了哪些领域包内容
347
+
348
+ | 环节 | 用到的本包内容 |
349
+ | --- | --- |
350
+ | 目标收敛 | `archetypes.md` 原型 1「2D 玩法原型」的最小可验证成果 4 条,直接改写成 `doneCriteria` |
351
+ | 出题 | `diagnosis-bank.md` 的 Q3-2、Q4-2、Q6-1(覆盖理解预测 / 问题定位 / 小型实现);另按引擎 **R9** 用 Q1-1 作**讲授后确认题**(学员自述没学过生命周期) |
352
+ | 判分 | Q3-2、Q4-2、Q6-1 与 Q1-1 各自的「合格回答要点」与「典型错误回答」;Q1-1 的结论只用于**确认讲授效果**,不作首次能力诊断 |
353
+ | 阶段切分 | `archetypes.md` 原型 1 的阶段 A–C,映射为任务 1–4 |
354
+ | 审阅 | `pitfalls.md` P-09(BUG 对应 B-1)、P-10(推论依据)、P-08(对应 S-2) |
355
+ | 验收证据 | `verification.md` V-c1(读 Editor.log 定位编译错误)、V-d1(无 Unity 时的降级与「未验证」声明) |
356
+ | 术语一致 | `glossary.md`:`FixedUpdate`、`Rigidbody2D`、`Collider2D`、`Is Trigger`、`SerializeField`、`Layer`、Gizmos、GC Alloc |
@@ -0,0 +1,110 @@
1
+ # Unity / C# 术语表(中英对照)
2
+
3
+ > 用途:教练与学员对齐用词。学员说出「中文名」时,教练应能对应到「英文名」,因为 Unity 报错、Inspector 字段名、官方文档都以英文呈现。
4
+ > 「易混点」列只写这一条最容易被误解的地方,不写完整定义。
5
+
6
+ ## A. C# 语言
7
+
8
+ | 中文名 | 英文名 | 一句话说明 | 易混点 |
9
+ | --- | --- | --- | --- |
10
+ | 引用类型 | reference type | 变量存的是对象地址,赋值只复制地址 | 与值类型混淆:`Vector3 a = b;` 改 `a` 不影响 `b` |
11
+ | 值类型 | value type | 变量直接存数据本身,赋值复制整份数据 | `struct` 默认是值类型,`class` 是引用类型 |
12
+ | 装箱 | boxing | 把值类型转成 `object` 时会新建堆对象 | 每次装箱都产生垃圾,是隐藏的 GC 来源 |
13
+ | 拆箱 | unboxing | 把装箱后的 `object` 转回值类型 | 类型不符会抛 `InvalidCastException` |
14
+ | 垃圾回收 | Garbage Collection (GC) | 运行时自动回收不再使用的托管堆内存 | Unity 用的是 Boehm 式非分代 GC,一次回收容易造成卡顿 |
15
+ | 托管堆 | managed heap | C# 对象所在、由 GC 管理的内存区域 | 与 Unity 的原生内存(贴图、Mesh)是两套账 |
16
+ | 属性 | property | 带 `get`/`set` 的成员,语法像字段 | 属性不能被 `[SerializeField]` 直接序列化 |
17
+ | 自动属性 | auto-property | 编译器自动生成后备字段的属性 | `public int Hp { get; set; }` 不会出现在 Inspector |
18
+ | 接口 | interface | 只声明契约、不含实现的类型 | Unity 旧版不支持在 Inspector 里直接拖接口引用 |
19
+ | 委托 | delegate | 指向方法的类型,可当参数传递 | 与 `event` 关键字不是一回事 |
20
+ | 事件 | event | 基于委托、只允许 `+=`/`-=` 的成员 | 外部无法直接调用或清空事件 |
21
+ | 匿名方法 | anonymous method | 用 `delegate {}` 或 `=>` 就地写的方法体 | 闭包会捕获外部变量,导致对象无法释放 |
22
+ | 闭包 | closure | lambda 捕获了外部局部变量后形成的引用 | 循环里捕获循环变量是经典错误 |
23
+ | 装箱集合 | non-generic collection | `ArrayList`、`Hashtable` 等 `System.Collections` 类型 | 新代码一律用 `List<T>`、`Dictionary<K,V>` |
24
+ | 泛型 | generics | 用类型参数写一份代码适配多种类型 | 泛型集合通常不装箱 |
25
+ | 可空类型 | nullable type | `int?` / `float?`,表示「可能没有值」 | `Vector3?` 与 `Vector3` 是不同类型 |
26
+ | 结构体 | struct | 值类型的数据容器,常用于小型数据 | `struct` 方法内改字段不会改到调用者的副本 |
27
+ | 静态成员 | static member | 属于类型而非实例,全局只有一份 | 静态字段跨场景重载不会自动重置 |
28
+ | 分部类 | partial class | 一个类拆到多个文件声明 | Unity 生成的代码常用它,手写代码别重复声明 |
29
+ | 只读字段 | readonly field | 只能在声明处或构造函数里赋值 | `readonly` 不等于「引用内容不可变」 |
30
+ | 常量 | const | 编译期常量,只能用于基本类型和字符串 | `const` 会内联进调用方程序集,改值需重新编译所有程序集 |
31
+ | 静态只读 | static readonly | 运行期初始化一次,可用于复杂类型 | 初始化时机不确定,别依赖它做跨脚本初始化 |
32
+ | 命名空间 | namespace | 类型的逻辑分组,避免重名 | 不会自动成为 `using`,跨程序集还要引 asmdef |
33
+ | 特性 | attribute | 附加在类型/成员上的元数据标记 | `[SerializeField]`、`[Test]`、`[RequireComponent]` 都是特性 |
34
+ | 扩展方法 | extension method | 在静态类里为已有类型「加」方法 | 必须写在非泛型静态类里,第一个参数带 `this` |
35
+ | 异步方法 | async method | 用 `async`/`await` 写、由状态机驱动的方法 | 与协程是两套机制,不能互相 `yield` |
36
+ | 任务 | Task | .NET 的异步操作抽象 | `Task` 的续体默认不保证回到 Unity 主线程 |
37
+ | 可枚举 | IEnumerable | 能被 `foreach` 遍历的对象 | 协程里 `yield return someEnumerable` 的语义和普通 `foreach` 不同 |
38
+
39
+ ## B. Unity 运行时
40
+
41
+ | 中文名 | 英文名 | 一句话说明 | 易混点 |
42
+ | --- | --- | --- | --- |
43
+ | 游戏对象 | GameObject | 场景里的容器实体,本身不含行为 | GameObject 的「启用」和组件的「启用」是两级开关 |
44
+ | 组件 | Component | 挂在 GameObject 上提供功能 | 所有组件都继承自 `Component` |
45
+ | 行为脚本 | MonoBehaviour | 可挂载、有生命周期回调的脚本基类 | 不继承它就进不了 Inspector、收不到 `Update` |
46
+ | 变换 | Transform | 位置/旋转/缩放组件,每个 GameObject 必有 | 3D 用 `Transform`,2D 也用它但只看 XY |
47
+ | 刚体 | Rigidbody | 让对象受物理引擎驱动 | 2D 用 `Rigidbody2D`,强行混用不生效 |
48
+ | 碰撞体 | Collider | 定义物理外形 | 触发器也要碰撞体,只把 `isTrigger` 打勾 |
49
+ | 触发器 | Trigger | 只检测「进入/停留/离开」不做物理反弹 | 触发需要至少一方带刚体 |
50
+ | 预制体 | Prefab | 可复用的 GameObject 模板 | 实例上的改动要 Apply 才回写模板 |
51
+ | 实例化 | Instantiate | 运行时从预制体/对象复制出新实例 | 频繁调用是性能与 GC 大坑 |
52
+ | 销毁 | Destroy | 延迟到帧末真正删除对象 | 销毁后同一帧内引用仍「非空」但在 Unity 里等同已死 |
53
+ | 立即销毁 | DestroyImmediate | 立刻删除对象 | 官方不建议在运行时用,容易破坏遍历与引用 |
54
+ | 生命周期回调 | lifecycle callback / message | Unity 按固定顺序反射调用的方法 | 顺序不随脚本挂载顺序或代码书写顺序变化 |
55
+ | 启用回调 | OnEnable | 组件被启用时调用,可能多次触发 | 与 `Start` 不同:禁用再启用会再调一次 |
56
+ | 物理帧 | FixedUpdate | 固定时间步长调用,物理相关逻辑写这里 | 与 `Update` 帧率不同步,别在这里读输入 |
57
+ | 逐帧更新 | Update | 每渲染帧调用一次 | 帧率越高调用越频繁,逻辑要乘 `deltaTime` |
58
+ | 迟帧更新 | LateUpdate | 所有 `Update` 之后调用 | 相机跟随应写在这里,避免抖动 |
59
+ | 帧间隔 | deltaTime | 上一帧到这一帧的秒数 | `FixedUpdate` 里应使用 `fixedDeltaTime` |
60
+ | 时间缩放 | Time.timeScale | 全局时间倍率,`0` 即暂停 | 设为 0 后 `Update` 仍执行,`FixedUpdate` 停止 |
61
+ | 协程 | Coroutine | 用 `IEnumerator` + `yield` 写的分帧流程 | 由挂载它的 MonoBehaviour 驱动,组件禁用即失效 |
62
+ | 等待指令 | yield instruction | `yield return` 后面跟的等待对象 | `WaitForSeconds` 受 `timeScale` 影响,`WaitForSecondsRealtime` 不受 |
63
+ | 脚本化对象 | ScriptableObject | 可当资源保存的数据容器,适合放配置 | 它是资源不是场景对象,不能用 `Instantiate` 当运行时实例 |
64
+ | 单例 | singleton | 全局唯一访问点 | `static Instance` 跨场景不重置,是常见脏状态来源 |
65
+ | 对象池 | object pool | 复用对象、避免频繁创建销毁 | 取出的对象要重置状态,否则带回上次的脏数据 |
66
+ | 射线检测 | Raycast | 从一点沿方向探测碰撞体 | 2D 必须用 `Physics2D.Raycast`,不通用 |
67
+ | 刚体插值 | interpolation | 在物理步之间平滑渲染位置 | 不解决逻辑抖动,只改善视觉 |
68
+ | 主线程 | main thread | 唯一能安全访问 Unity API 的线程 | 子线程调 Unity API 会抛异常或崩溃 |
69
+ | 时间步长 | fixedDeltaTime | 物理帧的固定间隔 | 改它会影响所有物理行为的手感 |
70
+
71
+ ## C. Unity 编辑器
72
+
73
+ | 中文名 | 英文名 | 一句话说明 | 易混点 |
74
+ | --- | --- | --- | --- |
75
+ | 检视窗口 | Inspector | 显示并编辑选中对象/资源的字段 | 只显示可序列化字段 |
76
+ | 序列化 | serialization | 把字段值保存进场景/预制体/资源文件 | `public` 字段默认序列化,`private` 需要 `[SerializeField]` |
77
+ | 序列化字段 | SerializeField | 让私有字段出现在 Inspector 并被保存 | 加了这个特性字段仍不是 `public` |
78
+ | 隐藏字段 | HideInInspector | 让公开字段不显示但仍被保存 | 与 `[NonSerialized]` 完全不同,后者不保存 |
79
+ | 不可序列化 | NonSerialized | 标记字段完全不参与序列化 | 运行时赋值在重载后会丢失 |
80
+ | 自定义编辑器 | custom Editor / EditorWindow | 扩展 Inspector 或新建编辑器窗口 | 编辑器代码必须放在 `Editor` 文件夹或编辑器程序集 |
81
+ | 资产数据库 | AssetDatabase | 编辑器侧访问/刷新资源的 API | 只能在编辑器里用 |
82
+ | 菜单项 | MenuItem | 给编辑器菜单加自定义入口 | 同样属于编辑器专用 API |
83
+ | 控制台 | Console | 显示日志、警告、错误的窗口 | `Debug.Log` 在正式包里也会执行 |
84
+ | 播放模式 | Play Mode | 编辑器内运行游戏的模式 | Play 模式下的改动默认不保存回资源 |
85
+ | 暂停 | pause | 冻结编辑器更新 | 暂停时 `Update` 不再执行,协程也停 |
86
+ | 帧调试器 | Frame Debugger | 逐 DrawCall 查看渲染过程 | 与 Profiler 分工不同 |
87
+ | 性能分析器 | Profiler | 采样 CPU/GPU/内存开销 | 连接真机分析才有意义,编辑器数据会偏高 |
88
+ | 域重载 | Domain Reload | 进入 Play 模式前重新加载脚本域 | 关闭它会让静态字段不再自动重置 |
89
+ | 资源导入设置 | import settings | 每种资源各自的导入参数 | 2D 项目常要手动改贴图的 Pixels Per Unit 与 Filter Mode |
90
+
91
+ ## D. 构建与程序集
92
+
93
+ | 中文名 | 英文名 | 一句话说明 | 易混点 |
94
+ | --- | --- | --- | --- |
95
+ | 程序集 | assembly | 编译产物的最小单元(`.dll`) | 它决定了「哪些代码能看见哪些代码」 |
96
+ | 程序集定义 | assembly definition / asmdef | 用 `.asmdef` 文件把脚本划分成独立程序集 | 划分后默认看不见其它程序集的类型 |
97
+ | 预定义程序集 | predefined assembly | `Assembly-CSharp`、`Assembly-CSharp-Editor` 等 | 所有没被 asmdef 覆盖的脚本都进 `Assembly-CSharp` |
98
+ | 程序集引用 | assembly reference | 在 asmdef 里声明依赖的其它程序集 | 循环引用会被 Unity 拒绝 |
99
+ | 编辑器程序集 | editor assembly | 只在编辑器编译、能访问 UnityEditor API | 平台设置里没排除 Editor 就会打包报错 |
100
+ | 脚本宏定义 | scripting define symbol | 按平台/配置条件编译的符号 | 写错符号名不会有任何提示,代码静默不编译 |
101
+ | 条件编译 | conditional compilation | `#if UNITY_EDITOR` 等预处理分支 | 分支写错会导致「编辑器里好、打包就挂」 |
102
+ | 构建目标 | Build Target | 目标平台(Windows/Android/WebGL…) | 与「运行平台」不是同一个概念 |
103
+ | 脚本后端 | scripting backend | Mono 或 IL2CPP | IL2CPP 会做代码剥离,反射用法容易被打掉 |
104
+ | 托管代码剥离 | managed stripping | 构建时移除未引用的代码 | 只靠反射调用的代码可能被误删 |
105
+ | 链接文件 | link.xml | 告诉剥离器保留指定类型 | 只在 IL2CPP/剥离开启时起作用 |
106
+ | 播放器日志 | Player.log | 打包后运行时的日志文件 | 与编辑器 `Editor.log` 路径不同 |
107
+ | 批处理模式 | batch mode | 无界面、无人工交互地运行编辑器 | 出异常会直接以返回码 1 退出 |
108
+ | 测试程序集 | test assembly | 引用了 NUnit 的程序集,测试必须放在里面 | 少了 `UnityEngine.TestRunner` 引用就不会被识别 |
109
+ | 编辑模式测试 | Edit mode test | 不在 Play 模式下运行的测试 | 无法测试依赖帧循环的行为 |
110
+ | 播放模式测试 | Play mode test | 在 Play 模式下运行、可跨帧的测试 | 跨帧测试需要 `[UnityTest]` 与 `yield` |
@@ -0,0 +1,14 @@
1
+ id: unity-csharp
2
+ name: Unity / C# 开发
3
+ version: 1.1.0
4
+ engine: ">=1.1.0"
5
+ locale: zh-CN
6
+ sections:
7
+ archetypes: archetypes.md
8
+ diagnosis: diagnosis-bank.md
9
+ verification: verification.md
10
+ pitfalls: pitfalls.md
11
+ example: example.md
12
+ glossary: glossary.md
13
+ taskMinutes: [30, 90]
14
+ notes: 适用于能打开 Unity 工程、手写 C# 脚本的初学到中级学员;覆盖 MonoBehaviour 脚本、编辑器工具与小型联网玩法,不适用于纯 DOTS/ECS、Shader 图形学、原生插件与大规模商业项目架构评审。