SuperAPI AI 工具集

implementing-gameplay-invariants

编程与开发

查看项目

更新于 10-15 10:16

技能介绍

用途

本技能把设计意图转化为具体的实现约束。它处于游戏设计与引擎特定编码之间:将“连打应弱于节奏操作”这类陈述,转换为不变量、实现模式和验证检查,可在任何引擎中应用。

即使没有使用 designing-mini-games,只要游戏规格包含风险/回报描述但没有说明代码如何强制执行,也可以使用本技能。如果游戏还没有设计规格,请从 designing-mini-games 开始(它涵盖单按钮和多输入简报)。

何时使用

  • 根据设计规格或 README 实现游戏机制。
  • 在运行平衡测试之前,对照设计规格审查代码。
  • 检查器或模拟显示 mono_dominant、tied 或重复输入占优,并且你已经知道哪个设计承诺需要代码级不变量。
  • 设计提到空闲弱、仅按住弱、连打弱、安全等待、冷却、连击、得分脉冲、护盾、充能或无敌。
  • 实现是引擎特定的,但失败模式是规则层面的而非 API 层面的。

何时不使用

  • 纯渲染、资源、UI、音频、构建或引擎 API 工作。
  • 规则级不变量已经强制执行后的数值调优。
  • 核心规则和得分事件已知之前的开放式游戏设计。
  • 从遥测中选择平衡修复策略。当失败已经发生且需要诊断原因或选择平衡模式时,请从 evaluating-gameplay-balance 开始;之后当所选修复需要新的代码级不变量时,再使用本技能。

必需输入

  • 设计规格或 README。
  • 必须弱于熟练操作的输入策略:空闲、仅按住、连打、始终左/右、交替连打、安全等待或等效策略。
  • 预期的熟练模式。
  • 得分事件、游戏结束条件以及任何安全/能量状态。
  • 项目检查器使用的任何确定性验证种子或已知初始随机序列。
  • 可选的遥测或检查器输出。

过程

  1. 提取每个必须在代码中成立的设计承诺:

    • “空闲弱”
    • “仅按住弱”
    • “连打弱”
    • “得分来自时机”
    • “护盾安全但代价高”
    • “落地/脉冲/反射奖励精准”
  2. 将每个承诺改写为不变量:

    • 使用可测试的语言:“在上一次点击后 12 帧内的点击不能得分”,而不是“连打不好”。
    • 包含范围:每次输入、每个目标、每个危险、每次落地、每个连击、每秒或每个资源单位。
    • 包含后果:不得分、冷却、暴露、资源消耗、连击重置、瞄准失误或可见的位置代价。
    • 当得分对象可以跨帧或跨输入持续存在时,明确定义其生命周期:生成/开始、活跃得分窗口、已得分/已消耗状态、过期/移除,以及新的生命周期是否可以再次得分。
    • 对于重复脉冲、反射、AoE、护盾或释放得分,在验证日志中尽可能要求每次机会的身份标识(targetId、pulseId 或等效物),以便检测重复得分。
  3. 当机制可以从规格推理时,在编码前运行策略特定的预检查:

    • 对于每个坏策略,写明它应该死亡、安全超时还是以受限得分存活。
    • 如果坏策略可以无限期存活,不变量必须限制或饿死其得分;死亡不能成为弱点。
    • 如果随机初始危险、间隙、生成或路线影响生存能力,预览项目的确定性验证种子并确认前 5-10 个结果符合不变量假设。
  4. 如果需要,从 references/invariant-patterns.md 中选择实现模式。

  5. 检查得分漏洞:

    • 没有每次输入的原始得分。
    • 没有可重复得分而无需新危险/机会的可重用脉冲。
    • 除非生存是主要精通信号,否则玩家处于安全/低技能状态时没有生存得分。
    • 没有无上限得分的永久安全状态。
    • 没有长期存活的已得分对象在未来仍有资格得分,除非其生命周期明显重置。
    • 对于连打修复,不能只验证总分;当机制可以重新得分同一对象时,还要比较每个唯一机会或唯一目标的得分。
  6. 添加或审查实现:

    • 引擎特定代码属于引擎技能或项目代码。
    • 本技能定义必须成立的内容,而不是确切的渲染或 API 调用。
  7. 验证:

    • 运行可用的单调策略和探索/熟练策略。
    • 在完整的全局比率检查之前,优先使用不变量特定的探针: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 — 用于连打、仅按住、空闲、安全等待、脉冲得分、连击和资源状态的可重用模式。