Mac OS X 游戏如何接收低级键盘输入事件?
Posted
技术标签:
【中文标题】Mac OS X 游戏如何接收低级键盘输入事件?【英文标题】:How can Mac OS X games receive low-level keyboard input events? 【发布时间】:2011-11-13 00:22:06 【问题描述】:游戏需要对键盘输入进行低级访问。在 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。它似乎足够低级。
不可接受的缺点: 需要在通用访问 - 键盘下的系统首选项中启用辅助设备。虽然这可能默认开启,但不能保证它可能会在某些系统上关闭。
我还读到 Quartz 事件点击需要应用程序以 root 身份运行,但没有找到对此的确认。
解决方案 #3:Carbon Events / IOKit HID
Carbon Event Manager Reference 被标记为旧版,不应用于新开发。
不可接受的缺点:没有人知道未来的 Mac OS 版本会继续支持 Carbon 事件多长时间。
除了 Carbon 是一个遗留框架之外,这似乎仍然是最好的解决方案。但是使用 Carbon Events 还有其他缺点吗?
问题:
Mac OS X 游戏开发人员使用哪种技术来接收低级键盘输入事件?如果他们使用上述技术之一,他们如何解决我提到的缺点?
更新:
我最终转而使用常规的 NSEvent 消息并将它们包装在 neat API for polling the keyboard states 中。
【问题讨论】:
为什么 keyDown: 的重复行为不可接受?在获得 keyUp 之前,您不能忽略其他事件吗? 我真的不知道最好的解决方案是什么,但关于解决方案 #1 的缺点 -NSEvent
有 isARepeat
方法来确定事件是否是自动键重复的结果.您可以简单地忽略这些并假设该键被连续按下,直到您收到相应的keyUp:
事件。
我将此作为注释添加到解决方案 #1。我希望的是一个游戏开发者的框架,它只允许你调用像 isKeyDown:(UInt16)virtualKeyCode 这样的方法。
关于#2,在文档中找到了这个:新事件点击的位置。传递 Event Tap Locations 中列出的常量之一。只有以 root 用户身份运行的进程才能在 HID 事件进入窗口服务器的位置找到事件点击;对于其他用户,此函数返回 NULL。 developer.apple.com/library/mac/documentation/Carbon/Reference/…
关于#2,是的,我确认某些事件类型需要root权限。例如,key_down 和 key_up。我猜原因是他们不想让程序监控用户的按键输入。
【参考方案1】:
看看 ControllerMate 和 manymouse。
http://icculus.org/manymouse/
http://www.orderedbytes.com/controllermate/
【讨论】:
【参考方案2】:我在#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 完全分开。
【讨论】:
【参考方案3】:polkit(Obj-C 工具包)中也有一些设备类,包括...
HIDController 使用 USB HID 设备 AppleRemote 使用 Apple Remote MidiController 使用 Midi 设备 OSCController 通过 UDP 使用 OSC 兼容设备 SerialPort 使用任何串口设备 SC2004LCDModule 使用来自 Siliconcraft.net 的 SC 2004http://code.google.com/p/polkit/
【讨论】:
【参考方案4】:我找到了using Quartz Event Taps 的一个很好的例子。
但问题是:
对于某些事件类型,用户需要以 root 权限运行应用程序。例如拦截 keydown 和 keyup 事件需要 root。
拦截是系统范围的,这意味着当程序运行时,它将捕获所有关键事件,甚至那些发送到其他应用程序的关键事件。实施需要非常小心,以免破坏其他应用程序。
【讨论】:
【参考方案5】:我参加这个聚会迟到了,但这是我使用 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,所以你不必担心重复或排序。密钥要么在集合中,要么不在集合中。
我不太确定键码的标准化程度。但是您可以提供一个键盘配置页面,用户可以在其中为每个操作键入键,然后保存发生的任何键码。
【讨论】:
这样其实挺优雅的,但是不收集修饰键。 @2075 不,它没有。它不适用于不同类型的键盘和布局。对于国际比赛来说,这不是一个好的解决方案。 @Andreas 如果你能想出更好的解决方案,请分享:)【参考方案6】:好的,所以我真的迟到了,但我认为我的观点直截了当,类似于上述霍华德先生的解决方案。请记住,此输入用于控制 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")
【讨论】:
以上是关于Mac OS X 游戏如何接收低级键盘输入事件?的主要内容,如果未能解决你的问题,请参考以下文章
OS X (macOS) 和低级 C(Objective-C 替代方案)中的 Applescript