LoginSignup
1
0

【visionOS】3Dモデルをドラッグ&ドロップできるようにする方法

Posted at

本記事では3Dモデルをドラッグ&ドロップできるようにする方法を紹介します。

デモ

画面収録 2024-05-13 18.11.22.gif

実装

以下のコードは、RealityView内で3Dモデルを表示し、モデルをドラッグできるようにする実装です。

struct ImmersiveView: View {
    var body: some View {
        RealityView { content in
            if let entity = try? await ModelEntity(named: "Ball")
                {
                entity.generateCollisionShapes(recursive: false)
                entity.components.set(InputTargetComponent())
                content.add(entity)
            }
        }
        .gesture(dragGesture)
    }
    
    private var dragGesture: some Gesture {
        DragGesture()
            .targetedToAnyEntity()
            .onChanged { value in
                value.entity.position = value.convert(value.location3D, from: .local, to: value.entity.parent!)
            }
    }
}

DragGesture() を使用して、エンティティをドラッグできるようにします。onChanged クロージャ内でエンティティの位置を更新します。

RealitiyViewでイベントの入力を受け取るには、ModelEntityにInputTargetComponentとCollisionComponentの両方を設定する必要があります。

Reality Composer Proを使用してアセットに直接コンポーネントを指定することもできますが、この例ではコードで設定しています。

WWDCのセッションBuild spatial experiences with RealityKit - WWDC23では、以下のように説明されています。

スクリーンショット 2024-05-10 16.33.28.png

You can add a gesture to a RealityView, like any other SwiftUI view, and it will hit test against entities in that view. To receive input, the entity must have both an input target component and a collision component. When a touch event is handled by RealityView, it will ignore any entities that don't have both collision and an input target. Only this last entity has both components, so gestures added to this RealityView will only react to input directed at this entity.

また、ModelEntityにPhysicsBodyComponentPhysicsMotionComponentを設定し、モデルに物理特性とモーション特性を追加することで、ドラッグ&ドロップも可能になります。

画面収録 2024-05-13 18.12.13 (1).gif

以下のコードでは、RealityKitを使用して床とボールの3Dモデルを生成し、ユーザーがボールをドラッグ&ドロップできるようにしています。

struct ImmersiveView: View {

    var body: some View {
        RealityView { content in
            let floor = ModelEntity(mesh: .generatePlane(width: 50, depth: 50), materials: [OcclusionMaterial()])
            floor.generateCollisionShapes(recursive: false)
            floor.components[PhysicsBodyComponent.self] = .init(
                massProperties: .default,
                material: .generate(staticFriction: 0.8, dynamicFriction: 0.7, restitution: 0.2),
                mode: .static
            )
            content.add(floor)

            if let entity = try? await ModelEntity(named: "Ball")
                {
                entity.generateCollisionShapes(recursive: false)
                entity.components.set(InputTargetComponent())
                entity.components[PhysicsBodyComponent.self] = .init(
                    massProperties: .default,
                    material: .generate(staticFriction: 0.6, dynamicFriction: 0.2, restitution: 0.5),
                    mode: .dynamic
                )
                entity.components[PhysicsMotionComponent.self] = .init()
                content.add(entity)
            }
        }
        .gesture(dragGesture)
    }

    private var dragGesture: some Gesture {
        DragGesture()
            .targetedToAnyEntity()
            .onChanged { value in
                value.entity.position = value.convert(value.location3D, from: .local, to: value.entity.parent!)
                value.entity.components[PhysicsBodyComponent.self]?.mode = .kinematic
            }
            .onEnded { value in
                value.entity.components[PhysicsBodyComponent.self]?.mode = .dynamic
            }
    }
}

床とボールのモデルには、PhysicsBodyComponentとPhysicsMotionComponentを設定することで、物理特性とモーション特性を追加しています。

参考資料

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0