SceneKitでサイコロが転がるようなアニメーションを再現する

##はじめに
サイコロが転がるようなアニメーションを再現します。Unityでは軸を設定してその周りの回転を行うことができる`RotateAround`というメソッドが用意されていますが、SceneKittには無いようです。主な動きは↓こんな感じ。SceneKit SceneFileの使い方についても触れています。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">全方向への移動ver <a href="https://t.co/PwEDmYHJre">pic.twitter.com/PwEDmYHJre</a></p>&mdash; 二次曲線 (@Hyperbolic____) <a href="https://twitter.com/Hyperbolic____/status/1300324733330681858?ref_src=twsrc%5Etfw">August 31, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

##考え方のヒント・実装の流れ
- Rotateなどのメソッドはオブジェクトの座標を原点とし、回転する方向を決めて、回転を行っている
- 親と子の階層関係にあるオブジェクトは、親が動くと子も動く
- 球や立方体のような形を持たないオブジェクト(ここではemptyNodeとしている)を生成することも可能

##再現したい動きをSceneKit SceneFileで確認する
SceneFileでは、Unityのように3Dオブジェクトを実際に配置することが可能です。動きの確認だけでなく、ファイルをそのまま起動させることもできます。
<img width="200" alt="スクリーンショット 2020-08-31 16.05.54.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/549093/6a915993-cabe-48ac-c41f-eca5560105d1.jpeg"> <img width="400" alt="スクリーンショット 2020-08-31 16.06.11.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/549093/0c4acb82-12fd-c582-ec36-7a7c484e874f.jpeg">


<img width="500" alt="スクリーンショット 2020-08-31 16.10.14.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/549093/93ef5159-e39c-2cac-9d40-8d299915be0b.jpeg">

では実際に回転の流れを見ていきます。
###1.boxを追加する
右上の➕からboxを選択し、ドラッグ&ドロップします。その後、position(座標)を(0,0.5,0)、size(大きさ)を(1,1,1),Eular(回転)を(0,0,0)に設定します。
<img width="400" alt="スクリーンショット 2020-08-31 16.14.23.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/549093/e749304b-5ebc-d092-92d1-daac16071623.jpeg">
###2.形を持たないnodeを追加する
右下の➕ボタンを押し、nodeを追加します。`<untitled>` が追加されればOKです。(以下これをemptyNodeとします。)
右側に回転する動きを再現したいので、中心となる立方体の辺の中点にemptyNode(0.5,0,0.5)に移動します。
<img width="400" alt="スクリーンショット 2020-08-31 16.15.06.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/549093/17ae6c49-cac7-4949-7413-e4e42f0cd849.jpeg">

###3.emptyNodeの階層下にboxを配置する
階層関係にあるnodeにおいて、子nodeは親nodeの動きの影響を受けます。boxをemptyNodeの下にドラッグ&ドロップし、子nodeとして設定します。
<img width="300" alt="スクリーンショット 2020-08-31 16.15.44.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/549093/e1b8e302-f1f7-2b61-222b-a3b4c8c92951.jpeg">
###4.emptyNodeを回転させる
emotyNodeのz軸を-90度回転させることで、回転を表現することができます。
<img width="300" alt="スクリーンショット 2020-08-31 16.16.04.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/549093/b91bd478-d654-53d5-c9d8-27656cb5442e.jpeg"> <img width="300" alt="スクリーンショット 2020-08-31 16.16.22.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/549093/ee2bebf8-48c3-b30a-0240-8efdf08010cf.jpeg">

##SceneFileで確認した動きをコードで再現する
SCNSceneクラスを継承したGameSceneクラスを作成します。GameSceneにはboxNodeとemptyNodeをプロパティとして持たせています。また、boxNodeの座標である0,0,0.5をそれぞれx,y,zをとして定義します。

```Swift
class ViewController: UIViewController {
    var x = 0.0
    var y = 0.5
    var z = 0.0
@IBOutlet weak var test: SCNView!
    let scene = GameScene()
    override func viewDidLoad() {
        super.viewDidLoad()
        test.scene = scene
    }
}
```

rightボタンを紐付け、動きを記述します。

```Swift
@IBAction func right(_ sender: Any) {
        //EmptyNodeを定義
        scene.emptyNode.position = SCNVector3(x: Float(scene.boxNode.worldPosition.x+0.5), y: Float(scene.boxNode.worldPosition.y-0.5), z: Float(scene.boxNode.worldPosition.z))
        scene.emptyNode.addChildNode(scene.boxNode)
        scene.boxNode.worldPosition = SCNVector3(x,y,z)

        let a = SCNAction.rotate(by: -CGFloat(Float.pi/2), around: SCNVector3(0,0,1), duration: 0.1)
        scene.emptyNode.runAction(a)
        x+=1
    }
```

基本的にはSceneFileで確認した動きをそのまま書くだけなのですが、**addChild**を行うと動くことが確認されたので、最初に定義したx,y,zを用いて解決しています。