Mac OS X游戏如何接收低级键盘输入事件?

时间:2011-09-08 13:32:02

标签: macos cocoa keyboard macos-carbon

游戏需要低级访问键盘输入。在Windows上,有DirectInput。但是Mac OS X游戏开发者使用什么技术呢?

显然,有足够的Mac游戏可以让键盘输入正确,没有众所周知的解决方案的缺点:

解决方案#1:Use keyUp / keyDown events

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

不可接受的缺点: keyDown事件根据“密钥重复率”和“密钥重复延迟”的系统首选项设置重复。这会导致初始keyDown事件,然后暂停,然后以系统首选项设置定义的速率开始重复。此解决方案不能用于连续的键事件。

我想知道是否可以禁用密钥重复行为?我想你可以读取第一个keyDown键,然后记住你班级中“keyCode x关闭的键”状态,直到收到该键的keyUp事件,从而绕过重复延迟和重复率问题。

解决方案#2:Use Quarts Event Taps

Quartz Event Services Reference。它似乎足够低级。

不可接受的缺点:要求在Universal Access - Keyboard下的系统首选项中启用辅助设备。虽然默认情况下可能会启用,但无法保证某些系统可能会关闭它。

我还读到Quartz事件点击需要应用程序以root身份运行,但没有找到确认。

解决方案#3:Carbon Events / IOKit HID

Carbon Event Manager Reference被标记为遗留,不应用于新开发。

无法接受的缺点:没有人知道未来Mac OS版本将继续支持Carbon事件的时间。

除了Carbon是一个遗留框架,这似乎仍然是最好的解决方案。但使用碳事件还有其他任何缺点吗?

问题:

Mac OS X游戏开发人员使用哪种技术接收低级键盘输入事件?如果他们使用上述技术之一,他们如何解决我提到的缺点?

更新

我最终转向使用常规NSEvent消息并将其包装在neat API for polling the keyboard states

6 个答案:

答案 0 :(得分:9)

我对#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

同样,这不会很快消失,而且与碳和碳事件完全分开。

答案 1 :(得分:2)

我参加这个派对已经很晚了,但这是我使用Swift写的OSX游戏的解决方案。它非常简单,似乎运作良好。

首先,您可以将此代码放在接收键盘事件的控制器中:

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,因此您不必担心重复或订购。密钥可以在集合中,也可以不是。

我不太确定密钥代码的标准化程度。但是你可以提供一个键盘配置页面,用户可以在其中键入每个动作的键,然后保存这个碰巧的密钥代码。

答案 2 :(得分:1)

好的,所以我真的很晚才参加派对,但我认为我的观点很直接,就像上面的Howards先生的解决方案一样。请记住,此输入用于控制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") }
}

答案 3 :(得分:0)

答案 4 :(得分:0)

polkit(Obj-C工具包)中还有一些设备类,包括......

  • HIDController使用USB HID设备
  • AppleRemote使用Apple Remote
  • MidiController使用Midi设备
  • OSCController通过UDP使用OSC兼容设备
  • SerialPort以使用任何串行端口设备
  • SC2004LCDModule使用来自siliconcraft.net的SC 2004

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

答案 5 :(得分:0)

我找到了using Quartz Event Taps的一个很好的例子。

然而问题是:

  1. 对于某些事件类型,用户需要以root权限运行应用程序。例如拦截keydown和keyup事件需要root。

  2. 拦截是系统范围的,这意味着当程序运行时,它将捕获所有关键事件,甚至是那些发送到其他应用程序的事件。实现需要非常小心,以免破坏其他应用程序。