本記事では3Dモデルをドラッグ&ドロップできるようにする方法を紹介します。
デモ
実装
以下のコードは、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では、以下のように説明されています。
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にPhysicsBodyComponent
とPhysicsMotionComponent
を設定し、モデルに物理特性とモーション特性を追加することで、ドラッグ&ドロップも可能になります。
以下のコードでは、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を設定することで、物理特性とモーション特性を追加しています。
参考資料