马尔默持续瞄准目标

4

Malmo是微软针对Minecraft开发的AI框架,包括游戏本身的mod和一个多平台框架,用于发送输入和接收有关世界的数据。

Minecraft的瞄准是圆柱形的。它存储在偏航(左右)和俯仰(上下)中,而不是完整的旋转四元数中。偏航从最左边的-180度开始,并在最右边包装到180度。俯仰从-90度直接朝上(天顶)到90度直接朝下(天底)。在我的代码中,我将它们存储为Vector2(重新创建以类似于XNA),其中X表示偏航,Y表示俯仰。

我正在努力创建一个连续瞄准目标算法,使得AI能够将其相机瞄准到给定的目标偏航和俯仰。因为唯一的方法是通过连续瞄准(设置偏航和俯仰速度,而不是值)来实现连续运动,所以我需要反复逐步增加偏航和俯仰方向。

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

在纸上,这似乎会使相机的瞄准直接朝向目标方向(不包括偏航包装)。然而,客户端的俯仰通常会直接超过目标方向,尽管更新方法发送了一个说要往相反方向走的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度,原因不明。我的计算是否正确?

2个回答

2

从我所看到的一切,数学优美且正确。如果在最低点处俯冲速度为负且不下降,则对我而言似乎是 Agent.sendCommand 没有正常执行其工作。

设置速度时,它是否保持在您设置的速度直到设置另一个速度?或者速度会相互叠加吗?如果俯仰角超出范围会发生什么?


1
我已经使用从控制台接收命令的AI进行了测试,当我运行pitch -1后跟着pitch 1(以及它们之间的各种值)时,响应是即时的。也许它有一个积压。我不完全确定,因为在循环运行之间设置500毫秒延迟进行测试会导致野蛮的不准确性和过度校正,几乎无法与此问题区分开来。 - tageta72

1

你可能早就解决了这个问题,但以防万一,这里有几个想法:

  1. In your Look() method, you have the following:

    Agent.sendCommand("turn " + velocity);
    Agent.sendCommand("pitch " + velocity);
    
我猜测你在简化代码以供SO使用时,重复使用velocity是一个打字错误?否则,这肯定可以解释这种行为。
  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在渲染时更新俯仰/偏航,而不是世界刻度时间,因此较慢的渲染速度会产生更大的振荡问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接