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.

Rashwan L
  • 38,237
  • 7
  • 103
  • 107
George
  • 153
  • 1
  • 8

3 Answers3

7

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

JWWalker
  • 22,385
  • 6
  • 55
  • 76
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

Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
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.

tglas
  • 949
  • 10
  • 19