用途
本技能把设计意图转化为具体的实现约束。它处于游戏设计与引擎特定编码之间:将“连打应弱于节奏操作”这类陈述,转换为不变量、实现模式和验证检查,可在任何引擎中应用。
即使没有使用 designing-mini-games,只要游戏规格包含风险/回报描述但没有说明代码如何强制执行,也可以使用本技能。如果游戏还没有设计规格,请从 designing-mini-games 开始(它涵盖单按钮和多输入简报)。
何时使用
- 根据设计规格或 README 实现游戏机制。
- 在运行平衡测试之前,对照设计规格审查代码。
- 检查器或模拟显示 mono_dominant、tied 或重复输入占优,并且你已经知道哪个设计承诺需要代码级不变量。
- 设计提到空闲弱、仅按住弱、连打弱、安全等待、冷却、连击、得分脉冲、护盾、充能或无敌。
- 实现是引擎特定的,但失败模式是规则层面的而非 API 层面的。
何时不使用
- 纯渲染、资源、UI、音频、构建或引擎 API 工作。
- 规则级不变量已经强制执行后的数值调优。
- 核心规则和得分事件已知之前的开放式游戏设计。
- 从遥测中选择平衡修复策略。当失败已经发生且需要诊断原因或选择平衡模式时,请从 evaluating-gameplay-balance 开始;之后当所选修复需要新的代码级不变量时,再使用本技能。
必需输入
- 设计规格或 README。
- 必须弱于熟练操作的输入策略:空闲、仅按住、连打、始终左/右、交替连打、安全等待或等效策略。
- 预期的熟练模式。
- 得分事件、游戏结束条件以及任何安全/能量状态。
- 项目检查器使用的任何确定性验证种子或已知初始随机序列。
- 可选的遥测或检查器输出。
过程
-
提取每个必须在代码中成立的设计承诺:
- “空闲弱”
- “仅按住弱”
- “连打弱”
- “得分来自时机”
- “护盾安全但代价高”
- “落地/脉冲/反射奖励精准”
-
将每个承诺改写为不变量:
- 使用可测试的语言:“在上一次点击后 12 帧内的点击不能得分”,而不是“连打不好”。
- 包含范围:每次输入、每个目标、每个危险、每次落地、每个连击、每秒或每个资源单位。
- 包含后果:不得分、冷却、暴露、资源消耗、连击重置、瞄准失误或可见的位置代价。
- 当得分对象可以跨帧或跨输入持续存在时,明确定义其生命周期:生成/开始、活跃得分窗口、已得分/已消耗状态、过期/移除,以及新的生命周期是否可以再次得分。
- 对于重复脉冲、反射、AoE、护盾或释放得分,在验证日志中尽可能要求每次机会的身份标识(targetId、pulseId 或等效物),以便检测重复得分。
-
当机制可以从规格推理时,在编码前运行策略特定的预检查:
- 对于每个坏策略,写明它应该死亡、安全超时还是以受限得分存活。
- 如果坏策略可以无限期存活,不变量必须限制或饿死其得分;死亡不能成为弱点。
- 如果随机初始危险、间隙、生成或路线影响生存能力,预览项目的确定性验证种子并确认前 5-10 个结果符合不变量假设。
-
如果需要,从 references/invariant-patterns.md 中选择实现模式。
-
检查得分漏洞:
- 没有每次输入的原始得分。
- 没有可重复得分而无需新危险/机会的可重用脉冲。
- 除非生存是主要精通信号,否则玩家处于安全/低技能状态时没有生存得分。
- 没有无上限得分的永久安全状态。
- 没有长期存活的已得分对象在未来仍有资格得分,除非其生命周期明显重置。
- 对于连打修复,不能只验证总分;当机制可以重新得分同一对象时,还要比较每个唯一机会或唯一目标的得分。
-
添加或审查实现:
- 引擎特定代码属于引擎技能或项目代码。
- 本技能定义必须成立的内容,而不是确切的渲染或 API 调用。
-
验证:
- 运行可用的单调策略和探索/熟练策略。
- 在完整的全局比率检查之前,优先使用不变量特定的探针:NoInput 得分/死亡、HoldOnly 得分/死亡、SpamPress 得分事件、实体计数、资源消耗和每机会得分上限。
- 对于脉冲/反射/AoE 修复,记录接受/拒绝的输入、脉冲年龄或冷却状态、目标/机会 ID、已得分状态、得分增量和最大活跃实体数。
- 尽可能在编辑前定义探针的通过/失败界限,例如 SpamPress 每个唯一目标的得分低于脚本化熟练循环、HoldOnly 得分限制在指定速率以下,或最大活跃实体低于固定限制。
- 确认已知坏策略的得分低于熟练操作。
- 如果不存在模拟器,编写简短的人类可读验证清单并标记差距。
不变量模板
在规格、PR 说明或代码审查中使用此紧凑形式:
- 设计承诺:<规格中的文字>
- 实现不变量:<可测试规则>
- 模式:<来自 references/invariant-patterns.md 的模式 ID,例如 cooldown、per-target-cap、resource-drain>
- 读数:<让玩家能够根据此规则行动的持久屏幕指示器——或说明为什么此规则不改变玩家决策>
- 预检查:<坏策略是死亡、安全存活还是必须限制得分>
- 验证:<如果违反不变量应失败的策略、遥测或种子场景>
读数不是规则的文档;它是规则的一部分。一个改变玩家应该做什么的不变量,如果没有被绘制出来,就与不公平的游戏无法区分——参见下面的“不可见规则”。
常见失败模式
- 仅文字弱点:README 说连打不好,但代码给每次点击都提供了得分机会。
- 可重用得分脉冲:落地、护盾、AoE 或反射脉冲可以通过重复输入多次得分。
- 安全得分:最安全的状态也能得分或保留倍率。
- 隐藏测试补丁:代码特殊处理坏策略,而不是创建可见的游戏规则。
- 仅数值修复:更改生成率或得分值,而没有强制执行缺失的不变量。
- 无界实体:得分或危险对象存活太久,导致验证缓慢和连打得分循环。
- 种子特定意外:验证种子产生违反设计假设的早期危险/间隙,问题仅在完整 GA 运行后才被发现。
- 不变量冲突:设计要求互斥的不变量(例如“空闲必须死亡”和“生存是唯一得分”)。如果解决冲突需要更改核心体验或丢弃标签关系,报告设计不可挽救并建议失败报告。
- 不可见规则:不变量实现正确,通过所有值断言,但从未在屏幕上绘制。这是最难捕捉的一类,经验上也是最常见的之一——在一个测量项目中,九个已发布并修复的 bug 中有四个是同一缺陷(控制、过载触发器和清除条件在模拟中完全正确,但对玩家不可见)。值断言从构造上无法检测它:模拟是正确的。只有读数要求加上截图才能检测。
验证
当以下条件满足时,更改是有效的:
- 每个设计承诺至少有一个实现不变量。
- 每个单调策略都有可见代价或得分上限。
- 任何无限期存活的单调策略都有得分上限或得分饿死规则。
- 每个改变玩家决策的不变量都有持久读数,并且该读数在情况变坏之前填充,而不是在已经变坏时触发一次警告。跟踪拥挤程度接近上限的仪表教会玩家行动;过载时刻的警报只是叙述损失。在规则的同一次更改中添加读数,并截图。
- 当随机初始危险或间隙决定生存能力时,已检查种子早期游戏随机序列。
- 熟练操作有因果路径获得更高得分或更长的有意义生存。
- 实现可以通过策略比较、遥测或具体人工清单进行测试。
- 如果无法验证,报告不变量已指定但未验证。
参考
- references/invariant-patterns.md — 用于连打、仅按住、空闲、安全等待、脉冲得分、连击和资源状态的可重用模式。