【Swift】 SpriteKit 入門 ②

SpriteKitの使い方を解説します。
[SpriteKit 入門 ①](https://qiita.com/tosssy/items/ef514ce1f12d497c9da8) で画像を表示させることができたので、続いて画像をドラッグして動かせるようにしていきたいと思います。
（環境：Xcode12.1 　Swift5.3）

##画像をドラッグで移動させる
前回作成したMyScene.swiftファイルにコードを追加していきましょう。タッチされた画面上の位置は`touchesMoved()`で取得することができます。また画像(SpriteNode)をコードで操作できるようにMyScene.sksファイルにてNameを設定しておきます(ここでは<font color="Tomato">SampleImage</font>)。
`self.childNode(withName: )`のwithNameプロパティには設定したNameを指定します。

![SK1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498332/f25d437d-dda6-0040-bf40-08ec920e705e.png)

```swift:MyScene.swift
import SpriteKit

class MyScene: SKScene {
    
    var image: SKSpriteNode!
    
    override func sceneDidLoad() {
        //MyScene.sksからSpriteNodeを作成
        self.image = self.childNode(withName: "SampleImage") as! SKSpriteNode?
        self.image.size = CGSize(width: 200, height: 200);
    }
    
    // ドラッグした際に呼ばれる
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        for t in touches {
            self.touchMoved(toPoint: t.location(in: self))
        }
    }
    
    // 画像の位置を更新
    func touchMoved(toPoint pos : CGPoint) {
            self.image.position = pos
    }
}
```
これでシミュレーターを起動してみると、ドラッグで画像を移動することができるようになっているのが確認できると思います。

##改良①
ドラッグで画像を移動させることはできるようになったのですが、現状だと画像でない部分をドラッグするとその位置に画像が瞬間移動してしまいます。画像でない部分をドラッグした場合には移動しないようにするには`func touchMoved(toPoint pos : CGPoint)`の中身を以下のように変更します。

```swift:MyScene.swift
// 画像の位置を更新(改良①)
    func touchMoved(toPoint pos : CGPoint) {
        if (pos.x < self.image.position.x + self.image.size.width/2 && pos.x > self.image.position.x - self.image.size.width/2)&&(pos.y < self.image.position.y + self.image.size.height/2 && pos.x > self.image.position.y - self.image.size.height/2) {
            self.image.position = pos
        }
    }
```
これで画像部分をドラッグしたときのみ移動するようになりました。

##改良②
画像が小さい場合、改良①の処理だとうまくドラッグさせることができないおそれがあります。また、ドラッグ中の画像が指で隠れてしまうこともあるでしょう。この問題を解決するために、ドラッグされた方向に相対的に画像を移動できるようにしてみましょう。注意点としては、指が画面から離れた際に`beforeDragPosition = nil`とするのを忘れないようにしましょう。これを忘れると、再びドラッグする際に前回のドラッグ終了地点の情報が残ったままになり、画像が瞬間移動してしまいます。

```swift:MyScene.swift
//改良②
import SpriteKit

class MyScene: SKScene {
    
    var image: SKSpriteNode!
    var beforeDragPosition: CGPoint?
    
    override func sceneDidLoad() {
        //MyScene.sksからSpriteNodeを作成
        self.image = self.childNode(withName: "SampleImage") as! SKSpriteNode?
        self.image.size = CGSize(width: 200, height: 200);
    }
    
    // ドラッグした際に呼ばれる
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        for t in touches {
            self.touchMoved(toPoint: t.location(in: self))
        }
    }
    
    //ドラッグが終了したとき(画面から指が離れたとき)に呼ばれる
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        for t in touches { self.touchUp(atPoint: t.location(in: self)) }
    }
    
    // 画像の位置を更新
    func touchMoved(toPoint pos : CGPoint) {
        if let p1 = self.beforeDragPosition {
            self.image.position.x += pos.x - p1.x
            self.image.position.y += pos.y - p1.y
        }
        self.beforeDragPosition = pos
    }
    
    func touchUp(atPoint pos : CGPoint){
        self.beforeDragPosition = nil
    }
}
```
この方法はドラッグする画像が1つの場合のみ有効です。2つ以上の画像をドラッグする必要がある場合には改良①の方法を採用すると良いでしょう。また、改良②の問題点として、画像が画面外にあっても移動できてしまうためどこに画像があるのか分からなくなるおそれがあります。これを解消するためには`touchMoved()`の中身を次のようにコードを追加すると良いでしょう。

```swift:MyScene.swift
    // 画像の位置を更新
    func touchMoved(toPoint pos : CGPoint) {
        if let p1 = self.beforeDragPosition {
            self.image.position.x += pos.x - p1.x
            self.image.position.y += pos.y - p1.y
            //左右に飛び出ないようにする
            if !(self.image.position.x < self.size.width/2 && self.image.position.x > -self.size.width/2) {
                self.image.position.x -= pos.x - p1.x
            }
            //上下に飛び出ないようにする
            if !(self.image.position.y < self.size.height/2 && self.image.position.y > -self.size.height/2) {
                self.image.position.y -= pos.y - p1.y
            }
        }
        self.beforeDragPosition = pos
    }
```

###まとめ
さて今回は画像をドラッグして動かせるようにすることができました。次回はSKShapeNodeを使って画像(自機)から弾を発射するようにしていきましょう。

[【Swift】 SpriteKit 入門 ③](https://qiita.com/tosssy/items/45572121a33eb73b991e)