---
课时: 9
主题: 涂鸦PK（二）— 基础对战系统
核心能力: [拆解力, 韧性力]
核心工具: [Trae IDE, Kimi]
时长: 90分钟
透明化层级: 过程层
适用路线: AICODE-06
---

### 1. 课程目标

**知识目标：**
- 理解「边界情况」的概念：任何系统都有正常路径以外的边缘场景，这些场景必须在需求里明确定义，否则 AI 的行为不可预测
- 理解「独立窗口审核」原则：需求文档、边界审核、代码生成、测试验证各用独立会话，防止 AI 自我偏袒
- 理解「测试脚本」的价值：用代码来验证代码，比人工点击效率高十倍，且可重复执行

**能力目标：**
- 能用 Plan Mode 整理战斗系统需求文档，并用窗口 B 发现边界情况（拆解力）
- 能把自己的 Spritesheet 导入对战系统，完成逐条验收，遇到 bug 能描述「步骤→预期→实际」（韧性力）
- 能让 AI 生成测试脚本，读懂「✅通过/❌失败」的输出，并定位到需求文档中对应的条款

**情感目标：**
- 建立「测试不是挑刺，是保护」的意识——有 bug 的游戏比没有游戏更让人沮丧，测试帮你在发布前修好它
- 体验「让 AI 测试 AI 写的代码」的效率感，而不是自己一条条人工验证
- 对「今天的角色真的打起来了」产生真实的成就感，为第10课加动画做好期待

---

### 2. 核心概念与误概念预设

**核心概念认知层级：**

| 概念 | 学生类比 | 认知层级 |
|------|---------|---------|
| 边界情况 | 游戏规则书上写了「普通攻击」怎么算，但没写「两个人同时出手」怎么判——比赛现场这种情况真出现了，裁判不知道怎么判，整场比赛乱掉 | 理解层 |
| 独立窗口审核（新窗口原则） | 作文让同学评改：写作文的同学自己改，永远改不出大问题；换一个没看过你作文的同学来改，他会发现真正的漏洞 | 应用层 |
| 测试脚本 | 乐高玩具的「质检机器人」——每次按一个按钮，它自动把所有零件逐一检查一遍，告诉你哪个零件不对；比你自己一个个用眼睛看快一百倍，而且不会看漏 | 应用层 |
| 伤害公式 | 攻击力是出拳力气，防御力是护甲厚度，伤害 = 出拳力气 − 护甲厚度，最少也会有 1 点伤害（护甲再厚也会有点疼） | 识别层 |
| 先手机制 | 速度值高的人先出手，就像运动会百米跑步，反应快的人先起跑——如果两人反应时间一样，就抛硬币决定 | 识别层 |

**典型误概念表：**

| 编号 | 误概念 | 正确认知 | 激发策略 |
|------|--------|---------|---------|
| M1 | 游戏能跑就行，不用测试 | 有 bug 的游戏比没有游戏更难受；测试发现的 bug 才是可修复的 bug | 问：「你有没有玩过一个游戏，有个 bug 特别烦但一直不修？你是什么感受？」 |
| M2 | 测试脚本要自己写，太难了 | 测试脚本也可以让 AI 写，你只需要描述「我要验证什么结果」 | 演示：把「验证 ATK=15, DEF=5 时伤害=10」告诉 AI，它直接给出测试代码 |
| M3 | 同速的情况 AI 会自动处理好 | 边界情况必须在需求里明确，否则 AI 行为不可预测（可能每次随机，也可能直接报错） | 问：「如果两个角色速度一样，谁先出手？你需求里写了吗？」 |
| M4 | 伤害公式在脑子里，不用写进需求 | 代码里的公式必须和需求文档一致，否则测试脚本无法验证；口头约定不算 | 演示：需求里写「ATK 减 DEF」，代码里却写成「ATK 乘以 DEF」，逻辑完全不同 |
| M5 | 格挡和攻击同帧不会有问题 | 行动顺序是战斗公平性的核心；「同时出手」必须在需求里明确谁的优先级高 | 类比：石头剪刀布如果两人同时出，规则说谁赢？——必须有一个规则，不能靠 AI 猜 |

---

### 3. 教学准备

**工具与环境：**
- 每台电脑已安装 Trae IDE，Kimi 已登录，网络正常
- 每台电脑浏览器可以正常打开本地 HTML 文件
- 每台电脑上已有上节课完成的 Spritesheet PNG 文件（128×64，帧1待机+帧2攻击）
- 每台电脑上已有上节课完成的角色属性 JSON（hp/atk/def/spd/skill 字段，20分预算制）
- 投影可切换至任意学生屏幕

**教学资源：**
- 教师准备：demo-2-pk-battle.html（可直接运行的战斗演示，开课前准备好，用于 Connect 展示和 Contemplate 演示）
- 教师准备：战斗需求文档范例（见第5节，完整版，供学生卡壳时参考）
- 教师准备：测试脚本的预期输出截图（4条全部 ✅ 通过的版本，用于对比）
- 学生资源：上节课完成的 Spritesheet PNG 和角色属性 JSON（若有学生未完成，教师提供默认模板）

**教师备课体验任务：**
> 备课前，教师必须亲自完成以下操作：
>
> 1. 打开 demo-2-pk-battle.html，完整打一局，记录下「格挡+重击同回合」时伤害是怎么算的，以及「特技用完后再点特技按钮」的实际行为
> 2. 在窗口 A 整理一份战斗需求文档，然后在全新窗口 B 用审核提示词跑一遍，记录 AI 问出来的边界问题里哪几条最让你意外
> 3. 在窗口 D 生成测试脚本，验证伤害公式和先手机制，确认本课所有测试用例都能通过
> 4. 故意把伤害公式改成「ATK × DEF」，重跑测试脚本，截图「❌失败」的输出，备课时展示用

---

### 4. 教学流程

---

**第一幕：联系 (Connect) — 10分钟** 🔗

*本幕目标：让学生展示上节课做的角色，说出打法定位和取胜策略，激活策略意识；教师演示可以运行的对战系统，建立「今天目标」的画面感*

**【环节】角色展示 + 打法定位 (7分钟)**

**师：** 上节课结束前，每个人都交了两样东西：一个 Spritesheet PNG，一份角色属性 JSON。今天我们用这两样东西做什么？

**生：** （预期：做游戏？让它打起来？）

**师：** 对。上节课大家做了什么，记得吗？

**生：** （预期：画了自己的角色 / 设计了角色属性）

**师：** 对。你们每个人手里有一个属于自己的角色：有自己画的图，有自己定的属性，还有自己选的特技。今天不是来欣赏它的——今天要让它真的打起来。

**师：** 在打之前，我先问每个人一个问题。谁来第一个回答——你的角色是什么打法定位？为什么你的角色应该赢？

【点一个学生，把他的 Spritesheet 投屏展示】

**生：** （预期 A：「我是高攻击型，我的 ATK 是 16，我可以直接打穿对面」）

**生：** （预期 B：「我是高防御型，我血多，耗死对手」）

**生：** （预期 C：「我速度最快，我先手，每次都比对面早打」）

**师：** 很好。你说的「先手」——如果两个角色速度值一样怎么办？

**生：** （预期：不知道 / 随机？/ 谁编号小谁先？）

**师：** 这就是我们今天第一件事要想清楚的——你脑子里的规则，必须写成文字，AI 才能按你想的来做。

【诊断点：学生能否意识到「打法逻辑」和「代码规则」之间有一层需要翻译的过程】【识别层】

**【分支A】若学生对打法定位有清晰的想法：**
**师：** 很好，待会儿写需求文档的时候，你的打法定位就是你的出发点——你的角色是高攻击，那你的伤害公式、重击倍率、特技效果就都要围绕「攻击压制」来设计。

**【分支B】若学生说「我随便，什么定位都行」：**
**师：** 没关系，我们来帮你定位。你的 ATK 最高，还是 DEF 最高，还是 SPD 最高？
（引导学生从属性数值反推定位，而不是空想）

**【环节】展示今天目标 (3分钟)**

**师：** 我先给大家看一个东西。

【投屏打开 demo-2-pk-battle.html，运行一局对战】

**师：** 你们看——左边是玩家角色，右边是 AI 对手，有血条，有行动按钮，有战斗日志。这就是今天下课前你们要做到的东西。

**师：** 但注意——你们做出来的版本，角色是你们自己画的，属性是你们自己定的。你的角色放在左边，跟 AI 对手打。今天不做动画，角色还是用矩形占位，但战斗逻辑是真实的。下节课我们把图换上去、加动画。

**师：** 今天的核心是：战斗规则要真的是你想的规则，而不是 AI 随机猜的规则。怎么保证？用 Plan Mode。

**师：** 今天我们会开 4 个窗口——窗口 A 写需求、窗口 B 审核边界、窗口 C 生成代码、窗口 D 做测试。这 4 个窗口，每个只做一件事，互相不干扰。记住这个结构，以后你做任何大一点的项目都可以用。

---

**第二幕：建构 (Construct) — 65分钟** 🛠️

*本幕目标：走完「需求文档→边界审核→代码生成→导入 Spritesheet→验收→测试脚本验证」完整闭环，强化多窗口协作和边界意识*

---

**【分段一：Plan Mode — 战斗系统需求文档】(20分钟)**

*本段重点：学生在窗口 A 整理战斗系统需求，在窗口 B 用「边界审核」提示词发现漏洞，补充进文档*

**预设误概念：**
- 误概念 M3：同速情况 AI 会自动处理好，不用写
- 误概念 M4：伤害公式在脑子里不用写进需求文档
- 误概念 M5：格挡和攻击同帧不会有问题

**讲解与演示 (Teach & Demo): (7分钟)**

**师：** 我们先开窗口 A——这个窗口只做一件事：整理需求文档。

**师：** 今天的需求文档比俄罗斯方块简单一点，但有一个新的挑战——这个系统里有很多「边界情况」。什么叫边界情况？就是不在「正常流程」里，但真实对战时会出现的场景。

**师：** 我给你们举几个例子。正常流程是什么？玩家点攻击，AI 点攻击，互相扣血，谁血量归零谁输。这是主路径，好想，好写。

**师：** 但是——如果两个角色速度值完全相同，谁先出手？这条你有没有想过？

**生：** （预期：没想过 / 随机？）

**师：** 还有——特技每场只能用一次。如果玩家特技已经用完了，再点「特技」按钮，游戏会怎样？按钮灰掉不能点？还是提示「已用完」？还是直接变成普通攻击？

**生：** （预期：好像要变灰 / 要提示吧）

**师：** 对。还有——格挡时受到的伤害减少 50%，那对方用的是重击（伤害×1.8），格挡减伤是基于重击后的伤害算，还是基于原始伤害算？

**生：** （预期：有区别吗……？哦，区别很大！）

**师：** 这些都是边界情况。这些情况如果你没有在需求里写清楚，AI 会自己猜一个——可能猜得跟你想的不一样，可能每次结果还不同，可能直接崩掉。

**师：** 所以今天需求文档要写两轮。第一轮是窗口 A：把主路径写出来。第二轮是窗口 B：让 AI 扮演审核工程师，专门找你没考虑到的边界情况。

【投屏展示战斗需求文档的五个核心模块】

**师：** 战斗系统需求文档有五个必填模块。我过一遍每个的关键要点——

**师：** 模块一：先手机制。你必须写清楚两条：第一，SPD 高的先出手——这好理解；第二，**同速时怎么处理**——你必须选一个方案，写进文档。你可以写「同速时随机决定」，也可以写「同速时玩家先手」，但必须选一个，不能空着。

**师：** 模块二：伤害公式。写「ATK 减 DEF，最低造成 1 点伤害」。注意「最低 1 点」这个细节必须写——如果不写，当防御值大于攻击值时，AI 可能算出负数伤害，变成治疗对手了。

**师：** 模块三：行动类型。四种：普通攻击、重击（伤害×1.8）、格挡（本回合受伤减少 50%）、特技（每场只能用一次，效果来自角色 JSON 的 skill 字段）。

**师：** 模块四：胜负判定。HP≤0 立即判输，游戏结束。

**师：** 模块五：AI 对手决策逻辑。AI 对手不是乱打的——写清楚它的决策规则：HP 低于 30% 时用特技；25% 概率重击；否则普通攻击。

**师：** 我来快速演示一下这五个模块写出来是什么样的。

【投屏展示教师自己的需求文档草稿，只展示模块一和模块二，让学生对格式有直观感受，不逐字念，只指出关键字段】

**师：** 你们看，模块一的「同速处理」我写了「同速时随机决定，Math.random() < 0.5 为玩家先手」。你们不需要写代码，但要写清楚「同速时谁先」——随机、或者固定玩家先手，选一个写进去。

**师：** 模块二里，我特别标了「最低 1 点」并且加了验收标准：「当 ATK=3, DEF=8 时，伤害=1 而不是负数」。这就是一条可测试的需求——能写出具体数字的需求，才是真正写清楚了的需求。

**师：** 这五个模块写完，是你的「主路径」。然后我们开窗口 B，让 AI 来挑刺。

**学生实践 (Practice): (10分钟)**

**师：** 现在打开 Kimi，开一个新对话，这是窗口 A。用我刚才说的五个模块，把你的战斗系统需求文档写出来。不确定的地方先写一个版本，待会儿窗口 B 会帮你发现问题。

> 教师走动观察重点：
> - 是否有学生跳过「同速处理」这条？走过去问：「你这里写了 SPD 高的先手，那两人 SPD 一样怎么办？」
> - 是否有学生漏写「最低 1 点伤害」？问：「如果对手 DEF 比你 ATK 还高，伤害是负数还是零？你想要哪个？」
> - 是否有学生把 AI 对手的决策逻辑写得很复杂（超过 3 条）？提醒：「AI 对手逻辑先简单，战斗规则才是重点」

**进度同步 (Checkpoint): (3分钟)**

**师：** 五个模块都填了的举手。

**师：** 好，现在开窗口 B——新开一个 Kimi 对话，把你写好的需求文档整个复制过去，然后加上这句话：「你是一个游戏测试工程师，请列出这份需求文档里所有的边界情况和异常情况，只列问题，不需要解答。」提交。

【诊断点：学生是否能把「用窗口 B 挑需求漏洞」理解为「需求还没写完」，而不是「可以不管这些问题」】【理解层】

**【分支A】若学生窗口 B 得到了边界问题列表：**
**师：** 好，先把这些问题读一遍。你觉得哪条最重要——就是如果不处理，对战时一定会出问题的那条？
（让学生自己判断优先级，而不是逐条补充所有问题）

**【分支B】若学生说「AI 没问出什么有用的问题」：**
**师：** 我来帮你加一条——「同速时谁先出手」这条你文档里怎么写的？
（从教师预设的边界情况里选一条，帮学生发现漏洞）

**【分支C】若学生觉得「这些边界情况不重要，先做再说」：**
**师：** 好，我们来做个实验——你先做，遇到边界情况的时候我们再说。
（让学生先继续，在分段二验收时用真实 bug 来说明边界问题的后果）

**师：** 窗口 B 出来的问题，不需要全部解决——先挑最重要的 3 条，补充到你的需求文档里。什么叫「最重要」？就是如果不写清楚，对战时一定会出现的场景。「同速先手」、「特技用完后再点」、「格挡+重击叠加」——这三条必须在文档里有明确答案。

**师：** 好，5分钟，把窗口 B 给你的问题里最关键的几条回答了，补到需求文档里。

> 教师走动：重点帮学生判断哪条边界问题「必须解决」。判断标准——对战时能触发的频率高不高？触发了但没处理会崩游戏还是只是显示不对？优先处理会崩游戏的。

---

**【分段二：生成 PK 系统 → 导入 Spritesheet → 逐条验收】(25分钟)**

*本段重点：在窗口 C 生成战斗系统骨架，替换为自己的角色数据，验收核心功能*

**预设误概念：**
- 误概念 M1：能跑就行，不用验收每一条
- 误概念 M4：遇到 bug 直接让 AI 改代码，不回需求文档溯源

**讲解与演示 (Teach & Demo): (5分钟)**

**师：** 需求文档确认好了，进入执行阶段。打开 Trae IDE，新开一个文件，命名为 pk-battle.html。

**师：** 这次生成分两步走。**第一步**：先生成「带占位图的骨架」——角色先用彩色矩形代替，重点让战斗逻辑跑通。**第二步**：骨架验收通过后，再把你的 Spritesheet 换进去。

**师：** 为什么要分两步？如果你一开始就把 Spritesheet 传进去，战斗逻辑有 bug 的话，你不知道是逻辑问题还是图片问题——两个问题混在一起，很难调试。先把逻辑跑通，再换图，问题清晰。

**师：** 现在开窗口 C——这个窗口专门做代码生成。把你的需求文档复制进去，加上保底提示词，提交。

【投屏展示学生保底提示词（见第5节），教师演示提交过程，展示骨架生成结果】

**师：** 生成出来之后，把代码复制到 Trae IDE 里的 pk-battle.html，保存，用浏览器打开，开始验收。

**师：** 验收怎么做？还是我们之前说的三步——「步骤→预期→实际」。你做了一个操作，你期望看到什么，实际看到了什么。如果期望和实际不一样，记下来，这就是一个 bug。

**师：** 验收清单，我们一起来定：

```
验收清单（至少验这 5 条）：
1. 普通攻击 → 对手 HP 减少，减少量 = ATK - DEF（最低 1）
2. 格挡 → 下一次受到伤害减少 50%
3. 重击 → 伤害比普通攻击×1.8
4. 特技按钮 → 用完一次后变灰或显示「已用」
5. HP≤0 → 游戏结束，显示胜负
```

**学生实践 (Practice): (17分钟)**

第一步（8分钟）：用保底提示词在窗口 C 生成战斗骨架，复制到 Trae IDE，浏览器打开
第二步（4分钟）：按验收清单逐条点击验收，记录「通过/不通过」
第三步（5分钟）：把验收通过的骨架里的角色数据，替换为自己的角色属性 JSON；把矩形占位符的颜色改成自己角色的代表色

> 教师走动观察重点：
> - 学生是否在逐条验收，还是点一下「感觉可以」就算通过？走过去问：「第 3 条重击倍率你验了吗？怎么确认它是 1.8 倍？」
> - 学生遇到 bug 时，是直接跟 AI 说「帮我修复」，还是先回需求文档确认这条需求怎么写的？
> - 有学生进展很快的：引导他多做一步——把 Spritesheet 也导入，用 `drawImage` 替换矩形，不需要动画，只是让图片显示出来

**进度同步 (Checkpoint): (3分钟)**

**师：** 谁来说一条验收结果——哪条通过了，哪条没通过？

**生：** （预期 A：「普通攻击通过了，格挡没通过，格挡好像没有减伤」）

**生：** （预期 B：「特技用完之后按钮没变灰，还能继续按」）

**师：** 格挡没有减伤——这个 bug 我们用「步骤→预期→实际」来描述。步骤是什么？

**生：** 点格挡，然后对面攻击我。

**师：** 预期是什么？

**生：** 受到的伤害应该是正常伤害的 50%。

**师：** 实际是什么？

**生：** 受到的伤害跟没格挡一样。

**师：** 对。这个描述发给 AI，加上一句「请检查需求文档里的格挡逻辑，修复这个 bug」。注意——不是说「帮我改代码」，是「检查需求文档对应的逻辑」，让 AI 溯源。

【诊断点：学生是否会用「步骤→预期→实际」三要素描述 bug，而不是只说「格挡不对」】【应用层】

**【分支A】若学生能准确描述 bug 三要素：**
**师：** 这就是专业的 bug 报告。真正的软件工程师报 bug 就是这个格式，你今天已经在用了。

**【分支B】若学生描述模糊（「格挡感觉不太对」）：**
**师：** 「感觉不对」AI 不知道从哪里改。你告诉我——你点了格挡之后，对面攻击你，你的 HP 掉了多少？你预期应该掉多少？
（帮学生把感受翻译成具体数字）

---

**【分段三：窗口 D 生成测试脚本 → 验证核心逻辑】(15分钟)**

*本段重点：在独立窗口 D 生成测试脚本，运行 4 条测试用例，读懂「✅通过/❌失败」输出，体验「让 AI 测试 AI」的效率*

**预设误概念：**
- 误概念 M2：测试脚本要自己写，太难了，我不会代码
- 误概念 M1：能跑就行，不需要写测试

**讲解与演示 (Teach & Demo): (5分钟)**

**师：** 你们刚才用手工逐条验收——点按钮，看结果。这样做是对的，但有一个问题：下次你改了一段代码，你要把所有条目再点一遍吗？

**生：** （预期：要的 / 好累 / 不想点）

**师：** 有一个更高效的方法——**测试脚本**。你告诉 AI「我要验证哪些规则，预期结果是什么」，AI 写一个 HTML 文件，你一打开，它自动跑完所有验证，每条显示「✅通过」或者「❌失败+原因」。

**师：** 这叫「用代码测试代码」。以后你每次改完代码，打开测试脚本跑一遍，5 秒看完所有结果，知道改了哪里有没有破坏原来的逻辑。

**师：** 注意——测试脚本必须在全新窗口 D 生成。为什么？因为如果你在窗口 C 生成测试脚本，AI 知道自己刚刚写了什么代码，它的测试会偏向「帮自己的代码辩护」——故意写成能通过的测试，而不是真正验证逻辑是否正确。

**师：** 独立窗口 D，让一个「什么都不知道」的 AI 来写测试，它才会真的按需求文档来验证。

【投屏展示窗口 D 测试脚本提示词（见第5节），演示把需求文档粘贴进去，提交】

【投屏展示测试脚本的运行结果：4条全部✅，效果直观】

**师：** 这 4 条测试验证了什么——

```
测试 1：ATK=15, DEF=5 → 伤害=10       ✅
测试 2：ATK=5, DEF=10 → 伤害=1（最低）  ✅
测试 3：SPD=8 vs SPD=5 → SPD=8 先手   ✅
测试 4：ATK=10, DEF=2, 重击 → 伤害=14  ✅
```

**师：** 现在我把伤害公式故意改错——把「ATK - DEF」改成「ATK × DEF」，再跑一遍。

【演示：故意修改代码，重跑测试脚本，出现 ❌ 失败】

**师：** 你们看——测试脚本立刻告诉我「测试1失败：期望10，实际75」。这就是测试脚本的价值：改完代码立刻能发现问题，不用自己重新点一遍。

**学生实践 (Practice): (8分钟)**

**师：** 现在你们来做。打开全新窗口 D，用测试脚本提示词，把你的需求文档粘贴进去，生成测试脚本，保存为 pk-test.html，浏览器打开，看结果。

> 教师走动观察重点：
> - 是否有学生在窗口 C 生成测试脚本（违反独立窗口原则）？立刻叫停，强调「必须是窗口 D，全新对话」
> - 学生的测试结果有 ❌ 吗？引导他们找对应的需求条款，而不是直接改代码
> - 进度快的学生：引导拓展任务——「让 AI 帮你写一条需求里没有的边界测试，看是否通过」

**进度同步 (Checkpoint): (2分钟)**

**师：** 谁的 4 条测试全部通过的举手。

**师：** 有没有哪条测试失败了的？能说一下是哪条，失败的原因是什么？

**生：** （预期：「测试3失败了，说期望8先手但实际是5先手」）

**师：** 这就是测试脚本的价值——它告诉你具体哪里不对。现在打开你的需求文档，找「先手机制」那条，看看写的是什么，再对照你生成的代码，找差异。这才是正确的调试方式。

【诊断点：学生是否能从「❌失败」的输出里，找到对应的需求文档条款进行溯源】【应用层】

**【分支A】若学生能自己完成溯源：**
**师：** 你刚才做的就是「测试驱动调试」——测试报告告诉你哪里不对，你去需求里找根因，然后修代码。这是专业开发者的工作方式。

**【分支B】若学生看到 ❌ 直接去改代码而不溯源：**
**师：** 等等——你要改什么？测试说失败，但测试是按需求文档验证的。先问：你的需求文档里这条是怎么写的？再问：代码里是不是按这条写的？最后才改代码。

**【分支C】若学生的测试脚本全部通过但游戏实际有 bug：**
**师：** 这很有趣——测试通过但游戏有 bug，说明两件事之一：第一，测试脚本用的是独立实现，和游戏代码逻辑不一样；第二，你的 bug 不在测试覆盖的范围里——是一个「需求没写到的边界情况」。你知道是哪种吗？
（引导学生区分「需求定义的 bug」和「需求未覆盖的 bug」）

---

**第三幕：反思 (Contemplate) — 10分钟** 🤔

*本幕目标：两学生角色现场对战演示（投屏全班围观），展示「同一规则、不同角色」下的策略差异*

**【环节】角色对战演示 (6分钟)**

**师：** 好，现在我们来见证今天最重要的时刻——让两个同学的角色真的打一场。

【选两个进度最快的学生，把他们的 pk-battle.html 分别投屏，或者其中一个学生的对战画面投屏全班观看】

**师：** 我们先看左边——这个角色是什么打法？

**生（观察者）：** （预期：高攻击型 / 速度型 / 防御型）

**师：** AI 对手用的是默认属性。先看第一回合谁先出手——

【运行对战，全班实时观看，教师边打边念战斗日志】

**师：** 看到没，先手机制起作用了——SPD 高的先出手。现在玩家用重击——

【展示重击效果，强调伤害计算是按需求文档的公式来的】

**师：** 这场对战里有哪条规则你们觉得体现得最清楚？

**生：** （预期：先手 / 格挡减伤 / 特技限制次数）

**【环节】互评与讨论 (4分钟)**

**师：** 现在回顾今天的工作。今天你们打开了几个窗口，每个窗口做什么？

**生：** （预期：窗口 A 写需求，窗口 B 找边界，窗口 C 生成代码，窗口 D 做测试）

**师：** 对。这四个窗口，缺一个会出什么问题？比如如果跳过窗口 B——

**生：** （预期：边界情况没想到，游戏对战时可能出 bug）

**师：** 如果窗口 D 不开新窗口，在窗口 C 里让 AI 测自己的代码——

**生：** （预期：AI 会偏袒自己，测试结果不可信）

**师：** 今天遇到最大的挑战是什么？

**生：** （预期 A：「边界情况想不到」/ 预期 B：「看懂测试失败的原因」/ 预期 C：「导入 Spritesheet」）

**师：** 你们今天写需求、审核、生成、验收、测试——5 个步骤。这 5 步，哪一步对你来说最有收获？

**生：** （开放回应，教师倾听，不评判）

**师：** 我来说说我的观察——今天做得最好的事，是大家在遇到 bug 时，第一步不是「让 AI 帮我改」，而是先问「哪条需求没说清楚」。这个习惯，今天有人做到了，有人还在培养。没关系——这个习惯养成了，你做任何项目都会比别人少踩很多坑。

---

**第四幕：延续 (Continue) — 5分钟** 🚀

**【环节】抽象总结 (3分钟)**

**师：** 今天我们做了一件很重要的事——让「脑子里的规则」变成「代码里真正执行的规则」，中间经过了几个步骤？

**生：** （预期：写需求、找边界、生成代码、验收、测试脚本）

**师：** 对。这个流程不只是做游戏用的——以后你做任何项目，只要涉及「规则」，都要走这个流程。规则越复杂，「找边界」这步就越重要。

**师：** 今天你们掌握的能力有一个名字，叫**「测试驱动验证」**——先定规则，再写代码，再用测试检查代码是不是真的按规则跑。这是真实开发团队的工作方式，不是只有高手才能用的，你们今天已经用上了。

**师：** 还有一个能力——「边界意识」。以后遇到任何系统，你的第一反应不是「正常情况怎么用」，而是「边界情况、异常情况怎么处理」。这个意识让你的系统更稳定，bug 更少。

**师：** 边界意识不只是写代码时用的。你们有没有玩过一个游戏，有个 bug 特别烦——比如某个角色在特定情况下无敌，或者某个技能叠加 20 层秒杀对手——这些 bug 从哪里来？就是设计师在写规则的时候，没有考虑到「如果玩家这样做怎么处理」。你们今天练习的，就是这个能力。

**【环节】下节预告 + 5分钟挑战 (2分钟)**

**师：** 现在你们的战斗系统能跑，逻辑是对的，但角色还是矩形占位符。下节课我们做什么？

**生：** （预期：换成自己的图？/ 加动画？）

**师：** 对——下节课我们把你的 Spritesheet 真正加进战斗系统，受击有闪烁效果，攻击有前摇后摇，死亡有倒下动画。你的角色会真的「活起来」。

**师：** 本周 5 分钟 AI 挑战——让 AI 帮你写一条需求文档里没有的边界情况测试，提交到测试脚本里，看是否通过。如果通过了，说明你的代码比需求文档还严格；如果没通过，说明你发现了一个新 bug。下节课分享。

**师：** 最后一件事——在 pk-battle.html 的顶部注释里，写上你的角色名、打法定位一句话总结，还有今天你发现的最有意思的一条边界情况。格式随意，只要下节课你打开文件，还能想起今天做了什么。

---

### 5. AI助教使用指南

**教师演示用提示词（窗口 A，需求文档整理）：**

```
你是一个需求分析师。我要做一个基于 HTML/CSS/JS 的回合制战斗游戏，规则如下：
- 先手机制：SPD 高的角色先出手；同速时随机决定（或编号小的先手，明确规定一种）
- 伤害公式：ATK - DEF，最低造成 1 点伤害
- 行动类型：普通攻击（按公式）/ 重击（伤害×1.8）/ 格挡（本回合受伤减少50%）/ 特技（每场只能用一次，效果来自角色JSON的skill字段）
- 胜负判定：HP≤0 判输，立即结束
- AI对手决策：HP低于30%用特技；25%概率重击；否则普通攻击

请把这些需求整理成规范需求文档，包含：功能描述、触发条件、功能规则、边界情况说明、验收标准。
```

**边界审核用提示词（窗口 B，独立新对话）：**

```
你是一个游戏测试工程师。以下是战斗系统需求文档，请列出所有你能想到的边界情况和异常情况（例如：同速时谁先出手？特技已用完再点会怎样？格挡叠加重击怎么算？双方同时HP≤0怎么判？），只列问题，不需要解答：

[粘贴需求文档]
```

**测试脚本生成（窗口 D，独立新对话）：**

```
以下是我的战斗系统需求文档：

[粘贴需求文档]

请帮我写一个 JavaScript 测试脚本（单文件 HTML），验证以下4条规则：
1. 伤害公式：ATK=15, DEF=5 → 伤害=10；ATK=5, DEF=10 → 伤害=1（最低1点）
2. 先手判定：SPD=8 vs SPD=5 → SPD=8 的先出手
3. HP≤0 → 游戏结束标志为 true
4. 重击倍率：ATK=10, DEF=2, 重击 → 伤害=(10-2)×1.8=14（取整）

每条测试显示"✅通过"或"❌失败：期望XXX，实际XXX"。用纯 HTML/JS，不需要 Phaser.js。
```

**学生保底提示词（窗口 C，生成战斗系统骨架）：**

```
请帮我用纯 HTML/CSS/JS（单文件）做一个回合制战斗游戏：

角色数据从 JS 对象读取（字段：name/hp/maxHp/atk/def/spd/skill）：
- 玩家角色在左边，AI 对手在右边
- 先用彩色矩形代替角色图片（稍后替换）

战斗规则：
- 先手：SPD 高的先出手；同速时随机
- 伤害公式：ATK - DEF，最低 1 点
- 四种行动按钮：普通攻击 / 重击（×1.8）/ 格挡（本回合受伤-50%）/ 特技（每场限用一次）
- AI对手逻辑：HP<30%用特技；25%概率重击；否则普通攻击
- HP≤0 判输，显示胜负结果，提供「再来一局」按钮

界面要求：
- 双方头顶有 HP 血条（数值显示）
- 底部有战斗日志（最近3条）
- 特技用完后按钮变灰不可点击

只给骨架，角色图片先用矩形占位，单文件 HTML。
```

**进阶提示词（替换 Spritesheet）：**

```
以下是我的 pk-battle.html 中绘制角色的代码片段：[粘贴相关代码]

请修改角色绘制逻辑，把左边玩家角色的矩形替换为 Spritesheet 图片显示：
- Spritesheet 文件名：player-sprite.png，128×64 像素
- 帧1（x=0, y=0, w=64, h=64）：待机状态
- 帧2（x=64, y=0, w=64, h=64）：攻击状态
- 平时显示帧1，玩家攻击时显示帧2 持续 300ms 后恢复帧1
- 图片要等比缩放，显示在原来矩形的位置
```

---

### 6. 教师指南

**本课技术备注：**

- **Spritesheet 导入**：`new Image()` → `onload` → `ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh)`。sx/sy 是帧在图片中的起始坐标，sw/sh 是帧的宽高，dx/dy 是画布上的目标位置，dw/dh 是目标大小。教师需要理解这 8 个参数的含义，学生不需要记，只需知道「帧1=x0，帧2=x64」。
- **回合制 vs 实时战斗**：本课做的是回合制（玩家点一次按钮 = 一个回合）。教师不要混淆，不要和「帧循环/requestAnimationFrame」扯在一起，那是下节课动画的内容。
- **先手机制的实现**：在回合开始时比较 SPD，高的先执行攻击逻辑，低的后执行。同速时 `Math.random() < 0.5` 决定。这个逻辑放在「玩家点击行动按钮」的事件处理函数里。
- **格挡状态**：格挡是一个「状态标记」（如 `isBlocking = true`），在本回合受到伤害时检查这个标记，伤害减半后重置标记。注意：格挡对「下一次受到的攻击」生效，还是「本回合」生效，需在需求里明确（本课设计为「格挡后敌方先手攻击时生效」）。
- **测试脚本独立窗口**：这是本课最重要的原则。测试脚本里的战斗逻辑函数是「从需求文档重新实现」的独立版本，不引用 pk-battle.html 的代码——所以必须在独立窗口生成，让 AI 按需求文档写，而不是看着自己的代码写测试。

**常见问题 FAQ：**

| 问题 | 应对 |
|------|------|
| 「特技按钮用了还能继续按」 | 引导学生检查需求文档里「特技每场限用一次」的验收标准写了什么，再在代码里找特技的触发逻辑，看有没有加 `skillUsed` 标记 |
| 「测试脚本生成出来全部通过，但游戏实际打起来伤害不对」 | 说明测试脚本和游戏代码用的是两套独立实现。先确认测试脚本按需求文档写，再对照游戏代码里的实现，找两者差异 |
| 「Spritesheet 图片不显示，一片黑」 | 99% 是路径问题——图片文件和 HTML 文件要放在同一个目录，路径写相对路径。让学生把 HTML 和 PNG 放到同一个文件夹再刷新 |
| 「重击的伤害计算和我预期不一样」 | 让学生打开战斗日志，找重击那行，用计算器手算一遍：(ATK - DEF) × 1.8，取整，对比日志里的数值 |
| 「AI 对手一直用特技，根本不普通攻击」 | AI 对手的决策逻辑里「HP<30%用特技」的判断可能有 bug——HP 初始值设置不对，或者条件写反了。引导学生检查 AI 对手的 maxHp 和当前 hp 的比较逻辑 |
| 「窗口 B 没问出什么问题」 | 提示学生在提示词末尾加一句：「请特别关注：同速先手、特技用完再点、格挡叠加重击、双方同时归零 这四个场景」 |
| 「测试脚本里 ✅ 全通过，但我不知道这说明什么」 | 说明你的战斗逻辑函数在这 4 种情况下是正确的。测试脚本是「护栏」——以后你改代码，再跑一次，如果还是全通过，说明你没有破坏原来的逻辑 |
| 「怎么知道 AI 对手「25%概率重击」是不是真的 25%？」 | 这是个好问题。严格测试需要跑 100 次统计分布，但我们的课堂不做这个。你可以告诉 AI「请解释这段 AI 决策代码里重击概率是如何实现的」，让 AI 用中文解释给你看 |

**课堂风险预案：**

- **如果 AI 服务不可用（Kimi 宕机）**：切换到 Trae IDE 的内置 AI 对话，或使用教师备用的 demo-2-pk-battle.html 直接展示；学生完成需求文档部分，代码生成推迟
- **如果学生进度差异过大**：进度快的学生做拓展（导入 Spritesheet + 写边界测试）；进度慢的学生保证完成「战斗骨架能跑通 + 普通攻击验收通过」即可，其余留作课后
- **如果有学生上节课的 Spritesheet 未完成**：提供默认占位图（单色 128×64 PNG），确保不因图片问题卡住当天进度

---

### 7. 5分钟日常AI挑战

**本周挑战：** 让 AI 帮你写一条「需求文档里没有的」边界测试，看看能不能通过

**挑战说明：**
打开窗口 D（全新对话），告诉 AI：「我的战斗系统需求文档里有这些规则：[粘贴你的需求文档]。请帮我写一条我没有明确写进需求的边界测试——就是那种「正常情况不会想到，但真实对战时可能出现」的场景。」把这条新测试加入你的 pk-test.html，运行，看是通过还是失败。把结果（截图或描述）带到下节课分享。

**下节课分享：** 下节课选 2-3 位同学展示——「我写的新边界是什么，测试结果如何，有没有发现 bug」

---

### 8. 拓展任务

**拓展一（推荐）：** 把你的 Spritesheet 真正导入 pk-battle.html，用 `drawImage` 替换矩形占位符；不需要动画，只要图片能显示在正确位置。验收标准：打开游戏，左边角色是你自己画的图，不是矩形。

**拓展二（挑战）：** 给你的战斗系统加一条新的行动类型：「反击」——格挡时如果对方用了重击，本回合额外反弹 30% 伤害给对方。先把这条规则写进需求文档，在窗口 B 审核边界情况（格挡+反击叠加怎么算？反击触发条件是什么？），然后在窗口 C 生成新代码，在窗口 D 补充测试用例，确保测试通过。

**拓展三（终极挑战）：** 做一个双人对战模式——两个玩家分别控制左边和右边的角色，轮流点行动按钮。需求文档里要写清楚：轮次如何切换、一方操作完后如何提示另一方、胜负后如何重新开始。先写需求，再生成，再测试。

---

> 本教案遵循穹狼科创 SDDT + 4C 教学方法论编写，配合 `教学方法论规则.md` 和 `标准教案模板.md` 使用。
