持续瞄准马尔默的目标

本文关键字:目标 马尔默 | 更新日期: 2023-09-27 17:57:07

Malmo是Microsoft的MinecraftAI框架,由游戏本身的mod和用于发送输入和接收有关世界的数据的多平台框架组成。

Minecraft的目标是圆柱形的。它存储在偏航(左右)和俯仰(向上和向下)中,而不是全旋转四元数。偏航从最左侧的 -180 度到最右侧的 180 度。音高从 -90 直接指向上方(天顶)到 90 直接向下(最低点)。在我的代码中,我将它们存储为Vector2(重新创建以非常像XNA的工作方式),这样X表示偏航,Y表示Pitch。

我在创建连续瞄准目标算法时遇到问题,以便 AI 能够将其相机对准给定的目标偏航和俯仰。因为在仍然允许连续移动的同时做到这一点的唯一方法是通过连续瞄准(设置偏航和俯仰速度,而不是值),我需要反复增加偏航并向目标方向俯仰。

我通过将目标方向存储在可为空的属性中来做到这一点。如果属性为 null,则表示不更改目标。否则,每次调用更新方法时,请减去存储值(目标目标)和当前目标(通过参数提供)之间的距离。然后,它缩放差异,使偏航或俯仰为 1(最大速度),另一个比例正确。这个比例Vector2速度被分成偏航和俯仰,然后通过turnpitch命令发送给客户端。一旦在目标方向的 10 度范围内,目标将设置为 null。

在纸面上,这似乎会使相机的目标直接朝向目标方向(不包括偏航环绕)。但是,客户端的音高通常直接超过目标方向,尽管 update 方法发送了一个 pitch 命令,说要朝相反的方向走。这意味着音高不知何故"卡"在天顶和最低点,但会自行修复并"转身"并在几秒钟后卡在对面的极点。每次转身前卡住的时间似乎都呈指数级(或可能是二次级)增加。

这是我的目标更新方法的源代码:

public void UpdateAim(Observations obs)
{
    // AimTarget is a Vector2? property
    if (AimTarget == null || obs == null)
    {
        return;
    }
    if (AimTarget.Value.Distance(obs.Aim) < AIM_CLOSE_ENOUGH) // < 10
    {
        Logger.LogInfo("Reached {0}", AimTarget.Value);
        Look(Vector2.Zero);
        AimTarget = null;
        return;
    }
    double deltaYaw = AimTarget.Value.X - obs.Aim.X;
    double deltaPitch = AimTarget.Value.Y - obs.Aim.Y;
    // These two are stored as private Vector2 fields
    deltaAim = new Vector2(deltaYaw, deltaPitch);
    scaledAim = deltaAim / Math.Max(Math.Abs(deltaAim.X), Math.Abs(deltaAim.Y));
    Look(scaledAim); // sets continuous aim velocity
}

Look(Vector2)的(简化)源代码:

public void Look(Vector2 direction)
{
    // Agent is an AgentHost property
    Agent.sendCommand("turn " + velocity);
    Agent.sendCommand("pitch " + velocity);
}

在主游戏循环中,UpdateAim()每秒调用 20 次(尽管我尝试过每秒高达 50 次,低至 5 次)。

在我最后一次运行 AI(卡在最低点)结束时,我的瞄准调试数据如下所示:

// Format: (yaw, pitch)
Target Aim: (134.75, 27.90)
Actual In-Game Aim: (-6.50, 90.00) // Lines up with MC's debug screen
Delta Aim :  (145.17, -62.10) // Total degrees needed to go in yaw and pitch
Scaled Aim Velocity: (1.00, -0.43)

缩放的瞄准速度是提供给Look()的。如您所见,俯仰速度是负的,但实际游戏内的目标仍然是 90,并且由于某种原因没有得到纠正。我算对了吗?

持续瞄准马尔默的目标

从我所看到的一切来看,数学是优雅的,可以检查出来。如果俯仰速度在最低点为负并且它没有向下移动,对我来说,它看起来像 Agent.sendCommand 没有正常工作。

设置

速度时,它是否保持您设置的速度,直到设置另一个速度?还是速度相互增加?如果球场超出界外会怎样?

您可能很久以前就设法解决此问题,但以防万一,这里有一些想法:

  1. Look()方法中,您具有以下各项:

    Agent.sendCommand("turn " + velocity);
    Agent.sendCommand("pitch " + velocity);
    

我假设重复使用velocity是您在简化代码以供 SO 使用时犯的错别字?否则,这肯定会解释这种行为。

  1. 您的缩放代码很有趣 - 有什么理由需要保持偏航速度与增量速度的比率相同吗? 即你真的需要Math.Max(Math.Abs(deltaAim.X), Math.Abs(deltaAim.Y))术语吗?这两个运动(偏航和俯仰)是完全独立的,所以没有理由依赖地缩放它们,除非它以某种我没有发现的聪明方式提高了性能。

  2. 您可能需要考虑振荡/阻尼。想象一下你的偏航是正确的(deltaYaw == 0)。您的缩放意味着您的俯仰增量速度将始终处于最大值(1 或 -1,具体取决于方向)。换句话说,即使增量音高仅为 0.0001,您仍将以最大速度进行调整,并且会显着超调。(显然,使用 AIM_CLOSE_ENOUGH 将有助于解决这个问题,但我认为仍然有可能获得振荡 - 特别是如果你有一个高turnSpeedDegs集 - 见 http://microsoft.github.io/malmo/0.17.0/Schemas/MissionHandlers.html#element_ContinuousMovementCommands)

有关此类工作的示例,请查看cart_test.py示例 - https://github.com/Microsoft/malmo/blob/master/Malmo/samples/Python_examples/cart_test.py

下面是相关的代码片段。 yaw_to_mob是目标偏航,yaw是玩家当前的偏航。

# Find shortest angular distance between the two yaws, preserving sign:
deltaYaw = yaw_to_mob - yaw
while deltaYaw < -180:
    deltaYaw += 360;
while deltaYaw > 180:
    deltaYaw -= 360;
deltaYaw /= 180.0;
# And turn:
agent_host.sendCommand("turn " + str(deltaYaw))

如果你想看看振荡问题的实际情况,顺便说一下,看看 MazeRunner.py 样本(与cart_test.py相同的位置),并将turnSpeedDegs增加两到三倍。Minecraft 在渲染刻度时更新俯仰/偏航,而不是世界刻度时间,因此较慢的渲染速度会产生更大的振荡问题。