跳转至

实现自适应误差校正

但如果你的治疗能力提高了呢?如果角色可以升级、应用技能点或提高最大生命值,它能治疗的生命值可能会相应改变。例如,如果你在 10 级角色上启动机器人,让它运行到角色 40 级,你的治疗代码需要适应。40 级角色像 10 级时那样治疗,要么会严重过量治疗,要么会在同级游戏敌人面前迅速死亡。

要处理这个场景,机器人需要不断更新治疗阈值以反映观察到的治疗量。清单 11-4 展示了如何修改清单 11-1 中的强治疗条件函数来做到这一点。

curDef->condition = [](GameSensors* sensors) -> 
bool {
    static float healAt = 50;
  if (sensors->detectedStrongHeal()) {
        auto newHealAt = 100 - 
sensors->getStrongHealIncrease();
      sensors->clearStrongHealInfo();
    }
    return sensors->getHealthPercent() > healAt;
};

清单 11-4:调整强治疗条件代码

与修改后的弱治疗函数一样,治疗阈值已移到名为 healAt 的静态变量中,但这一次逻辑略有不同。由于学习必须持续进行,没有变量跟踪机器人是否已经学会其真实治疗能力。代码只检查传感器自上次调用以来是否看到强治疗事件①。如果是则用 healAt 和 newHealAt 的平均值替换 healAt,并调用函数清除传感器中与强治疗相关的信息③。

清除传感器其实非常重要,因为它防止代码持续针对同一次强治疗施放的反馈更新 healAt。还要注意,这个函数不是把 healAt 更新为完美值,而是向观察到的理想值滑动。这种行为使新函数适合实际治疗量存在一定随机性的情况。如果你的机器人需要更快地滑向新值,你可以把②处的行改为这样:

healAt = (healAt + newHealAt * 2) / 3.00f;

这段更新 healAt 的代码使用偏向 newHealAt 值的平均值。不过,使用这种方法有几个要点需要考虑。首先,过量治疗时会发生什么?在一些游戏中,当你治疗到满血时,传感器可能只能检测到你实际治疗了多少。在其他游戏中,传感器可能能检测实际治疗量。换句话说,如果你在 85% 生命值时施放 30%的强治疗,你的传感器看到的是 30% 还是 15% 的治疗?如果答案是 30%,你就没问题。如果答案是 15%,你的代码需要向下调整的方法。

一种调整方式是当传感器看到把你带到满血的治疗时递减 healAt,像这样:

curDef->condition = [](GameSensors* sensors) -> 
bool {
    static float healAt = 50;
      if (sensors->getStrongHealMaxed()) {
          healAt--;
      } else {
          auto newHealAt = 100 - 
sensors->getStrongHealIncrease();
          healAt = (healAt + newHealAt) / 2.00f;
      }
      sensors->clearStrongHealInfo();
    }
    return sensors->getHealthPercent() > healAt;
};

这段代码与清单 11-4 几乎相同,但它添加了 if() 子句,在检测到最大治疗时递减 healAt①。否则,函数应该像清单 11-4 那样表现。

治疗是一个简单的例子,但这段代码很好地展示了如何用误差校正动态改进机器人的行为。另一个高级用例是调整技能射击以应对敌人的移动模式。每个玩家躲避技能射击有模式,所以如果你的传感器能够测量敌人躲避技能射击时移动的方向和距离,你的控制器代码就可以调整机器人最初发射技能射击的位置。同样场景下,学习还可以帮助机器人考虑游戏服务器延迟、角色移动速度等差异。

使用误差校正时,注意如果你的状态定义有某种内部记账方式(而不是静态变量),你的代码会更干净、更可移植。此外,为避免弄乱状态定义,我建议把误差校正逻辑封装在需要时容易调用的外部模块中。