mcpField
P
phaser-feedback

phaser-feedback

github.com/yakoub-ai测试与质量2966 次安装9 周前更新
代码仓库

关于 phaser-feedback

phaser-feedback 是将玩家反馈转化为已验证游戏修复的技能。它引导开发者分类反馈、归纳可能的根因、编写复现问题的试玩场景,并将其保留为回归测试。

主要功能

  1. 01保留反馈原文及其上下文
  2. 02区分缺陷、数值调整、设计与信息不足
  3. 03按可能根因归类反馈
  4. 04创建可复现的试玩场景
  5. 05验证修复并检查回归问题
  6. 06将复现场景保留为回归测试

适用场景

  1. 01调查偶发的游戏问题
  2. 02区分程序错误与平衡或设计意见
  3. 03把玩家反馈转为长期回归检查

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/yakoub-ai/phaser4-gamedev --skill phaser-feedback

安装后在 Claude Code 中输入/phaser-feedback确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

examples/
worked-session.md
references/
feedback-to-scenario.md
response-templates.md
triage-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 — 修复

常规规范仍然适用,而且在这里更要严格遵守,因为你依据的是转述的描述:

  1. 先调查。 阅读源代码。在编写修复前,说明导致问题的原因、问题发生的机制,以及你的修复方案。
  2. 尝试两次,然后换方向。 如果一种方法尝试两次都失败,就停下来并提出两到三种真正不同的方法。不要对已经失败两次的方法再尝试第三次。
  3. 修复分组时确定的原因,而非分别修补每个症状。 如果三个报告有相同的根因,一个修复就应该解决全部三个。如果只解决了一个,说明分组有误——说明这一点并重新分组,而不是分别修补另外两个。

步骤 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/ — 性能报告的处理方式;在断定存在内存泄漏之前先查看

评论

支持 Markdown