如何检测 OS X 上是否按下了某个键?
How to detect if a key is pressed on OS X?
我正在为 OSX 在 Spritekit 中编写游戏。如何检查当前是否按下了一个键?考虑这个例子:
//some code. this could be e.g. inside a game loop
if (/*is key 'w' pressed*/) {
// move forward
}
//some more code
Swift 和 Objective-C 两种解决方案都很有用。
注意:我对接收或处理事件不感兴趣,所以我想尽可能避免这种情况。我只需要检查当前是否按下了特定的键。
注意#2:这个问题不是链接问题的重复,因为它只是从接收'keyPressed'事件的角度解决问题(如方法调用的参数),而不是从在别处执行检查以查明当前是否正在按下某个键的角度来看。换句话说,它并没有帮助我填写上面的if语句中的条件。
我想我可能不得不最终维护一个布尔数组,每个键代码一个,并在收到 keyDown 和 keyUp 事件时更新其内容。但我希望有一个更优雅的解决方案,因为这似乎是微不足道的功能。
这样试试:
正如我已经提到的,您需要为 keyUp 和 keyDown 事件将 addLocalMonitorForEventsMatchingMask 添加到您的游戏场景方法 didMoveToView 中,并为 keyCode 事件添加一个 switch 语句:
import SpriteKit
class GameScene: SKScene {
let sprite = SKSpriteNode(imageNamed:"Spaceship")
var keyDownState:[String:Bool] = ["k":false, "j":false]
var movingLeft = false
var movingRight = false
override func didMoveToView(view: SKView) {
sprite.position = CGPoint(x: view.scene!.frame.midX, y: view.scene!.frame.midY)
sprite.setScale(0.5)
addChild(sprite)
NSEvent.addLocalMonitorForEventsMatchingMask(.KeyDownMask) { (theEvent) -> NSEvent! in
print("keyDown event")
switch theEvent.keyCode {
case 38:
print("j is down")
self.keyDownState["j"] = true
case 40:
print("k is down")
self.keyDownState["k"] = true
default:
print("unknown key")
}
print(self.keyDownState.description)
// j and k are pressed
if self.keyDownState["j"]! && self.keyDownState["k"]! {
self.sprite.removeAllActions()
view.scene?.backgroundColor = NSColor.init(red: 1, green: 1, blue: 0, alpha: 1)
}
// j is pressed
if self.keyDownState["j"]! && !self.keyDownState["k"]! {
view.scene?.backgroundColor = NSColor.greenColor()
if !self.movingLeft {
self.sprite.removeActionForKey("moveSpriteRight")
self.movingRight = false
let moveSpriteLeft = SKAction.moveByX(-50, y: 0, duration: 0.1)
self.sprite.runAction(SKAction.repeatActionForever(moveSpriteLeft) , withKey: "moveSpriteLeft")
self.movingLeft = true
}
}
// k is pressed
if !self.keyDownState["j"]! && self.keyDownState["k"]! {
view.scene?.backgroundColor = NSColor.redColor()
if !self.movingRight {
self.sprite.removeActionForKey("moveSpriteLeft")
self.movingLeft = false
let moveSpriteRight = SKAction.moveByX(50, y: 0, duration: 0.1)
self.sprite.runAction(SKAction.repeatActionForever(moveSpriteRight) , withKey: "moveSpriteRight")
self.movingRight = true
}
}
return theEvent
}
NSEvent.addLocalMonitorForEventsMatchingMask(.KeyUpMask) { (theEvent) -> NSEvent! in
print("keyUp event \(theEvent.keyCode)")
switch theEvent.keyCode {
case 38:
print("j is up")
self.keyDownState["j"] = false
self.movingLeft = false
case 40:
print("k is up")
self.keyDownState["k"] = false
self.movingRight = false
default:
print("unknown key")
}
print(self.keyDownState.description)
if self.keyDownState["j"]! && !self.keyDownState["k"]! {
view.scene?.backgroundColor = NSColor.greenColor()
self.sprite.removeActionForKey("moveSpriteRight")
self.movingRight = false
let moveSpriteLeft = SKAction.moveByX(-50, y: 0, duration: 0.1)
self.sprite.runAction(SKAction.repeatActionForever(moveSpriteLeft) , withKey: "moveSpriteLeft")
self.movingLeft = true
}
if !self.keyDownState["j"]! && self.keyDownState["k"]! {
view.scene?.backgroundColor = NSColor.redColor()
self.sprite.removeActionForKey("moveSpriteLeft")
self.movingLeft = false
let moveSpriteRight = SKAction.moveByX(50, y: 0, duration: 0.1)
self.sprite.runAction(SKAction.repeatActionForever(moveSpriteRight) , withKey: "moveSpriteRight")
self.movingRight = true
}
if !self.keyDownState["j"]! && !self.keyDownState["k"]! {
self.sprite.removeAllActions()
view.scene?.backgroundColor = NSColor(red: 0.72628, green: 0.726298 , blue: 0.726288, alpha: 1)
}
return theEvent
}
}
override func mouseDown(theEvent: NSEvent) {
/* Called when a mouse click occurs */
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
如果轮询而不是接收通知更方便,您可以使用 Quartz 事件服务功能 CGEventSourceKeyState
。
我知道这已经过时了,但问题很普遍,其他答案并没有真正按照问题要求的方式解决问题,即通过询问系统键盘状态,而不是更改事件。
SFML has already solved this problem. Check the code,特别是HIDInputManager
class。顺便说一句,我发现 Apple 的 IOKit 文档毫无帮助。
然后让我建议在编写游戏时依靠 SFML 或类似的框架,而不是自己解决这个和其他低级问题。此外,这样您就可以免费获得跨平台解决方案。
我正在为 OSX 在 Spritekit 中编写游戏。如何检查当前是否按下了一个键?考虑这个例子:
//some code. this could be e.g. inside a game loop
if (/*is key 'w' pressed*/) {
// move forward
}
//some more code
Swift 和 Objective-C 两种解决方案都很有用。
注意:我对接收或处理事件不感兴趣,所以我想尽可能避免这种情况。我只需要检查当前是否按下了特定的键。
注意#2:这个问题不是链接问题的重复,因为它只是从接收'keyPressed'事件的角度解决问题(如方法调用的参数),而不是从在别处执行检查以查明当前是否正在按下某个键的角度来看。换句话说,它并没有帮助我填写上面的if语句中的条件。
我想我可能不得不最终维护一个布尔数组,每个键代码一个,并在收到 keyDown 和 keyUp 事件时更新其内容。但我希望有一个更优雅的解决方案,因为这似乎是微不足道的功能。
这样试试:
正如我已经提到的,您需要为 keyUp 和 keyDown 事件将 addLocalMonitorForEventsMatchingMask 添加到您的游戏场景方法 didMoveToView 中,并为 keyCode 事件添加一个 switch 语句:
import SpriteKit
class GameScene: SKScene {
let sprite = SKSpriteNode(imageNamed:"Spaceship")
var keyDownState:[String:Bool] = ["k":false, "j":false]
var movingLeft = false
var movingRight = false
override func didMoveToView(view: SKView) {
sprite.position = CGPoint(x: view.scene!.frame.midX, y: view.scene!.frame.midY)
sprite.setScale(0.5)
addChild(sprite)
NSEvent.addLocalMonitorForEventsMatchingMask(.KeyDownMask) { (theEvent) -> NSEvent! in
print("keyDown event")
switch theEvent.keyCode {
case 38:
print("j is down")
self.keyDownState["j"] = true
case 40:
print("k is down")
self.keyDownState["k"] = true
default:
print("unknown key")
}
print(self.keyDownState.description)
// j and k are pressed
if self.keyDownState["j"]! && self.keyDownState["k"]! {
self.sprite.removeAllActions()
view.scene?.backgroundColor = NSColor.init(red: 1, green: 1, blue: 0, alpha: 1)
}
// j is pressed
if self.keyDownState["j"]! && !self.keyDownState["k"]! {
view.scene?.backgroundColor = NSColor.greenColor()
if !self.movingLeft {
self.sprite.removeActionForKey("moveSpriteRight")
self.movingRight = false
let moveSpriteLeft = SKAction.moveByX(-50, y: 0, duration: 0.1)
self.sprite.runAction(SKAction.repeatActionForever(moveSpriteLeft) , withKey: "moveSpriteLeft")
self.movingLeft = true
}
}
// k is pressed
if !self.keyDownState["j"]! && self.keyDownState["k"]! {
view.scene?.backgroundColor = NSColor.redColor()
if !self.movingRight {
self.sprite.removeActionForKey("moveSpriteLeft")
self.movingLeft = false
let moveSpriteRight = SKAction.moveByX(50, y: 0, duration: 0.1)
self.sprite.runAction(SKAction.repeatActionForever(moveSpriteRight) , withKey: "moveSpriteRight")
self.movingRight = true
}
}
return theEvent
}
NSEvent.addLocalMonitorForEventsMatchingMask(.KeyUpMask) { (theEvent) -> NSEvent! in
print("keyUp event \(theEvent.keyCode)")
switch theEvent.keyCode {
case 38:
print("j is up")
self.keyDownState["j"] = false
self.movingLeft = false
case 40:
print("k is up")
self.keyDownState["k"] = false
self.movingRight = false
default:
print("unknown key")
}
print(self.keyDownState.description)
if self.keyDownState["j"]! && !self.keyDownState["k"]! {
view.scene?.backgroundColor = NSColor.greenColor()
self.sprite.removeActionForKey("moveSpriteRight")
self.movingRight = false
let moveSpriteLeft = SKAction.moveByX(-50, y: 0, duration: 0.1)
self.sprite.runAction(SKAction.repeatActionForever(moveSpriteLeft) , withKey: "moveSpriteLeft")
self.movingLeft = true
}
if !self.keyDownState["j"]! && self.keyDownState["k"]! {
view.scene?.backgroundColor = NSColor.redColor()
self.sprite.removeActionForKey("moveSpriteLeft")
self.movingLeft = false
let moveSpriteRight = SKAction.moveByX(50, y: 0, duration: 0.1)
self.sprite.runAction(SKAction.repeatActionForever(moveSpriteRight) , withKey: "moveSpriteRight")
self.movingRight = true
}
if !self.keyDownState["j"]! && !self.keyDownState["k"]! {
self.sprite.removeAllActions()
view.scene?.backgroundColor = NSColor(red: 0.72628, green: 0.726298 , blue: 0.726288, alpha: 1)
}
return theEvent
}
}
override func mouseDown(theEvent: NSEvent) {
/* Called when a mouse click occurs */
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
如果轮询而不是接收通知更方便,您可以使用 Quartz 事件服务功能 CGEventSourceKeyState
。
我知道这已经过时了,但问题很普遍,其他答案并没有真正按照问题要求的方式解决问题,即通过询问系统键盘状态,而不是更改事件。
SFML has already solved this problem. Check the code,特别是HIDInputManager
class。顺便说一句,我发现 Apple 的 IOKit 文档毫无帮助。
然后让我建议在编写游戏时依靠 SFML 或类似的框架,而不是自己解决这个和其他低级问题。此外,这样您就可以免费获得跨平台解决方案。