【Swift Playgrounds】ARを作るPART.3「APIの概要」を読み解く

# この投稿は何か？
[前回](https://qiita.com/chino_tweet/items/db8182ad89d20625a56e)の続きです。
この投稿では、「APIの概要」ページを扱います。

# コードを読む
このページは次のふたつのページで構成されています。
- メインファイル
- SharedCodeファイル

SharedCodeファイルは、以前のページと同様です。
ここでは主に、メインファイルを扱います。
ソースコード全体で行っていることを大まかに分類すると...

- 3Dモデルおよびテキスト、3Dシェイプを作成してシーンに追加する
- モデルをタップした時のハンドラを設定
- カメライベントを設定


````Swift:メインファイル
// モデルを作成します。
let lemon = Model.lemon
let snail = Model.snail
let eye = Model.eye
let foot = Model.foot

// モデルにカラースキームを適用します。
lemon.applyColor(scheme: .basic)

// シェイプを作成します。
// .m、.cm、.mmのプロパティによって、ARシーン内のオブジェクトに現実世界の単位を指定できます。
let pyramid = Model.pyramid(width: 10.cm, height: 10.cm, depth: 10.cm)
let box = Model.box(width: 1.cm, height: 10.cm, depth: 5.mm)
let torus = Model.torus(size: 30.cm, thickness: 5.cm)
let cube = Model.cube(size: 5.mm)
let cylinder = Model.cylinder(size: 10.cm, height: 30.cm)
let sphere = Model.sphere(size: 30.cm)

// 3Dテキストを作成します。
let text = Model.text("こんにちは。", elevation: 20.cm)

// シーンにモデルを追加します。
scene.add([lemon, pyramid, text, snail, eye, foot])

// タップでモデルが選択されたときのハンドラを設定します。
foot.onSelected = {
    foot.run(action: .scaleBy(1.2, duration: 1))
}

// onStartハンドラは“シーンを開始”を押した後に実行されます。
scene.setOnStartHandler {
    // アニメーションAPIを使うと、モデルでアニメーションを再生できます。
    pyramid.animate()
    snail.animate(.waggle)
    eye.animate(.lookLeft, repeats: .loop, completion: nil)
   
    // 1つのサウンドを再生します。
    lemon.play(.chord)
 
    // サウンドをループします。
    snail.play(.buzz, loops: false, completion: {
        snail.play(.frown, loops: true)
    })
}

// 距離イベントを作成します。
scene.camera.when(snail, isWithin: 10.cm, do: {
    // オプションのパラメータを指定して、モデルからテキストを読み上げます。
    snail.speak(text: "私にそのような話しかたをしないでください!", rate: 0.2, pitch: 1.76, completion: nil)
    
    // モデルで1つのアクションを実行します。
    lemon.run(action: .moveBy(x: 10.cm, y: 10.cm, z: 50.cm, duration : 5))
    
    // アクションの実行後に完了ハンドラを実行します。
    foot.run(action: .fadeTo(0.5, duration: 2), completion: {
        foot.applyColor(scheme: .tacky)
    })
    
    // シーン内のすべてのモデルにカラースキームを適用します。
    scene.applyColor(scheme: .wacky)


    // 複数のアクションを同時に実行します。
    eye.run(group: [.rotateBy(x: 0, y: 20, z: 0, duration: 5), .moveBy(x: 0, y: 20.cm, z: 0, duration: 5)])
    
    // 複数のアクションを順番に実行します。
    pyramid.run(sequence: [.scaleTo(0.2, duration: 4), .fadeTo(0.25, duration: 2)])
})
````

ページごとのコード量も少しずつ、多くなってきました。
いくつかに分けて、解説します。

### モデルの変数
レモン、カタツムリ、目、足の3Dモデルを作成して、変数に割り当てています。
レモンのカラースキームを変更していますが、外観の変化は個人的にあまり感じられませんでした。

````swift:
// モデルを作成します。
let lemon = Model.lemon
let snail = Model.snail
let eye = Model.eye
let foot = Model.foot

// モデルにカラースキームを適用します。
lemon.applyColor(scheme: .basic)
````

### シェイプとシーン
ピラミッドの3Dモデルが初登場です。
さらに、様々な幾何学構造体の3Dモデルを作成しています。
まだまだ、いろんなモデルがありそうです。

````swift:
// シェイプを作成します。
// .m、.cm、.mmのプロパティによって、ARシーン内のオブジェクトに現実世界の単位を指定できます。
let pyramid = Model.pyramid(width: 10.cm, height: 10.cm, depth: 10.cm)
let box = Model.box(width: 1.cm, height: 10.cm, depth: 5.mm)
let torus = Model.torus(size: 30.cm, thickness: 5.cm)
let cube = Model.cube(size: 5.mm)
let cylinder = Model.cylinder(size: 10.cm, height: 30.cm)
let sphere = Model.sphere(size: 30.cm)

// 3Dテキストを作成します。
let text = Model.text("こんにちは。", elevation: 20.cm)

// シーンにモデルを追加します。
scene.add([lemon, pyramid, text, snail, eye, foot])
````

最後に、作成したモデルやテキスト、シェイプを`add()`メソッドでシーンに追加しています。
この時点では、ボックスやトーラス、キューブなどの幾何学構造モデルはシーンに追加していません。

### ハンドラ
シーンが開始されたあと、モデルがタップされた時にインタラクションするようにしています。
ここでは、足がタップされたら大きくなります。

````swift:モデルがタップされたときのハンドラ
foot.onSelected = {
    foot.run(action: .scaleBy(1.2, duration: 1))
}
````

足モデル`foot`の`onSelected`プロパティにクロージャを割り当てています。

さらに、シーンが開始された直後に動き出すハンドラも設定しています。

- ピラミッド、カタツムリ、目がアニメーション
- レモンが鳴る
- カタツムリが鳴き続ける

````swift:シーンを開始した直後のハンドラ
scene.setOnStartHandler {
    pyramid.animate()
    snail.animate(.waggle)
    eye.animate(.lookLeft, repeats: .loop, completion: nil)
   
    lemon.play(.chord)
    snail.play(.buzz, loops: false, completion: {
        snail.play(.frown, loops: true)
    })
}
````

`animate()`メソッドには、パラメータ違いによるいくつかのバラエティがあるようです。
また、これらのクロージャボディは、シーンの`setOnStartHandler`メソッドのパラメータとして渡されるような記述になっています。

### 距離のイベント
「簡単なシーン」ページと同様に、カメラのイベントを指定しています。
ただし、ここでは後付けクロージャ形式になっていません。

カメラとカタツムリの距離が10cm以内になると...

- カタツムリが喋り出す
- レモンが動く
- 足が半透明になる
- シーン全体のカラースキームが変わる

````swift:距離イベントを作成
scene.camera.when(snail, isWithin: 10.cm, do: {
    snail.speak(text: "私にそのような話しかたをしないで!", rate: 0.2, pitch: 1.76, completion: nil)    
    lemon.run(action: .moveBy(x: 10.cm, y: 10.cm, z: 50.cm, duration : 5))
    foot.run(action: .fadeTo(0.5, duration: 2), completion: {
        foot.applyColor(scheme: .tacky)
    })
    
    scene.applyColor(scheme: .wacky)

    eye.run(group: [.rotateBy(x: 0, y: 20, z: 0, duration: 5), 
                    .moveBy(x: 0, y: 20.cm, z: 0, duration: 5)])    
    pyramid.run(sequence: [.scaleTo(0.2, duration: 4), 
                           .fadeTo(0.25, duration: 2)])
})
````

# コードを読んでわかったこと

- 3Dモデルには、幾何学構造モデルがある
- モデルがタップされた時のハンドラは、`onSelected`プロパティに割り当てる
- シーン開始時のハンドラは、`setOnStartHander`メソッドに渡す

# さらに...
次はいよいよ、ページ「蝶を捕まえる」です。
こちらで[解説](https://qiita.com/chino_tweet/items/5cd1f4800380ab5ef3b5)します。