57770
自機としてPlayerクラスのインスタンスplayerを操作しようと考えています。  
WASDの４つのキーで４方に移動しますが、keyDownとkeyUpでは押している間の移動はかろうじてできるのに対し、８方向への入力、つまり同時押しによる斜め方向への移動は不可能でした。  
フレームごとにキー入力が受け付けられているようなので、switch文からifで記述もしてみましたが、うまくいきません。何か良いアイデアはありませんでしょうか。

```

    override func keyDown(with event: NSEvent) {
        if event.keyCode == 13 {
            player.moveUp(distance: 10)
        }
        if event.keyCode == 0 {
            player.moveLeft(distance: 10)
        }
        if event.keyCode == 1 {
            player.moveDown(distance: 10)
        }
        if event.keyCode == 2 {
            player.moveRight(distance: 10)
        }
    }
    
```

以上がkeyDownの中身です。  
なお、PlayerクラスはmoveRight, moveLeft
...などのメソッドを持っていて、同名の関数などもありましたがoverrideはしておりませんので独自の関数です。  
また中身は単純でPlayerのpositionの値をdistance（引数）に応じて動かすだけです
CC BY-SA 4.0
2019-09-01T08:58:23.120
57748
2019-09-02T09:35:44.797
16877
question
swift spritekit
spritekitを用いたMac向けのアプリでキーボードの同時入力を処理したい
今の処理方法ですと、１つのキーイベントが来たときに、そのイベントが来る度に処理をする方法になっているため、`keyDown`の中で移動を行うのはかなり難しいと思います。

多分`SKView`のサブクラスで処理を書かれていると思うので、そのクラスにX移動量とY移動量のメンバー変数を追加し、

  * `KeyDown`, `KeyUp`イベントで行うのは移動量変数の更新のみ
  * 実際の移動処理はタイマーを使って定期的に行う
  * `moveXXX`関数は一つに纏め、`move(x: X座標移動量, y: Y座標移動量)`とする

と言う風に考えると、move関数以外の書き替え、書き加えるべき部分は以下のようになると思います。

  * 更新頻度を20fps (1000ms / 20 = 50)と考えていますので、この辺りは適宜調整して下さい
  * タイマーの初期化を一番省略した形で行っているので、細かい設定を行いたい場合は、`DispatchSource.makeTimerSource()`の引数を適宜設定して下さい
  * 関数`move`は引数に移動量を受けとっていますが、引数無しで、直接メンバー変数を参照しても良いです

```

    private var distanceX: Int = 0
    private var distanceY: Int = 0
    private var moveHandlingTimer: DispatchSourceTimer = DispatchSource.makeTimerSource()
    
    deinit {
        moveHandlingTimer.cancel()
    }
    
    override func viewDidLoad () {
        // 他の必要な処理
        moveHandlingTimer.setEventHandler { [weak self] in
            guard let weakSelf = self, let x: Int = weakSelf.distanceX, let y: Int = weakSelf.distanceY else { return }
            if ((x != 0) || (y != 0)) {
                weakSelf.move(x: x, y: y)
            }
        }
        moveHandlingTimer.schdule(deadline: DispatchTime.now(), repating: DispatchTimeInterval.miliseconds(50), leeway: DispatchTimeInterval.miliseconts(1))
        moveHandlingTimer.resume()
    }
    
    override func keyDown(with event: NSEvent) {
        switch event.keyCode {
        case 13:
            distanceX = 10
        case 1:
            distanceX = -10
        case 0:
            distanceY = -10
        case 2:
            distanceY = 10
        default:
            break
        }
    }
    
    override func keyUp(with event: NSEvent) {
        switch event.keyCode {
        case 13, 1:
            distanceX = 0
        case 0, 2:
            distanceY = 0
        default:
            break
        }
    }
    
```
CC BY-SA 4.0
2019-09-02T09:35:44.797
57770
2019-09-02T09:35:44.797
14745
57748
answer