2016-07-16 2 views
4

Malmo - это основа AI для Microsoft для Minecraft, состоящая из мод для самой игры и многоплатформенной платформы для отправки входов и получения данных о мире.Непрерывная цель для цели в Мальме

Целевое назначение Minecraft является цилиндрическим. Он хранится в рыскании (влево и вправо) и шаге (вверх и вниз) вместо полного кватерниона поворота. Yaw идет от -180 градусов в крайнем левом углу и обертывается до 180 в крайнем правом углу. Шаг идет от -90, указывая прямо вверх (зенит) на 90 вниз (надир). В моем коде я храню их как Vector2 (воссозданный для работы так же, как XNA), так что X представляет собой Yaw, а Y представляет собой Pitch.

У меня возникли проблемы с созданием непрерывного прицеливания к целевому алгоритму, чтобы ИИ мог направить свою камеру на заданный рывок и шаг. Поскольку единственный способ сделать это, продолжая непрерывное движение, - это непрерывное прицеливание (установка рыскания и шаг скорости, а не значения), мне нужно многократно увеличивать рыскание и подачу в направлении цели.

Я делаю это, сохраняя целевое направление в нулевом свойстве. Если свойство имеет значение NULL, это означает, что нельзя изменить цель. В противном случае вычитайте расстояние между сохраненным значением (цель) и текущей целью (поставляемой через параметр) каждый раз при вызове метода обновления. Затем он масштабирует разницу, так что либо рыскание, либо шаг равно 1 (максимальная скорость), а другая правильная пропорция для этого. Эта пропорциональная скорость Vector2 делится на ее рыскание и шаг, а затем отправляется клиенту с помощью команд turn и pitch. Как только в пределах 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 и по какой-то причине не исправляется. Правильно ли я делаю математику?

ответ

1

Из всего, что я вижу, математика элегантна и проверяется. Если скорость подачи отрицательная на надире и она не опускается вниз, для меня это выглядит как Agent.sendCommand не выполняет свою работу должным образом.

При настройке скорости оно остается на той скорости, которую вы установили, до тех пор, пока не будет установлен другой режим? Или скорость добавляется друг к другу? Что произойдет, если поле вне поля?

+1

Я тестировал это с помощью ИИ, который принимает команды с консоли, и когда я запускаю «шаг -1», за которым следует «шаг 1» (и различные значения между ними), ответ мгновенно. Возможно, у него есть отставание. Я не совсем уверен, так как тестирование с задержкой в ​​500 мс между циклами приводит к диким неточностям и чрезмерной коррекции, что почти невозможно отличить от этой проблемы. –

1

вероятно Вам удалось исправить это давным-давно, но только в том случае, вот несколько мыслей:

  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 обновляет подачу/рыскание при времени тика рендеринга, а не во всем мире, так что медленные скорости рендеринга создадут большие проблемы с колебаниями.

Смежные вопросы