Комбинации клавиш при вводе на основе опросов

9

Итак, предположим, что у вас есть система ввода, основанная на опросе

void update()
{
    if( Keyboard[ 'A' ] )
        // A is down
}

Скажем, вы хотите иметь возможность распознавать комбинации клавиш длиной от 3 до 8 (например, вниз, вниз, вперед, вперед, A для хадо-кена)

Как бы вы лучше всего создали общую (легко модифицируемую / программируемую) комбинацию клавиш ввода при опросе ?

bobobobo
источник

Ответы:

7

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

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

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


источник
Немного вводит в заблуждение, что вы не можете сделать это без событий. Технически, события - это просто способ вызова метода или списка методов, когда выполняется какое-то условие. В этом смысле вам нужны события. Тем не менее, я могу применить слово events (неправильно) ко многим задачам программирования, которые соответствуют этому описанию. Я добавил ответ, который решает проблему так, как я это обычно делаю, и в нем не используется то, что большинство программистов считают «событиями».
Ольховский
2
Ваш код делает именно то, что я сказал, без событий, но игнорирует предостережения - что вы теряете внутрикадровое упорядочение. Когда вы получаете события, например, из цикла сообщений Win32, вы получаете их в порядке, более детализированном, чем глобальный цикл на кадр. Когда вы сами вычисляете дельты, вы теряете это. Для файтинга, в котором игроки могут вводить несколько частей комбо в одном кадре, вы должны знать, были ли они в порядке или не в порядке.
Справедливо. При условии, что мы проводим опрос на частоте 60 Гц, я сомневаюсь, что мы хотим различать нажатия, которые находятся на расстоянии менее 16 мс, что обеспечивает опрос на 60 Гц (при условии, что наши игроки - люди).
Ольховский
Мы делаем. Опытные игроки в файтинги могут ввести команду «хадукен / шорюкен» менее чем за три кадра, что означает, что мы должны различать их.
Тогда вам нужно опрашивать чаще, чем 60 Гц.
Ольховский
3

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

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

Обновите эти объекты, выполняя это при каждом опросе:

void update(){
    some_key_has_been_pressed = false;
    foreach(key in keys){
        if(previous_input[key].Down && current_input[key].Up){
            keys[key].up_timestamp = current_time();
        }

        if(current_input[key].Down){
            keys[key].down_timestamp = current_time();
            some_key_has_been_pressed = true;
        }
    }
}

Теперь вы можете сопоставлять свои комбинации с содержимым keys.

Иметь комбо-объект для каждого комбо и вызывать update () для каждого комбо-объекта при каждом опросе.

Метод update () объекта combo будет соответствовать шаблону combo, проверяя, выполнены ли все необходимые условия для combo в этом опросе. Т.е. все временные метки клавиш для комбо пока находятся в порядке, и никакая другая клавиша, которая могла бы нарушить комбо, не была нажата в этом кадре Для каждого выполненного условия увеличивайте счетчик в комбинированном объекте до следующего проверяемого условия. Когда все условия выполнены в комбо, вызовите метод, который должен выполнить комбо. Если some_key_has_been_pressed == trueтолько клавиша, которая является следующим условием для комбинации, не была нажата, тогда сбросьте счетчик удовлетворенных условий комбинации на 0.

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

Однако, для другого хорошего метода, посмотрите образец входной последовательности XNA , который написан на C #, и логика, вероятно, переносима на язык, который вы используете.

Ольховский
источник
Во втором операторе if, if(current_input[key].down){не хотите ли вы также проверить, был ли ключ в previous_inputсостоянии? Как написано, я думаю, что это будет постоянно обновлять удерживаемый ключ down_timestamp.
webdevkit
Постоянное обновление отметки времени простоя - это предполагаемое поведение. Это позволяет вам сравнивать up_timestamp и down_timestamp любого ключа, чтобы проверить, как долго он удерживался. Если вы постоянно удерживаете клавишу, вы не изменяете up_timestamp, поэтому (down_timestamp - up_timestamp) по-прежнему дает вам продолжительность, в течение которой она удерживалась.
Ольховский
благодарю за разъяснение. Я думал об обратном, где up_timestamp - down_timestamp - это длительность удерживаемого ключа.
webdevkit
1

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

AAAAAAAAAAAA
источник
2
Стек, из которого можно удалить нижний объект, на самом деле не является стеком;)
Ольховский,
Упорядоченная группа объектов данных, список может быть правильным термином.
аааааааааааа
deque;) O (1) enq / deq ftw.
Майкл Бартнетт
3
Упорядоченная последовательность, в которой объекты помещаются на один конец и удаляются с другого, наиболее правильно называется очередью .
1
Вы можете создать кольцевой буфер (BufferLength> = самая длинная команда) и записать в него ключи (Position = Number MOD BufferLength). Никакого добавления / удаления не потребуется вообще
Kromster