こんにちは!金融ソリューション事業部WEB3グループの山下です。
本記事は「SpatialComputing入門」シリーズのPart2となります。
前回のPart1で追加した3Dオブジェクトに対して複数のGestureによるインタラクションを実装していきます。
「SpatialComputing入門」シリーズ:
- Part1:3Dオブジェクトを生成して空間に配置する
- Part2:3DオブジェクトにGestureを追加する
- Part3:現実空間の平面に3Dオブジェクトを配置する
動作環境
実装手順
- TapGestureの追加
- VisionPro動作確認
- DragGestureの追加
- VisionPro動作確認
1. TapGestureの追加
Part1で作成したVolumeView.SwiftのStructを、以下に修正します。
オブジェクトをTapをすると、3Dコンテンツのスケールを増加する処理を実装します。
struct VolumeView: View { @State private var scale: Float = 1.0 var body: some View { RealityView { content in // Generate ModelEntity let model = ModelEntity( mesh: .generateCone(height: 0.1, radius: 0.1), materials: [SimpleMaterial(color: .lightGray, isMetallic: true)] ) // Add components model.components.set(InputTargetComponent()) model.components.set(CollisionComponent(shapes: [.generateSphere(radius: 0.1)])) // Add ModelEntity to RealityView content content.add(model) } update: { content in if let model = content.entities.first { model.transform.scale = [scale, scale, scale] } }.gesture(TapGesture().targetedToAnyEntity().onEnded{ _ in scale += 0.1 }) }
変更点は以下のとおりです。
- State変数の追加
@State private var scale:Float = 1.0
:スケール率を管理するState変数を追加
- .update:クロージャを追加:
- RealityViewで最初に取得するcontentに対して、scaleを変更する
- State変数が変更されたら毎回呼び出される処理
- RealityViewで最初に取得するcontentに対して、scaleを変更する
- .gestureイベントを追加:
- TapGester()が終了したら、scaleを0.1増加
2. VisionPro動作確認
実行すると、以下のような挙動になります。
オブジェクトを見てTapすると、スケールが増加するインタラクションを確認できます。
3. DragGestureの追加
次に、Dragをすることで3Dコンテンツが回転する処理を実装します。
VolumeView.Swiftを、以下に修正します。
struct VolumeView: View { @State private var scale: Float = 1.0 @State private var rotationAngleX: Float = 0.0 @State private var rotationAngleY: Float = 0.0 @State private var lastTranslation: CGSize = .zero var body: some View { RealityView { content in // Generate ModelEntity let model = ModelEntity( mesh: .generateCone(height: 0.1, radius: 0.1), materials: [SimpleMaterial(color: .lightGray, isMetallic: true)] ) // Add components model.components.set(InputTargetComponent()) model.components.set(CollisionComponent(shapes: [.generateSphere(radius: 0.1)])) // Add ModelEntity to RealityView content content.add(model) } update: { content in if let model = content.entities.first { model.transform.scale = [scale, scale, scale] model.transform.rotation = simd_quatf(angle: rotationAngleY, axis: [0, 1, 0]) * simd_quatf(angle: rotationAngleX, axis: [1, 0, 0]) } } .gesture(TapGesture().targetedToAnyEntity().onEnded{ _ in scale += 0.1 }) .simultaneousGesture( DragGesture(minimumDistance: 0) .onChanged { value in let translation = value.translation if abs(translation.height) > 0 { rotationAngleX += Float(translation.height - lastTranslation.height) * 0.01 } if abs(translation.width) > 0 { rotationAngleY += Float(translation.width - lastTranslation.width) * 0.01 } lastTranslation = translation } .onEnded { _ in // ドラッグ終了時に変化量をリセット lastTranslation = .zero } ) }
変更点は以下のとおりです。
- State変数の追加
@State private var rotationAngleX: Float = 0.0
:@State private var rotationAngleY: Float = 0.0
:@State private var lastTranslation: CGSize = .zero
:
- .updateクロージャの修正
- Dragジェスチャーで取得したrotationAngleを用いて、モデルを回転させる処理を追加
- .simultaneousGestureイベントの追加
以下の回転処理のコードが少し特殊なので、補足します。
model.transform.rotation = simd_quatf(angle: rotationAngleY, axis: [0, 1, 0]) * simd_quatf(angle: rotationAngleX, axis: [1, 0, 0])
ここではsimd_quatf(angle:axis)関数を用いてmodelを回転させています。
引数であるangle:では、Float値で回転角度を指定しています。
axis:では回転させる座標軸を指定([0, 1, 0]の場合はY軸周り、[1, 0, 0]の場合はX軸周り)しています。
上記により、DagGestureで取得した上下左右の動きをモデルの回転に変換することで、直接物体をつかんで回転させているようなジェスチャを実現しています。
4. VisionPro動作確認
実行すると、以下のような挙動になります。
Dragしている手の動きに応じて、モデルが回転していることを確認できました。
終わりに
今回はSpatialComputingの入門編Part2として、VisionProにて導入された目および手を用いたインタラクションを実装しました。
今回はシステムが提供するジェスチャーを用いましたが、ARKitを用いることで独自のカスタムジェスチャーを実装することも可能です。ARKitの空間認識機能やアンカー機能を用いるには、今回実装したようなWindowやVolumeではなく、ImmersiveViewでの実装が必要になります。
次回Part3はImmersiveViewの実装方法をご紹介します。
現在、電通総研はweb3領域のグループ横断組織を立ち上げ、web3およびメタバース領域のR&Dを行っております(カテゴリー「3DCG」の記事はこちら)。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください!
私たちは一緒に働いてくれる仲間を募集しています!
参考文献
執筆:@yamashita.yuki、レビュー:@handa.kenta
(Shodoで執筆されました)