Rotate Gesture 3D Improved

We can smooth out Rotate Gesture 3D by multiplying the gesture rotation by the initial orientation of the entity.

I left this note at the end of the example on Rotate Gesture 3D.

This doesn’t take into account the initial orientation of the entity. If the entity has a rotation value on the Y axis is anything other than 0, you will see the entity “snap” to the starting value of the gesture.

We can fix in much the same way that we fixed the drag and magnification gestures. We just need to:

  • Capture the initial orientation of the entity when the gesture starts
  • Multiply the gesture rotation by initial orientation
  • Clean up the state when the gesture ends
struct Example012: View {
    var body: some View {
        RealityView { content in
            // Load the scene from the Reality Kit bundle
            if let scene = try? await Entity(named: "GestureLabs", in: realityKitContentBundle) {
                content.add(scene)

                // Lower the entire scene to the bottom of the volume
                scene.position.y = -0.4
            }
        }
        .modifier(RotateGesture3DImproved())
    }


}

fileprivate struct RotateGesture3DImproved: ViewModifier {

    @State var isRotating: Bool = false
    @State var initialOrientation:simd_quatf = simd_quatf(
        vector: .init(repeating: 0.0)
    )

    func body(content: Content) -> some View {
        content
            .gesture(
                RotateGesture3D(constrainedToAxis: .y)
                    .targetedToAnyEntity()
                    .onChanged { value in

                        // Cache the entity's initial orientation when the gesture starts
                        if !isRotating {
                            isRotating = true
                            initialOrientation = value.entity.transform.rotation
                        }

                        let rotation = value.rotation
                        let rotationTransform = Transform(AffineTransform3D(rotation: rotation))

                        // Multiply the initial orientation by the gesture rotation
                        value.entity.transform.rotation = initialOrientation * rotationTransform.rotation

                    }
                    .onEnded { value in
                        // Clean up when the gesture has ended
                        isRotating = false
                        initialOrientation = simd_quatf(
                            vector: .init(repeating: 0.0)
                        )
                    }
            )
    }
}

There may be a better default/reset value for initialOrientation, but this does the trick for me.

Video demo

Support our work so we can continue to bring you new examples and articles.

Questions or feedback?