关于 phaser-feedback
phaser-feedback 是将玩家反馈转化为已验证游戏修复的技能。它引导开发者分类反馈、归纳可能的根因、编写复现问题的试玩场景,并将其保留为回归测试。
主要功能
- 01保留反馈原文及其上下文
- 02区分缺陷、数值调整、设计与信息不足
- 03按可能根因归类反馈
- 04创建可复现的试玩场景
- 05验证修复并检查回归问题
- 06将复现场景保留为回归测试
适用场景
- 01调查偶发的游戏问题
- 02区分程序错误与平衡或设计意见
- 03把玩家反馈转为长期回归检查
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ yakoub-ai/ phaser4-gamedev --skill phaser-feedback安装后在 Claude Code 中输入/phaser-feedback确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
examples/worked-session.mdreferences/feedback-to-scenario.mdresponse-templates.mdtriage-guide.md详细介绍
将玩家反馈转化为已上线的修复
反馈通常是不了解你代码的人用文字写下的:
“跳跃感觉飘飘的,而且我有时会直接穿过平台掉下去。还有,Boss 难到根本打不过。商店也坏了。”
四句话,四种不同类型的问题,其中只有一个通常意义上的 Bug。把它们一概而论,最终就会导致修复结束后,玩家真正的抱怨依然没有得到解决。
此技能运行的循环:
intake ──► triage ──► group by root cause ──► reproduce (failing scenario)
│
report back ◄── verify ◄── fix
│
└──► scenario is kept as a regression test
真正起到关键作用的一步是先复现,再修复。如果某个游戏测试场景因玩家描述的原因而失败,就能证明你理解了他们的意思。没有这一步,你只是在把文字描述和代码进行模式匹配,最常见的结果就是修复了一个听起来合理、但没人抱怨过的问题。
步骤 1 — 收集反馈
原样记录反馈。不要润色,不要将其转成技术语言,也不要删掉看似模糊的部分。原话中包含有用信息:
- “有时”/“偶尔”/“一次” → 表示频率,会影响你复现问题的方式(见下文的
--repeat) - “过了一会儿”/“后来” → 表示状态累积或泄漏
- “在我的手机上”/“全屏时” → 表示平台问题;你需要了解设备状态
- “以前还能用” → 表示回归问题;先找到出问题前的提交,再着手查找 Bug
记录每项反馈的来源,以及反馈者提供的任何背景信息。如果多位反馈者都提到了同一件事,记下人数——反馈频率是你能获得的成本最低的优先级信号。
对于任何与平台有关的问题,先了解设备状态,再做其他事情:浏览器及其版本、iOS 还是 Android、Safari 标签页还是 PWA 还是 Capacitor、屏幕方向、触控还是鼠标。大多数“移动端 Bug”都是特定平台上的 Bug;不了解这些信息就无法区分。
步骤 2 — 分为四类
这一步决定了整个处理是否成功。每一类都需要不同的应对方式,分类错误会白白浪费整个周期。
| 类型 | 游戏…… | 需要什么 | 由谁决定 |
|---|---|---|---|
| 缺陷 | 做了不该做的事 | 一个失败的场景,然后修复 | 你 |
| 调优 | 按代码的设定运行,但数值感觉不对 | 一个数值目标,然后修改数值 | 用户 |
| 设计 | 按设计运行,但用户不喜欢这种设计 | 修改 GDD | 用户 |
| 无法采取行动 | — | 向反馈者提出具体问题 | 反馈者 |
将上述示例分类:
| 引言 | 类型 | 原因 |
|---|---|---|
| “而且我有时会直接穿过平台掉下去” | 缺陷 | 碰撞检测失效了。没人会故意这么设计。 |
| “跳跃感觉飘飘的” | 调优 | 重力和跳跃速度完全按当前设定运行。 |
| “Boss 难到根本打不过” | 调优或设计 | 含义模糊——是生命值数值的问题,还是整场战斗的问题?需要询问。 |
| “商店也坏了” | 无法采取行动 | 怎么个坏法?无法据此复现问题。 |
不要为主观感受问题编造指标。 难度感受、美术辨识度、音频混音和“好不好玩”,都无法通过无头浏览器判断。将它们标记为需要人工评估,并保留这个标记——为主观感受问题设定虚假的数值目标,只会让你信心十足地朝任意方向修改。
不要擅自把调优反馈升级为缺陷。 如果把“Boss 难到根本打不过”当成缺陷修复,就会去排查并不存在的伤害计算 Bug。如果把它当成调优问题来处理,就应该询问“这场战斗应该打多久——需要命中几次、耗时多长?”然后修改数值。第二种处理方式几乎总是正确的,但需要用户给出答案。
动代码之前,先写下分类结果并展示给用户。纠正错误分类只需一句话;如果等修复后才发现分类错误,就会浪费整个处理周期。
步骤 3 — 按疑似根因分组
不同报告通常有着相同的原因。调查前先分组——一个根因胜过一堆单独的补丁,而症状归组后也比单独查看任何一个症状更容易快速找到根因。
"sometimes i fall through the platform"
"the moving platform drops me at the top" ──► one suspect:
"i clipped into the wall when i dashed" high velocity vs. collision step
三个问题单,很可能只需一个修复:物体每帧移动的距离足够大,穿透了较薄的碰撞体。如果分别修补,你会写出三个特例,而下周就会收到第四个报告。
难以归组的报告可以单独成组。不要勉强归组。
步骤 4 — 复现为失败场景
先写场景,再修复。看着它失败。永远保留它。
场景能将玩家的文字描述转化为机器可检查的内容。一旦熟悉词汇,转换就是机械性的——完整对应关系见
references/feedback-to-scenario.md。
// playtest/repro-fall-through-platform.mjs
export default [
{ name: 'set up the reported situation', action: 'eval', code: `
const s = scene('GameScene');
s.player.setPosition(400, 100); // above the platform
s.player.body.setVelocityY(1200); // the reporter was falling fast
` },
{ name: 'player lands on the platform, does not pass through',
action: 'waitFor',
expression: `scene('GameScene').player.body.blocked.down`,
timeout: 3000 },
{ name: 'and is still above it',
action: 'expect',
expect: { expression: `scene('GameScene').player.y`, below: 500 } },
];
运行它:
node "${CLAUDE_PLUGIN_ROOT}/skills/phaser-playtest/scripts/playtest.mjs" \
--project . --scenario playtest/repro-fall-through-platform.mjs
它必须失败。 首次运行就通过的复现并没有复现任何问题,而你接下来写的修复也将无的放矢。这种情况发生时,错的是场景,不是报告——回去获取更多细节。
“有时”表示发生频率,也有对应的工具
node ".../playtest.mjs" --project . --scenario playtest/repro.mjs --repeat 10
判定结果会告诉你遇到的是哪种 bug:
| 判定结果 | 诊断 |
|---|---|
10/10 runs clean | 未复现。场景遗漏了玩家做过的某些操作。 |
INTERMITTENT — failed in 3/10 | 问题确实存在,且与竞态或随机数生成有关。 |
failed in all 10 — consistent | 根本不是偶发故障。复现完全准确;直接修复即可。 |
对于间歇性结果,添加 --seed 42。如果问题变得稳定,说明 bug 取决于随机数生成路径——例如生成判定或掉落判定进入了代码未处理的状态。如果固定种子后问题仍然间歇出现,原因就在时序:帧顺序、异步加载,或与 update() 竞争的事件。这两种情况需要不同的修复方法,而这是区分它们最省力的办法。
让断言与问题描述相匹配
| 报告者的描述 | 使用以下断言 |
|---|---|
| “有时会发生” | --repeat 10 |
| “敌人会卡住” | sample + expect: { stat: 'range', atLeast: n } |
| “摄像机会抖动” | sample + expect: { stat: 'stddev', atMost: n } |
| “要花很久才能击杀” | 使用 timeout 的 waitFor |
| “我的生命值会莫名其妙地下降” | sample + expect: { stat: 'delta', equals: 0 } |
| “过一会儿就会变慢” | --heap,再加上一个会重启场景的 repeat 块 |
| “只在我的手机上出问题” | --device iphone / --device android |
| “操作控制会互相干扰” | 使用多个按键的 hold——对角线输入有独立的处理路径 |
| “更新后就坏了” | 检出上一个提交,并运行相同的场景 |
步骤 5 — 修复
常规规范仍然适用,而且在这里更要严格遵守,因为你依据的是转述的描述:
- 先调查。 阅读源代码。在编写修复前,说明导致问题的原因、问题发生的机制,以及你的修复方案。
- 尝试两次,然后换方向。 如果一种方法尝试两次都失败,就停下来并提出两到三种真正不同的方法。不要对已经失败两次的方法再尝试第三次。
- 修复分组时确定的原因,而非分别修补每个症状。 如果三个报告有相同的根因,一个修复就应该解决全部三个。如果只解决了一个,说明分组有误——说明这一点并重新分组,而不是分别修补另外两个。
步骤 6 — 验证
以下两点必须同时成立,并且都必须检查:
# 1. The repro now passes — and for an intermittent bug, passes repeatedly.
node ".../playtest.mjs" --project . --scenario playtest/repro-fall-through-platform.mjs --repeat 10
# 2. Nothing else broke.
npx tsc --noEmit
node ".../playtest.mjs" --project . --mode build
for f in playtest/*.mjs; do node ".../playtest.mjs" --project . --scenario "$f"; done
对于间歇性 bug,单次成功运行不算验证——这只是导致问题难以发现的同一次抛硬币。重新运行的次数至少要达到复现问题所需的次数。
如实报告结果。 如果复现测试通过了,但另外两个场景现在失败了,就要说明这一点。用一个 bug 换来两个 bug 的修复还不算完成。
步骤 7 — 回复反馈,并保留测试场景
使用反馈者的语言回复,而不是你自己的语言。他们描述了一段体验;告诉他们这段体验有哪些变化:
"有时候我会直接穿过平台" — 已修复。下落速度足够快时,角色可能会在物理计算步骤之间穿过薄平台。现在快速下落时也能正常落在平台上。
"跳起来感觉飘" — 已调整:现在跳到顶点需要 0.35s,而不是 0.6s。请告诉我这是不是你想表达的问题。
"这个 Boss 不可能打赢" — 尚未调整。你觉得这场战斗应该持续多久,或者尝试多少次才算合理?我可以调整它的生命值、伤害或攻击频率。
"商店坏了" — 我没能复现这个问题。你点了什么?实际发生的情况又是什么?
回复中值得一提的有三件事:你修复了什么、你做了什么调整并希望对方确认,以及你还需要对方提供什么信息。只列出修复内容的回复,会漏掉那两件需要反馈者参与的事。
提交测试场景。 playtest/repro-*.mjs 现在是永久回归测试。这正是反馈不断积累价值的方式:每次按这种方式修复报告,都会留下一个检查项,防止问题再次出现。六个月的反馈会变成一套测试,记录真实玩家在你的游戏中实际做过的事情——再多单元测试也无法做到这一点。
反模式
- 复现之前就开始修复。 这是这里代价最高的错误。你根据陌生人对自己代码的描述开展工作;你第一次理解正确的概率不高,而通过测试的复现是验证这一点最省钱的方法。
- 把反馈改得面目全非。 把“感觉飘”改写成“调整重力”,会丢掉你还不知道对方想要什么这一事实。
- 把主观体验问题当作指标。 给“Boss 好不好玩”指定一个数字,你就会自信地朝没人要求的方向做出调整。
- 多个报告存在共同原因,却逐个修复。 这会产生特殊情况,而底层 Bug 仍会不断带来新工单。
- 间歇性 Bug 只跑一次就显示通过。 正是这种碰运气的做法,当初才让 Bug 隐藏起来。
- 修复后删除复现测试。 这等于丢掉唯一能防止问题卷土重来的材料。
- 对未修复的问题保持沉默。 反馈者无法区分“我决定暂不处理”和“我无视了你”,而后者会让你失去下一条反馈。
其他资源
参考文件
references/triage-guide.md— 详细介绍四种类型,逐条分析真实反馈内容、模糊案例,以及分类后如何确定优先级。references/feedback-to-scenario.md— 从反馈措辞到测试场景构造的完整对照表,附有常见投诉类型的完整示例场景。references/response-templates.md— 如何回复反馈者、编写面向玩家的更新日志,以及如何妥善跟进,让下一条反馈比这一条更有帮助。
示例
examples/worked-session.md— 从头到尾处理一段杂乱的 Discord 消息:分类、归组、三个复现测试场景、修复、验证,以及发回的回复。
相关内容
skills/phaser-playtest/— 测试工具、完整的场景参考,以及让游戏能够进行这一切所需的检测工具skills/phaser-debugger/— 创建复现测试后进行根因调查skills/phaser-gdd/— 设计类反馈的处理位置:第 13 节验收标准skills/phaser-analyze/— 性能报告的处理方式;在断定存在内存泄漏之前先查看