Как убрать дрожание с входа движения?

9

Я пишу мод Minecraft, который поддерживает ввод с Razer Hydra . Это пара контроллеров движения (по одному на каждую руку), которые обеспечивают невероятно точную информацию о положении и повороте.

Для целей этого вопроса, вращение правого контроллера по оси Y заставляет персонажа игрока смотреть влево или вправо (рыскание), а вращение по оси X заставляет игрока смотреть вверх и вниз (шаг).

Ввод от контроллера отображается непосредственно на заголовок персонажа. Если контроллер поворачивается на 30 градусов влево, персонаж поворачивается на 30 градусов влево.

Проблема в том, что вход "дрожит". Если я попытаюсь удерживать контроллер совершенно неподвижно, курс персонажа будет изменяться в очень маленьком конусе (возможно, на 1 градус).

Вероятно, это из-за моих дрожащих рук, так как данные контроллера кажутся точными.

Я попытался отфильтровать входные данные путем усреднения данных из последних X кадров, но это делает ввод неоправданным.

Мой вопрос: как я могу отфильтровать данные вращения, чтобы устранить дрожание без потери точности?

яблоки
источник
Вы рассматривали возможность игнорировать очень маленькие изменения в движении?
Филипп
1
Этот Razer Hyrdra интересен ... впервые слышу о нем, еще интереснее написать мод для Minecraft, чтобы дополнить его ... Мое предположение: так же, как пиксельные изображения, для того, чтобы "уменьшить шум", вам нужно размытие изображения ... По сути, вы можете либо жить с пиксельным изображением, либо жить с размытым изображением ... Я чувствую, что это тот же принцип, вам нужно выбрать то, что вы предпочитаете, шаткая игра или меньшая точность ... ... Это именно то, что я думаю ...
Люк Сан-Антонио Бялецки
1
Вы могли бы сделать лучшее из обоих. Всегда храните последние, скажем, 50 кадров. Но в среднем только по нескольким из них кадров, в зависимости от объема входного движения. Для больших движений используйте только последние 5 кадров, а для небольших (иначе дрожащих) движений используйте последние 30 кадров.
Данияр
@Sharethis Это интересная идея. Я могу в итоге реализовать нечто подобное. Джиттер не является проблемой, когда входной сигнал превышает определенный порог, поэтому я мог бы просто усреднить небольшие входные данные, чтобы удалить джиттер, и вообще ничего не усреднять для больших входных данных.
яблоки

Ответы:

8

Эта проблема с задержкой реагирования связана с практически всеми контроллерами движения, такими как Hydra, Wii Remote, Kinect или PlayStation Move.

Проблема заключается в следующем:

Когда поступает входной поток, вы принимаете решение покадрово о том, следует ли доверять входным данным; будут ли тенденции, которые вы видите сейчас, продолжаться в данных, которые вы получите через дюжину миллисекунд. Например, если в этом кадре произошел внезапный сдвиг вправо, вы не знаете, является ли это реальным битом входных данных (и поэтому вы должны воздействовать на него), или это просто джиттер (и поэтому вы должны его игнорировать). Что бы вы ни выбрали, если позже вы обнаружите, что ошиблись, вы либо позволили входному джиттеру войти в вашу игру (в первом случае), либо добавили лаг (в последнем случае).

Нет хорошего решения для этого. «Правильное» решение для определения того, является ли ввод действительным или дрожанием, требует знания того, что будет делать входной поток в будущем, а также того, что он делал в прошлом. Мы не можем сделать это в играх по понятным причинам. Так что нет, нет способа отфильтровать данные вращения, чтобы устранить джиттер без потери точности, в контексте игры, которая работает с входными данными в режиме реального времени.

Я видел, как крупный производитель рекомендовал разработчикам справиться с этой проблемой, заставляя игроков удерживать кнопку во время вращения элемента управления, чтобы в этот момент игра могла отключить свой код защиты от джиттера, чтобы он не отставал. (Я не рекомендую это, но это один из подходов).

Я видел несколько библиотек промежуточного программного обеспечения для ввода движения, которые решают эту проблему, вводя искусственную задержку ввода - есть буфер в четверть секунды, в который вводятся входные данные, и ваша игра слышит только о входе через четверть секунды, так что библиотека может сгладить дрожание для вас, зная, что происходит до и после «настоящего» с точки зрения игры. Это прекрасно работает, кроме четверти секунды задержки во всем. Но это один из способов решения проблемы, и он может выполнять потрясающую работу по точному представлению движения с удаленным джиттером за счет постоянного лага.

Но, не вдаваясь в эту крайность, мы все еще можем сделать что-то, чтобы значительно улучшить поведение, хотя мы знаем, что всегда будут «наихудшие сценарии», которые ведут себя неидеально.

Основная идея заключается в том, что мы действительно заботимся о джиттере, когда контроллер в основном неподвижен, и мы действительно заботимся о задержке, когда контроллер перемещается. Поэтому наша стратегия должна заключаться в том, чтобы попытаться справиться с такими вещами, чтобы у нас было отставание, когда контроллер находится в неподвижном состоянии, и дрожание, когда контроллер находится в движении.

Вот два возможных способа сделать это:

Одним из распространенных подходов является «заблокированная / разблокированная» система, в которой вы отслеживаете ориентацию устройства, и если она не меняется в течение короткого времени (полсекунды или около того), вы «блокируете» эту ориентацию, не предпринимая никаких действий. действие, основанное на заявленной ориентации устройства, до тех пор, пока оно не станет достаточно отличным для повторной разблокировки. Это может полностью подавить дрожание, основанное на ориентации, без введения задержки, когда ориентация активно меняется. Может быть, намек на задержку, прежде чем код решит, что ему нужно переключиться в «разблокированный» режим, но это будет намного лучше, чем иметь лаг везде.

Другой подход заключается в усреднении входных данных из кадров. Важным моментом здесь является усреднение только входных данных из фреймов, в которых входные данные были примерно одинаковыми. Это означало, что небольшие дрожания будут размываться и смягчаться, но большие изменения не размываются, потому что их данные не достаточно похож на данные из предыдущих кадров.

Есть и другие способы получить подобный эффект. Основная идея заключается в том, что в игре в реальном времени нельзя одновременно использовать джиттер и лаг, потому что для этого потребуются знания о будущем. Таким образом, вам нужно выбрать, когда смещать поведение вашего элемента управления к принятию джиттера, а когда - к смещению в сторону принятия лага, чтобы сделать общее восприятие как можно более неплохим.

Тревор Пауэлл
источник
Я только что опубликовал ответ, не могли бы вы высказать свое мнение о моем решении?
Яблоки
2

Я занимаюсь разработкой программного обеспечения, которое преобразует ввод движения в точный и точный ввод с помощью мыши, а также поддерживаю веб-сайт, который пытается помочь разработчикам самостоятельно реализовать столь же хорошие решения. Как правило, я советую избегать порогов передвижения, хотя это зависит от того, насколько игрокам нужна отзывчивость и точность, и я рад, что это работает для вас в вашей ситуации. Но здесь я предложу другое решение:

Я использую то, что называется Soft Tiered Smoothing . Идея состоит в том, что мы перенаправляем входные данные с помощью различных алгоритмов сглаживания в зависимости от текущей величины скорости гироскопа (на практике один из этих алгоритмов сглаживания просто "не сглаживает"). Это «многоуровневая» часть. «Мягкая» часть заключается в том, что мы можем фактически плавно разделить входные данные между различными алгоритмами сглаживания в зависимости от того, как он сравнивается с 2 порогами.

Он правильно сохраняет смещение и не добавляет никаких задержек быстрым движениям.

На практике у вас есть два порога. Когда величина входной скорости меньше нижнего порога, мы используем простой алгоритм сглаживания (среднее по нескольким кадрам). Когда он превышает другой порог, мы вообще не используем алгоритм сглаживания. Но в этом случае мы все еще передаем нули алгоритму сглаживания нижнего порога.

Когда входная скорость находится между двумя порогами, мы разделяем входные данные между двумя алгоритмами соответственно.

Вот фрагмент из статьи выше:

GetSoftTieredSmoothedInput(Vec2 input, float threshold1, float threshold2) {
    // this will be length(input) for vectors
    float inputMagnitude = Abs(input);

    float directWeight = (inputMagnitude - threshold1) / (threshold2 - threshold1);
    directWeight = clamp(directWeight, 0, 1);

    return GetDirectInput(input * directWeight) +
        GetSmoothedInput(input * (1.0 - directWeight));
}

GetDirectInput просто возвращает то, что ему дано, но это показывает, что здесь можно использовать другой алгоритм сглаживания. GetSmoothedInput принимает скорость и возвращает сглаженную скорость.

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

В то время как другие ответы правы, говоря, что трудно распознать джиттер в момент получения входного сигнала, также верно, что джиттер почти всегда имеет очень низкую скорость, а входная задержка, связанная с сглаживанием, гораздо менее заметна для низкоскоростных входов ,

Как упоминается в статье, это используется в нескольких местах моего инструмента с открытым исходным кодом JoyShockMapper , преобразователя ввода, который превращает гироскопический ввод в ввод мыши. Даже для людей, использующих другие инструменты переназначения, такие как Steam или reWASD, некоторые используют JoyShockMapper одновременно только для своих гироскопов.

Этот ответ предполагает, что входные данные даны в угловой скорости (что характерно для контроллеров, которые имеют элементы управления движением), а не в абсолютной ориентации (что звучит так, как дает вам Razer Hydra). С абсолютной ориентацией, я надеюсь, что вы можете использовать разницу между текущей ориентацией и ранее сообщенной ориентацией, чтобы получить скорость, но я не знаю точно, будет ли она работать так же, как и с контроллерами, которые самостоятельно сообщают угловую скорость ,

Распространенным решением сглаживания, когда вы имеете дело с абсолютной позицией / ориентацией, а не скоростями, является интерполяция в направлении вашей целевой ориентации во времени - это очень полезно описано в этой статье Gamasutra., Это может работать и с Soft Tiered Smoothing. Вы рассчитаете величину скорости, используя разницу между этим входом и предыдущим сообщенным входом. Вы примените разницу в ориентации между этим и последним кадрами, умноженную на значение "directWeight", рассчитанное во фрагменте выше. Последний шаг - добавление сглаженного ввода, но из-за того, как работает интерполированное сглаживание ориентации, вы просто применяете интерполированное изменение ориентации в соответствии с нормой - ему вообще не нужно учитывать «directWeight». Просто установите целевую ориентацию (это то, к чему вы интерполируете с помощью сглаживания, описанного в этой статье Gamasutra) на любую ориентацию, которую вы получаете от устройства, и интерполируйте свою ориентацию к нему, как описано в этой статье.

Jibb Smart
источник
1

Мне кажется странным отвечать на мой вопрос, но я думаю, что нашел свое решение.

//Pseudo-Java

update()
{
    //deltaYaw is the change in yaw of the controller since last update
    //yawBuffer is initialized to zero, and only modified here
    //coneAngle is the stabilizing cone

    deltaYaw = getData().yaw;

    yawBuffer += deltaYaw;
    if (abs(yawBuffer) >= coneAngle)
    {
        player.yaw += (abs(yawBuffer)-coneAngle) * sign(yawBuffer);
        yawBuffer = coneAngle * sign(yawBuffer);
    }
}

Вместо прямого изменения курса игрока, я просто «толкаю» конус с заданным углом (в моем случае 2,5 градуса). Я сделал небольшую демонстрацию HTML5 этой техники.

Как только вы начинаете толкать конус, появляется нулевая задержка и полная точность. Однако, если вы толкаете конус влево, а затем хотите прицелиться вправо, вы должны пройти через весь угол конуса, чтобы увидеть эффект.

Таким образом, он решает проблемы временной задержки и ужасного сглаживания, но вводит новую проблему порога движения. Однако, если стабилизирующий конус настроен правильно, пороговое значение незаметно.

яблоки
источник
Это кажется еще одним разумным способом сделать это. Раньше были (дорогие) видеокамеры, которые стабилизировали свое изображение с помощью этого подхода; это дает вам точность, когда вы продолжаете движение в одном направлении, но имеет задержку при смене направления. Если это хорошо работает для вашей игры, то обязательно сделайте это. Вы не найдете единственного решения, которое лучше всего подойдет для каждой игры; Это всегда компромисс, в котором вам нужно взвесить недостатки каждого подхода в зависимости от конкретных потребностей конкретной игры, которую вы делаете. :)
Тревор Пауэлл