Управление системой ввода с клавиатуры

13

Примечание: я должен опрашивать, а не делать обратные вызовы из-за ограничений API (SFML). Я также извиняюсь за отсутствие «приличного» названия.

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

Обработка ввода

Я говорю после того, как вы зарегистрировали тот факт, что, например, была нажата клавиша «А», и как это сделать.

Я видел массив всей клавиатуры, что-то вроде:

bool keyboard[256]; //And each input loop check the state of every key on the keyboard

Но это кажется неэффективным. Например, вы не только связываете клавишу «А» с «игроком, двигающимся влево», но и проверяет каждую клавишу 30–60 раз в секунду.

Затем я попробовал другую систему, которая просто искала нужные ключи.

std::map< unsigned char, Key> keyMap; //Key stores the keycode, and whether it's been pressed. Then, I declare a load of const unsigned char called 'Quit' or 'PlayerLeft'.
input->BindKey(Keys::PlayerLeft, KeyCode::A); //so now you can check if PlayerLeft, rather than if A.

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

Затем у меня есть вторая проблема, которую я не могу придумать для хорошего решения:

Отправка ввода

Теперь я знаю, что была нажата клавиша A или что playerLeft - это правда. Но как мне идти отсюда?

Я думал о простой проверке. if(input->IsKeyDown(Key::PlayerLeft) { player.MoveLeft(); }
Это сильно связывает ввод с сущностями, и я нахожу это довольно грязным. Я бы предпочел, чтобы игрок обрабатывал свое собственное движение, когда оно обновляется. Я думал, что какая-то система событий могла бы работать, но я не знаю, как с этим справиться. (Я слышал, что сигналы и слоты были хороши для такой работы, но, по-видимому, они очень медленные, и я не вижу, как они подходят).

Благодарю.

Коммунистическая утка
источник

Ответы:

7

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

Обычно игровые объекты имеют компонент обработки ввода, который отвечает за регистрацию себя во входном классе и предоставление методов для обработки интересующих его событий. Для перемещения у меня есть компонент перемещения ввода, который имеет метод move (x, y). Он регистрируется в системе ввода и получает уведомление о событиях движения (где x и y будут -1 и 0, чтобы обозначить перемещение влево), тогда компонент движителя изменяет свои координаты родительского игрового объекта на основе направления движения и скорости объекта.

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

Firas Assaad
источник
Это определенно меня интересует; будет ли входной компонент регистрироваться для определенных событий (что-то вроде playerInput регистрирует 'left', 'right' и т. д.) или каждый зарегистрированный входной компонент получает все сообщения?
Коммунистическая утка
Это действительно зависит от ваших требований и от того, как вы хотите их реализовать. В моем случае слушатели регистрируют себя для определенных событий и реализуют соответствующий интерфейс обработчика ввода. При получении каждого входного компонента все сообщения должны работать, но формат сообщения должен быть более общим, чем у меня.
Firas Assaad
6

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

Мое предложение:

Держите ваш массив состояний ключей. Разве нет вызова API для получения всего состояния клавиатуры одновременно? (Большая часть моей работы над консолями, и даже в Windows для подключенного контроллера вы получаете сразу все состояние контроллера.) Ваши состояния клавиш представляют собой «жесткую» карту клавиш клавиатуры. Непереведенный вариант лучше, но, тем не менее, его проще всего получить из API.

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

Затем создайте метод для сопоставления ключей с действиями. Вы захотите сделать это пару раз. Один раз для пользовательского интерфейса (и позаботьтесь о том, чтобы запросить сопоставления Windows, потому что вы не можете предположить, что скан-код, когда вы нажимаете «А», будет давать А на компьютере пользователя). Еще для каждого внутриигрового «режима». Это сопоставления ключевых кодов с действиями. Вы можете сделать это несколькими способами, один из них - сохранить перечисление, используемое принимающей системой, другой - указатель на функцию, указывающий функцию, вызываемую при изменении состояния. Может быть, даже гибрид из двух, в зависимости от ваших целей и потребностей. С этими "режимами" вы можете помещать их в стек режимов контроллера и вставлять их с флагами, которые позволяют игнорируемому вводу продолжать работу в стеке или что-то еще.

Наконец, обработайте эти ключевые действия как-нибудь. Для движения вы можете сделать что-то хитрое, переведите «W» вниз, чтобы обозначить «двигаться вперед», но это не обязательно должно быть двоичным решением; у вас может быть «W is down» означает «увеличить скорость на X до максимума Y», а «W up» означает «уменьшить скорость на Z, пока она не достигнет нуля». В общем, вы хотите, чтобы ваш «интерфейс управления контроллером в игровых системах» был довольно узким; сделать все ключевые переводы в одном месте, а затем использовать результаты этого везде. Это в отличие от прямой проверки, чтобы увидеть, нажимается ли пробел где-нибудь случайно в коде, потому что, если он находится в каком-то случайном месте, он, вероятно, попадет в случайное время, когда вы этого не хотите, и вы просто не не хочу иметь дело с этим ...

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

штрих-кот-бэнг
источник
Не кажется ли вам грязным привязывать события к графическим фреймам? Я думаю, что это должно быть отделено.
Джо Со
Я имею в виду, я понимаю, что почти во всех случаях проигрыватель будет медленнее вводить кнопки, чем видеокарта, генерирующая кадры, но на самом деле они должны быть независимыми, и фактически они предназначены для других типов событий, например, собранных из сети. ,
Джо Со
В самом деле; нет причины, по которой опрос клавиатуры (или другого устройства ввода) не может выполняться со скоростью, отличной от дисплея. Действительно, если вы опрашиваете входы на частоте 100 Гц, тогда вы можете обеспечить действительно согласованный пользовательский контроль независимо от частоты обновления видеооборудования. Вам нужно быть немного более умным в отношении того, как вы обрабатываете свои данные (в отношении фиксации и т. Д.), Но это упростит согласованность таких вещей, как жесты.
Даш-Том-
3

В SFML у вас есть клавиши в том порядке, в котором они были нажаты с типом события KeyPressed. Вы обрабатываете каждый ключ некоторое время (getNextEvent ()).

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

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

Если вы хотите декоррелировать входные данные и сущности, вы можете сделать свой вход State (RunKey = true, FireKey = false, ...) и отправить его игроку, как вы отправляете любое другое событие своим AI.

Calvin1602
источник
Я не обрабатываю события клавиатуры с помощью sf :: Event, а sf :: Input, потому что это обнаружение в реальном времени IIRC. Я старался сохранить его как можно более независимым от API. : P (вопрос то есть)
коммунистическая утка
1
Но я думаю, что Calvin1602 говорит о том, что ваше первоначальное утверждение в вопросе «я должен опрашивать, а не делать обратные вызовы из-за ограничений API (SFML)» не соответствует действительности; у вас есть события, поэтому вы можете выполнить обратный вызов при обработке события.
Kylotan
3

Правильное решение часто является комбинацией двух методов, которые вы обсуждаете:

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

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

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

Бен Зейглер
источник
1

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

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

http://www.gamedev.net/blog/355/entry-2250186-designing-a-robust-input-handling-system-for-games/

coeing
источник