見出し画像

【visionOS】RealityViewでVision Proの画面上に3Dオブジェクトを表示させる

RealityKit

RealityKitは、写実的レンダリング、カメラエフェクト、アニメーション、物理シミュレーションなどが備わった、拡張現実専用に一から開発されたフレームワークです。ネイティブのSwift API、ARKitとの統合、驚くほどリアルな物理ベースのレンダリング、変換および骨格ベースのスケルタルアニメーション、空間オーディオ、剛体力学などにより、RealityKitがAR開発をこれまで以上に迅速かつ容易なものにします。

https://developer.apple.com/jp/augmented-reality/realitykit/

ということでRealityKitを使用すると3DオブジェクトをVision Pro上に表示させることができます。
またRealityKitのRealityViewを使用することでSwiftUIのView内にvisionOS 上に3Dオブジェクトを表示させられるので試してみます。

A SwiftUI view for displaying RealityKit content on visionOS

https://developer.apple.com/documentation/realitykit/

ドキュメントは以下です。

サンプルコードの通りModelEntityを使用して表示します。
とりあえず一番簡単に3Dオブジェクトを表示させてみたいのでMeshResourceCreating a Boxを参考に試してみます。

import SwiftUI
import RealityKit

struct Sample2: View {
    var body: some View {
        VStack {
            RealityView { content in
                let box = ModelEntity(mesh: .generateBox(size: 0.1))
                content.add(box)
            }
        }
    }
}

たった数行で3Dオブジェクトを表示させることができました。
正方形以外表示させたい場合は'.generateBox(width: 0.2, height: 0.1, depth: 0.3)'で任意の辺の長さを定義できます。

3Dオブジェクトをタップをする

表示できたので3Dオブジェクトに対してタップして何かしらのアクションを行ってみます。
従来のSwiftUIではButtonもしくはonTapGestureで行うことができるので試してみます。

struct Sample2: View {
    @State var flag = false
    var body: some View {
        VStack {
            Text(flag ? "true" : "false")
                .font(.extraLargeTitle)
            Button(action: {
                flag.toggle()
            }, label: {
                RealityView { content in
                    let box = ModelEntity(mesh: .generateBox(size: 0.1))
                    content.add(box)
                }
            })
            RealityView { content in
                let box = ModelEntity(mesh: .generateBox(size: 0.1))
                content.add(box)
            }
            .onTapGesture {
                flag.toggle()
            }
        }
    }
}

Buttonはタップするとtrue/falseとテキストが変更しますがUIが変更されています。
onTapGestureは反応しません。
TapGestureに関してはRespond to interactions with RealityKit contentに記載されています。
実際にミニマムで検証したコードは以下の通りです。

RealityView { content in
    let model = ModelEntity(mesh: .generateBox(size: 0.1))
    model.components.set(InputTargetComponent())
    model.components.set(CollisionComponent(shapes: [ShapeResource.generateBox(size: SIMD3<Float>(repeating: 0.1))]))
    content.add(model)
}
.gesture(
    SpatialTapGesture()
        .targetedToAnyEntity()
        .onEnded { _ in
            flag.toggle()
        }
)

これで3Dオブジェクトがタップされたことを検知することができます。

タップ機能を実装するにはInputTargetComponentとCollisionComponentを必ずcomponents.setにセットすることとSpatialTapGesture()もしくはTapGesture()に対してtargetedToAnyEntityもしくはtargetedToEntity(任意のEntityをターゲットにする場合に使用する)を追加する必要があります。
これで3Dオブジェクトがタップされたことを検知することができます。

いいねと思えたらよろしくお願いします😋