4

I'm writing a game in Spritekit for OSX. How to check if a key is currently being pressed? Consider this example:

//some code. this could be e.g. inside a game loop if (/*is key 'w' pressed*/) { // move forward } //some more code 

Both Swift and Objective-C solutions are useful.

Note: I am not interested in receiving or dealing with the event, so I'd like to avoid that if possible. I only need to check whether a specific key is currently pressed or not.

Note #2: This question is not a duplicate of the linked question, as it only addresses the issue from the perspective of receiving the 'keyPressed' event (as a parameter to a method call), not from the perspective of performing a check elsewhere to find out whether a key is currently being pressed. In other words, it does not help me fill in the condition in the if-statement above.

I'm thinking I may have to end up maintaining an Array of booleans, one for each keycode, and updating its contents as I receive keyDown and keyUp events. But I was hoping for a more elegant solution as this seems pretty trivial functionality.

0

3 Answers 3

7

If it's more convenient to poll instead of receiving notifications, you could use the Quartz Event Services function CGEventSourceKeyState.

Sign up to request clarification or add additional context in comments.

Comments

4

Try like this:

As I have already mentioned, you need to add addLocalMonitorForEventsMatchingMask to your game scene method didMoveToView for the keyUp and keyDown events and add a switch statement to the keyCode event:

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 */ } } 

Sample project

Comments

1

I know that this is old, but the problem is quite generic and the other answers don't really solve the problem in the way the question asked for, i.e., by asking the system for the keyboard state, not for change events.

SFML has already solved this problem. Check the code, in particular the HIDInputManager class. Btw, I found Apple's documentation of IOKit as unhelpful as it gets.

When writing a game then let me recommend to rely on SFML or a similar framework instead of solving this and other low-level problems yourself. Also, that way you get a cross-platform solution for free.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.