Spatial Tap Gesture

Spatial Tap Gesture adds the location of the tap on the entity. We can use this to place indicators or spawn new models in our scene.

For most cases, we can use Tap Gesture to work with 3D content. As long as we use targetedToAnyEntity, Tap Gesture will return an entity along with its root and any children. We can use that data to move entities, run animations, etc. But what if we want to place an indicator at the location where we tapped?

SpatialTapGesture is a variant of TapGesture that adds data for the location of the tap. It provides both location (CGPoint) and location3D (Point3D). In this example I’ll use location3D.

The subject is a cube with input and collision components. It’s important to keep in mind that the tap takes place on the surface of the collision, not necessarily the surface of the mesh. It may be tricky to use this with complex meshes that don’t line up with their collision components.

struct Example005: View {

    static let info = ItemInfo(
        title: "Spatial Tap Gesture",
        description: "Expanding on the Spatial version of tap gesture with RealityKit",
        scene: .volume
    )

    @State var subject: Entity?
    @State var indicator: Entity?

    var body: some View {
        RealityView { content in
        
            // An entity we can tap on to place our indicator
            guard let cube = try? await Entity(named: "StepCube") else { return }
            content.add(cube)
            
            // We will place this indicator at the location of our taps
            let indicatorModel = ModelEntity(
                mesh: .generateSphere(radius: 0.025),
                materials: [SimpleMaterial(color: .black, isMetallic: false)])

            cube.components.set(HoverEffectComponent())
            cube.addChild(indicatorModel)
            subject = cube
            indicator = indicatorModel
        }
        .gesture(spatialTapExample)
    }

    var spatialTapExample: some Gesture {
        SpatialTapGesture()
            .targetedToAnyEntity()
            .onEnded { value in

                if let subject = subject, let indicator = indicator {
                    // Convert the location3D value to the coordinate space of the subject
                    // Place the indicator on the surface of the subject
                    let tappedPosition = value.convert(value.location3D, from: .local, to: subject)
                    indicator.position = tappedPosition
                }

            }
    }
}

When we tap the subject in this lab, we convert the location3D value to the local coordinate space of the subject. Then we use that value to position the indicator.

Video Demo

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

Questions or feedback?