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

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

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

Решение № 1: Используйте события keyUp / keyDown

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

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

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

Решение №2. Использование событий Quarts

См. Справочник по Quartz Event Services. Вроде бы достаточно низкого уровня.

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

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

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

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

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

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

Вопросы:

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

ОБНОВИТЬ:

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

6 ответов

Решение

Мне повезло с № 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 и Carbon Events.

Я опаздываю на эту вечеринку, но вот мое решение для игры для 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)
    }
}

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

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

Хорошо, так что я действительно опаздываю на вечеринку, но я думаю, что мой просто прямо к делу и напоминает решение мистера Говарда выше. Имейте в виду, что этот вход используется для управления камерой в игре 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") }
}

Я нашел очень хороший пример использования Quartz Event Taps.

Однако проблемы заключаются в следующем:

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

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

В polkit (Obj-C toolkit) также есть несколько классов устройств, в том числе...

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

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

Посмотрите на ControllerMate и Manymouse.

http://icculus.org/manymouse/

http://www.orderedbytes.com/controllermate/

Другие вопросы по тегам