Правильный способ абстрагирования контроллера XBox

12

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

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

За кулисами класс, который обрабатывает контроллеры, зависит от состояния кнопки опроса.

Я изначально пробовал что-то связать:

Event ButtonPressed() as ButtonEnum

где ButtonEnumбыл ButtonRed, ButtonStartи т.д ...

Это немного ограничено в том, что он поддерживает только нажатия кнопок, а не удержания / паттерны (дважды нажмите и т. Д.)

Следующая идея состояла в том, чтобы просто показать состояние кнопки приложению, например

Property RedPressed as Boolean
Property StartPressed as Boolean
Property Thumb1XAxis as Double

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

Я подумал добавить несколько событий, например:

Event ButtonPressed(Button as ButtonEnum)
Event ButtonPressedTwice(Button as ButtonEnum)
Event ButtonHeldStart(Button as ButtonEnum)
Event ButtonHeldEnd(Button as ButtonEnum)

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

Может кто-нибудь, пожалуйста, укажите мне на «правильный» способ обработки входов от контроллеров.

NB: я использую SlimDX внутри класса, который реализует интерфейс. Это позволяет мне очень легко читать состояние. Любые альтернативы, которые решат мою проблему, также приветствуются

основной
источник

Ответы:

21

Не существует ни одного идеального отображения, которое дало бы вам абстракцию, специфичную для платформы, потому что очевидно, что большинство идентификаторов, имеющих смысл для контроллера 360, не подходят для контроллера PlayStation (A вместо X, B вместо Circle). И, конечно, контроллер Wii - это совсем другое.

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

Средний слой - это фактическое сопоставление элементов управления от реальных кнопок до игровых концепций (например, A -> Jump). Мы называем их импульсами вместо кнопок, потому что они больше не привязаны к определенному типу контроллера. Именно на этом уровне вы можете переназначить элементы управления (либо во время разработки, либо во время выполнения по желанию пользователя). Каждая платформа имеет свое собственное отображение элементов управления на виртуальные импульсы . Вы не можете и не должны пытаться уйти от этого. Каждый контроллер уникален и требует своего отображения. Кнопки могут отображаться на более чем один импульс (в зависимости от режима игры), и более одной кнопки могут отображаться на один и тот же импульс (например, A и X оба ускоряются, B и Y замедляются). Отображение определяет все это,

Верхний слой - игровой слой. Он принимает импульсы, и ему все равно, как они возникли. Может быть, они пришли от контроллера, или записи контроллера, или, может быть, они пришли от ИИ. На этом уровне вам все равно. Вас волнует, что есть новый импульс Прыжка, или что импульс Ускорения продолжился, или что импульс Пикирования имеет значение 0,35 в этом тике.

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

MrCranky
источник
Это похоже на очень чистый и элегантный подход. Спасибо!
Основное
1
Очень хорошо. Гораздо лучше, чем у меня: P
Джордаан Милонас
3
Очень хорошая абстракция. Но будьте осторожны при реализации: не создавайте и не уничтожайте новый импульсный объект для каждого действия пользователя. Используйте пул. Сборщик мусора поблагодарит вас.
Грега г
Абсолютно. Статический массив, рассчитанный на максимальное количество одновременных импульсов, почти всегда является наилучшим вариантом; так как почти всегда вы хотите, чтобы в каждый момент времени был активен только один экземпляр каждого импульса. Чаще всего в этом массиве всего несколько элементов, поэтому его можно быстро повторить.
MrCranky
@ Грега спасибо вам обоим - я не думал об этом.
Basic
1

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

Нижний уровень может рассматриваться как уровень «на основе опроса» и отображает текущее состояние кнопки. Один из таких интерфейсов может просто иметь 2 функции, GetAnalogState(InputIdentifier)и GetDigitalState(InputIdentifier)где InputIdentifierэто перечисляемое значение, представляющее, какую кнопку, триггер или стик вы проверяете. (Запуск GetAnalogState для кнопки вернул бы 1.0 или 0.0, а запуск GetDigitalState для аналогового джойстика вернул бы true, если превышен заданный порог, или false в противном случае).

Затем второй уровень будет использовать нижний уровень для генерации событий при изменении состояния и позволять элементам регистрироваться для обратных вызовов (C # Events великолепны). Эти обратные вызовы будут включать в себя события для прессы, выпуска, касания, длинного склада и т. Д. Для аналоговой флешки вы можете включить жесты, например, QCF для файтинга. Количество выставленных событий будет зависеть от того, насколько детализировано событие, которое вы хотите отправить. Вы можете просто запустить простую, ButtonStateChanged(InputIdentifier)если вы хотите обрабатывать все остальное в отдельной логике.

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

Джордаан Милонас
источник