swiftmacossprite-kitkeyboard-eventsgamecontroller

Keyboard as game controller, handling multiple keys in Swift


I'm trying to make a spaceship move on the screen with the keyboard. I manage to deal with key events, but I noticed that when multiple keys are kept down at the same time, it won't behave correctly and only one will have priority. I'm using a switch statement because I thought the keyDown function was called once for every key, but even when I explicitly add a fallthrough in the cases, it's not better. Has anyone ever experienced anything like that and is there any better way to use the keyboard as a controller?

override func keyDown(with event: NSEvent) {

      switch event.keyCode {
      case 0x31:
         if let playerShip = self.playerShip {
            playerShip.run(SKAction.init(named: "Pulse")!, withKey: "fadeInOut")
         }
      case 123:
         if let playerShip = self.playerShip {
            playerShip.run(SKAction.applyAngularImpulse(0.001, duration: 0.1))
         }
      case 124:
         if let playerShip = self.playerShip {
            playerShip.run(SKAction.applyAngularImpulse(-0.001, duration: 0.1))
         }
      case 125:
         if let playerShip = self.playerShip {
            playerShip.run(SKAction.applyImpulse(CGVector.init(angle: playerShip.zRotation).opposite(), duration: 0.1))
         }
      case 126:
         if let playerShip = self.playerShip {
            playerShip.run(SKAction.applyImpulse(CGVector.init(angle: playerShip.zRotation), duration: 0.1))
         }
      default:
         print("keyDown: \(event.characters!) keyCode: \(event.keyCode)")
      }
   }

Solution

  • After testing a few things to make sure that my SKActions were not the cause of the problem, I remembered a few things from the days I was coding in Delphi with DelphiX and GLScene. I know it's PC, but it's related.

    The thing is that the keyboard event cue will retrigger only the las key that was pressed. So applying force with the up arrow and keeping it pressed to accelerate will work until I press, for example, the left arrow to apply some torque. Then the left arrow key will get retriggered, but the next event to come from the up arrow, even if it's still pressed by now, will be when you actually release it. Therefore, the ship will start rotating but will stop accelerating because the keyDown event won't get retriggered for the up arrow.

    This is why you need a way to keep track of key states, so you can check if multiple keys are pressed together at any given moment.

    This is also why I'm gonna build my keyboard manager class.