この投稿は何か?
前回からの続きです。
コードを読む
このページにもSharedCode
ファイルがありますが、以前のページと共通なので解説を省略します。
ここでは、メインファイルのみを扱います。
コード全体は、以下の通りです。
- 蝶とテキストの3Dモデルを作成して、シーンに追加する
- 蝶が飛んでいるように動かす
- カメラが蝶に近づいたとき、別の3Dモデルを表示する。
// バナーと蝶を作成します。
let banner = Model.text("すばしっこい蝶を捕まえてください!", elevation: 10.cm)
let butterfly = Model.flyingBug
// シーンにモデルを追加して、蝶を縮小します。
scene.add([banner, butterfly])
butterfly.run(action: .scaleTo(0.1, duration: 2))
// 蝶を移動するシーケンスを作成します。継続時間を変更して、移動を速くしたり遅くしたりします。
let duration: Float = 1
let sequence: [BuiltinAction] = [
.moveBy(x: 35.cm, y: 20.cm, z: -5.cm, duration: duration),
.moveBy(x: -20.cm, y: 5.cm, z: -20.cm, duration: duration),
.moveBy(x: -25.cm, y: -10.cm, z: 10.cm, duration: duration),
.moveBy(x: 15.cm, y: 20.cm, z: 30.cm, duration: duration),
.moveBy(x: -5.cm, y: -35.cm, z: -15.cm, duration: duration)
]
// シーンが開始するときに、蝶を舞わせます。
scene.setOnStartHandler {
butterfly.animate(.flap, repeats: .loop)
butterfly.repeat(.pingpong, sequence: sequence, completion: nil)
butterfly.play(.melodyLoop, loops: true, completion: nil)
}
// カメラが蝶から2cm以内に近づいたとき、サプライズを起こします!
scene.camera.when(butterfly, isWithin: 2.cm) {
let ear = Model.ear
let eye = Model.eye
let foot = Model.foot
scene.place(ear, at: butterfly.location)
scene.place(eye, at: butterfly.location)
scene.place(foot, at: butterfly.location)
ear.run(action: .moveBy(x: Float.random(in: -20.cm...20.cm), y: Float.random(in: -20.cm...20.cm), z: Float.random(in: -20.cm...20.cm), duration: 1))
eye.run(action: .moveBy(x: Float.random(in: -20.cm...20.cm), y: Float.random(in: -20.cm...20.cm), z: Float.random(in: -20.cm...20.cm), duration: 1))
foot.run(action: .moveBy(x: Float.random(in: -20.cm...20.cm), y: Float.random(in: -20.cm...20.cm), z: Float.random(in: -20.cm...20.cm), duration: 1))
}
コードを分解して、見ていきます。
モデル作成とシーンへの追加
テキストの3Dモデルを定数banner
に、蝶の3Dモデルを定数butterfly
に割り当てています。
add()
メソッドを使って、シーンに追加します。
let banner = Model.text("すばしっこい蝶を捕まえてください!", elevation: 10.cm)
let butterfly = Model.flyingBug
scene.add([banner, butterfly])
butterfly.run(action: .scaleTo(0.1, duration: 2)). // 蝶を縮小する
シーンに追加した後に、蝶のアクションを指定しています。
アクションを指定した後に、シーンに追加した方が個人的には自然な気がします。
コード2
蝶がゆらゆらと浮遊しているように見せています。
継続時間であるduration
を変更することで、移動を速くしたり遅くしたり調節できます。
let duration: Float = 1
let sequence: [BuiltinAction] = [
.moveBy(x: 35.cm, y: 20.cm, z: -5.cm, duration: duration),
.moveBy(x: -20.cm, y: 5.cm, z: -20.cm, duration: duration),
.moveBy(x: -25.cm, y: -10.cm, z: 10.cm, duration: duration),
.moveBy(x: 15.cm, y: 20.cm, z: 30.cm, duration: duration),
.moveBy(x: -5.cm, y: -35.cm, z: -15.cm, duration: duration)
]
ここでは、moveBy(x:y:z:duration:)
メソッドがBuiltinAction
の型メソッドであることがわかります。
この定数sequence
は、蝶を動かすことが目的ではあるものの、実装上、__どの3Dモデルを動かすか__は現時点では未定です。
シーケンスを蝶に渡す
「シーンを開始する」ボタンをタップしたら、蝶がAR空間を浮遊するようにします。
シーン開始後のハンドラは、setOnStartHandler
メソッドに渡します。
蝶は移動するだけでなく、音を鳴らしたり、パタパタと羽を動かし続けるようにアニメーションさせています。
scene.setOnStartHandler {
butterfly.animate(.flap, repeats: .loop)
butterfly.repeat(.pingpong, sequence: sequence, completion: nil)
butterfly.play(.melodyLoop, loops: true, completion: nil)
}
ここで、作成済みのシークエンスを蝶に渡しています。
カメラの距離イベント
カメラと蝶の距離が2cm以内になると、イベントが発生するようにしています。
耳と目と足の3Dモデルが出現して、蝶の周辺をランダムに動き回るようにしています。
scene.camera.when(butterfly, isWithin: 2.cm) {
let ear = Model.ear
let eye = Model.eye
let foot = Model.foot
scene.place(ear, at: butterfly.location)
scene.place(eye, at: butterfly.location)
scene.place(foot, at: butterfly.location)
ear.run(action: .moveBy(x: Float.random(in: -20.cm...20.cm),
y: Float.random(in: -20.cm...20.cm),
z: Float.random(in: -20.cm...20.cm),
duration: 1))
eye.run(action: .moveBy(x: Float.random(in: -20.cm...20.cm),
y: Float.random(in: -20.cm...20.cm),
z: Float.random(in: -20.cm...20.cm),
duration: 1))
foot.run(action: .moveBy(x: Float.random(in: -20.cm...20.cm),
y: Float.random(in: -20.cm...20.cm),
z: Float.random(in: -20.cm...20.cm),
duration: 1))
}
ここでは、Float.random(in: -20.cm...20.cm)
という記述に注目します。
Float.random(in:)
メソッドは、パラメータに指定された範囲内で乱数を返します。
また、-20.cm...20.cm
は、範囲の表現です。
したがって、最小-20
cmから最大20
cm以内にある小数点数を返すことになります。
Int
でなく、Float
のrandom(in:)
メソッドを使っているところに、何かこだわりがあるのでしょうか...。
コードを読んでわかったこと
このページで構築しているARコンテンツは、インタラクティブで何度も体験したくなる要素が詰め込まれていると感じました。
ランダムな要素を取り入れたり、タップしたり、実際に動いたりするところが楽しいと感じさせてくれる理由ではないでしょうか。
- 連続的にアクションさせるには、シークエンスを作ってから、モデルの
repeat(_:sequence:completion:)
メソッドに渡す。 - ちなみに、同時にいくつかのアクションを行うには、
run(group:)
メソッドにアクションコレクションを渡す。 - AR空間上に存在する3Dモデルの位置情報は、
location
プロパティから取得できる。