Search code examples
swiftswiftuirealitykitvisionosdraggesture

PhysicBodyComponent and DragGesture in RealityKit (visionOS)


I added some objects with a PhysicBodyComponent in my RealityView. So they have some physics to fall for example. Afterwards, I added some DragGesture to change the position of the objects. During the DragGesture, the object still try to fall during I change the position in the volumetric window.

import SwiftUI
import RealityKit
import RealityKitContent

struct ContentView: View {

    @State private var enlarge = false
    @State private var showImmersiveSpace = false
    @State private var immersiveSpaceIsShown = false
    
    @State private var newCubes = Entity()

    @Environment(\.openImmersiveSpace) var openImmersiveSpace
    @Environment(\.dismissImmersiveSpace) var dismissImmersiveSpace

    var body: some View {
        VStack {
            RealityView { content, attachments  in
                content.add(newCubes)
                var planeMaterial = PhysicallyBasedMaterial()
                planeMaterial.baseColor = PhysicallyBasedMaterial.BaseColor(tint: .brown)
                let planeGeometry: MeshResource = .generatePlane(width: 1, depth: 1)
                let plane = ModelEntity(mesh: planeGeometry, materials: [planeMaterial])
                plane.transform.translation.y = -0.5
                let materialPlane = PhysicsMaterialResource.generate(friction: 0.8, restitution: 0.0)
                plane.generateCollisionShapes(recursive: true)
                plane.components.set(PhysicsBodyComponent(shapes: plane.collision!.shapes, mass: 0, material: materialPlane, mode: .static))
                
                content.add(plane)
                
                var sphereMaterial = PhysicallyBasedMaterial()
                sphereMaterial.baseColor = PhysicallyBasedMaterial.BaseColor(tint: .blue)
                let sphereGeometry: MeshResource = .generateSphere(radius: 0.2)
                let sphere = ModelEntity(mesh: sphereGeometry, materials: [sphereMaterial])
                //CollisionComponent
                let collisionSphere = CollisionComponent(shapes: [.generateSphere(radius: 0.2)])
                //InputTargetComponent
                sphere.components.set(InputTargetComponent())
                sphere.components.set(collisionSphere)
                let material = PhysicsMaterialResource.generate(friction: 0.8, restitution: 0.0)
                //PhysicsBodyComponent
                sphere.components.set(PhysicsBodyComponent(shapes: sphere.collision!.shapes, mass: 1, material: material, mode: .dynamic))
                content.add(sphere)
                content.add(attachments.entity(for: "testAttachment")!)
            } update: { content,attachments  in
                //
            } attachments: {
                Attachment(id: "testAttachment") {
                    VStack{
                        Text("Hallong")
                        }
                    }
            }
            .gesture(DragGesture().targetedToAnyEntity().onChanged({ value in
                print("Angefasst")
                value.entity.position = value.convert(value.location3D, from: .local, to: value.entity.parent!)
            }))
            .gesture(TapGesture().targetedToAnyEntity().onEnded({ value in
                var cubeMaterial = PhysicallyBasedMaterial()
                cubeMaterial.baseColor = PhysicallyBasedMaterial.BaseColor(tint: .red)
                let cubeGeometry: MeshResource = .generateBox(size: 0.1)
                let cube = ModelEntity(mesh: cubeGeometry, materials: [cubeMaterial])
                //CollisionComponent
                let collisionCube = CollisionComponent(shapes: [.generateBox(width: 0.1, height: 0.1, depth: 0.1)])
                cube.components.set(collisionCube)
                //InputTarget
                cube.components.set(InputTargetComponent())
                //PhysicsBodyComponent
                let materialPhysicsCube = PhysicsMaterialResource.generate(friction: 0.8, restitution: 0.0)
                cube.components.set(PhysicsBodyComponent(shapes: cube.collision!.shapes, mass: 1, material: materialPhysicsCube, mode: .dynamic))
                newCubes.addChild(cube)
            }))
        }
        HStack{
            VStack{
                Text("Hallo")
            }
            VStack{
                Text("Zwei")
            }
        }
    }
}

#Preview(windowStyle: .volumetric) {
    ContentView()
}

How can I manage, that the force is disabled (or something else) during the DragGesture?


Solution

  • Kinematic vs Dynamic

    When the gesture is applied, you must have a Kinematic mode active, and immediately after ending the gesture, Dynamic mode should be turned on, in which a gravity will be activated.

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