3

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)")
      }
   }
BadgerBadger
  • 696
  • 3
  • 12

3 Answers3

1

My guess is that even if you get the code working exactly the way you describe it will be extremely difficult in practice for anyone playing your game to hit two keys at the exact same time. One key will almost always be hit slightly before the other. So, maybe you could implement it so that you capture one key event, and then look for a second one happening within a short time window after the first one (before the first one is released).

ClayJ
  • 377
  • 2
  • 14
  • Well it's actually when you keep the keys pressed that the problem arise. I use the up and down key to apply an impulse to my node and left and right arrows to apply an angular impulse. So you need to keep the keys pressed to control the ship (à la "Subspace/Continuum"). The use of a fallthrough helped but it still behaved oddly. – BadgerBadger Mar 18 '17 at 22:50
  • 1
    Actually I'm starting to think I should build a key manager that would track the state of the keys and put an update cycle in there. Then use the keyboard events handlers only to switch the key states of the manager, or simply turn the keyboard manager into a delegate... something like that. – BadgerBadger Mar 18 '17 at 22:53
0

The keyDown method should be called by the OS X every time a key is pressed. If two keys are pressed, it's called twice, etc. Perhaps you're seeing this behavior because only the last key's action overrides the first key's action?

Stoyan
  • 320
  • 1
  • 9
  • I'll investigate in that direction. Is there way to inspect a key state asynchronously built in somewhere? – BadgerBadger Mar 18 '17 at 22:54
  • I don't know. I never needed to do that asynchronously. – Stoyan Mar 20 '17 at 09:42
  • Well I tested it further and I do need some other way than the events to manage the keyboard. I will post an answer with an explanation and keep working on my keyboard manager. – BadgerBadger Mar 21 '17 at 05:53
0

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.

BadgerBadger
  • 696
  • 3
  • 12
  • Put a simple key state manager together and it now works flawlessly! If somebody wants the code, let me know, I'll put it on GitHub – BadgerBadger Mar 22 '17 at 06:11