Как игры Mac OS X могут принимать низкоуровневые события ввода клавиатуры?

Для игр необходим низкоуровневый доступ к клавиатурному входу. В Windows есть DirectInput. Но какую технологию используют разработчики игр Mac OS X?

Очевидно, что достаточно игр Mac, которые получают ввод с клавиатуры только вправо, без недостатков общеизвестных решений:

Решение # 1: Использовать события keyUp/keyDown

-(void)keyUp:(NSEvent*)event;
-(void)keyDown:(NSEvent*)event;

Недопустимый недостаток: события keyDown повторяются в соответствии с настройками системных настроек для "частоты повторения клавиш" и "задержки повторения клавиш". Это вызывает начальное событие keyDown, за которым следует пауза, прежде чем она начнет повторяться со скоростью, заданной параметром настройки системы. Это решение не может использоваться для непрерывных ключевых событий.

Интересно, можно ли отключить поведение повторения клавиш? Я полагаю, что вы могли бы прочитать первый ключ KeyDown, а затем запомните состояние "key with keyCode x is down" в своем классе до тех пор, пока не будет получено событие keyUp для этого ключа, тем самым минуя проблемы задержки повторения и повторения.

Решение # 2: Использовать кратки событий для проектов Quarts

См. Кварцевая служба служб событий. Это кажется достаточно низким уровнем.

Недопустимый недостаток: требуется, чтобы вспомогательные устройства включались в системные настройки в разделе Universal Access - Keyboard. Хотя это может быть включено по умолчанию, нет гарантии, что он может быть отключен в некоторых системах.

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

Решение # 3: Carbon Events/IOKit HID

Carbon Event Manager Reference отмечен как устаревший и не должен использоваться для новой разработки.

Недопустимый недостаток: никто не знает, как долго события Carbon будут поддерживаться в будущих версиях Mac OS.

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

Вопросы:

Какую технологию используют разработчики игр для Mac OS X для приема событий ввода с низким уровнем клавиатуры? Если они используют одну из вышеперечисленных технологий, как они справляются с недостатками, о которых я упоминал?

UPDATE:

В конце концов я перешел на использование регулярных сообщений NSEvent и завернул их в опрятный API для опроса состояний клавиатуры.

Ответ 1

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

Один быстрый момент перед тем, как мы погрузимся, хотя Carbon и IOKit HID - две разные вещи. Углерод может уйти в какой-то момент. Но IOKit HID здесь, чтобы остаться, и просто получил хорошую подтяжку лица в 10,5.

Для полного примера того, как все это объединяется, взгляните на https://github.com/OpenEmu/OpenEmu/blob/master/OpenEmu/OEHIDManager.m. Это небольшая часть головоломки, так как там есть и другие файлы.

Документацию о том, что вы хотите сделать, можно найти http://developer.apple.com/library/mac/#documentation/DeviceDrivers/Conceptual/HID/new_api_10_5/tn2187.html

Опять же, это не исчезнет в ближайшее время и полностью отделено от Carbon and Carbon Events.

Ответ 2

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

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

var keysDown = Set<UInt16>()

override func keyDown(e: NSEvent) {
    keysDown.insert(e.keyCode)
}

override func keyUp(e: NSEvent) {
    keysDown.remove(e.keyCode)
}

Затем другие части системы могут определить, опущен ли какой-то конкретный ключ:

if (keysDown.contains(49)) {
    // space bar is down
}

Или прокрутите все нажатые клавиши

for char in keysDown {

    switch char {

    case 49:
        // space
        player.jump()
    case 126:
        // up
        player.moveForward()
    case 124:
        // right
        player.turnRight()

        //   ... etc ...

    default:
        // during development I have this line so I can easily see which keys have which codes
        print(char)
    }
}

Note keysDown - это Swift Set, поэтому вам не нужно беспокоиться о дублировании или заказе. Ключ находится либо в наборе, либо нет.

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

Ответ 3

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

let moveRight: SKAction = SKAction.repeatForever(SKAction.moveBy(x: -5000, y: 0, duration: 1.5))
...

override func keyDown(with event: NSEvent) {
    camera!.constraints = [] // remove constraints usually added by a cameraComponent.

    let ekc = event.keyCode
    if ekc == 123 { camera!.run(moveLeft, withKey: "moveLeft") }
    if ekc == 124 { camera!.run(moveRight, withKey: "moveRight") }
    if ekc == 126 { camera!.run(moveUp, withKey: "moveUp") }
    if ekc == 125 { camera!.run(moveDown, withKey: "moveDown") }

    print("keyDown: \(event.characters!) keyCode: \(event.keyCode)")
}


override func keyUp(with event: NSEvent) {
    let ekc = event.keyCode
    if ekc == 123 { camera!.removeAction(forKey: "moveLeft") }
    if ekc == 124 { camera!.removeAction(forKey: "moveRight") }
    if ekc == 126 { camera!.removeAction(forKey: "moveUp") }
    if ekc == 125 { camera!.removeAction(forKey: "moveDown") }
}

Ответ 5

Есть также некоторые классы устройств в polkit (Obj-C toolkit), включая...

  • HIDController для использования USB-HID-устройств
  • AppleRemote для использования Apple Remote
  • MidiController для использования устройств Midi
  • OSCController для использования устройств, совместимых с OSC, через UDP
  • SerialPort использовать любое устройство последовательного порта
  • SC2004LCDModule для использования SC 2004 от siliconcraft.net

http://code.google.com/p/polkit/

Ответ 6

Я нашел очень хороший пример с помощью кранов событий Quartz.

однако проблемы:

  • для определенных типов событий пользователю необходимо запустить приложение в привилегии root. например, для перехвата событий keydown и keyup требуется root.

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