游戏需要低级访问键盘输入。在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。
中答案 0 :(得分:9)
我对#3感到好运,但是如果你想支持键盘以外的任何东西,它需要很多繁重的工作。
在我们潜入之前的一个快速点,Carbon和IOKit HID是两个独立的东西。碳可能在某些时候消失。但IOKit HID仍然存在,只是在10.5中进行了很好的改版。
有关这些内容如何组合在一起的完整示例,请查看https://github.com/OpenEmu/OpenEmu/blob/master/OpenEmu/OEHIDManager.m。这是一个难题的一小部分,因为那里还有其他文件。
同样,这不会很快消失,而且与碳和碳事件完全分开。
答案 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)
看看ControllerMate和manymouse。
答案 4 :(得分:0)
polkit(Obj-C工具包)中还有一些设备类,包括......
答案 5 :(得分:0)
我找到了using Quartz Event Taps的一个很好的例子。
然而问题是:对于某些事件类型,用户需要以root权限运行应用程序。例如拦截keydown和keyup事件需要root。
拦截是系统范围的,这意味着当程序运行时,它将捕获所有关键事件,甚至是那些发送到其他应用程序的事件。实现需要非常小心,以免破坏其他应用程序。