Lab 035 – Teleport to viewpoints

We can adjust the users orientation in the scene by rotating a pivot entity. This will let us take on the viewpoint of a target entity.

Overview

Building on the concepts from Lab 034, we can use the tapped entity orientation in our calculations. We’re going to need a new pivot entity. We’ll make our scene content a child of the pivot. Then we will move the scene content relative to the pivot. We will also rotate the pivot to the inverse rotation of the target.

// Create a new pivot entity. We'll add our scene root as a child to this.
@State var scenePivot: Entity = Entity()
@State var sceneContent: Entity? // set with the root of our RCP scene

...

// Get the root from the RCP scene
if let sceneContent = scene.findEntity(named: "Root") {
  // Add the root level scene content as a child of the scene pivot
  self.scenePivot.addChild(sceneContent)
  self.sceneContent = sceneContent
}

When we tap on the gesture, we’ll move the scene content relative to the target, just like in Lab 034. Review that lab for details on the math. The difference here is that we’ll set the position relative to the new scene pivot entity.

sceneContent.setPosition([newPosition.x, 0, newPosition.z], relativeTo: scenePivot)

Now that we’ve moved, let’s adjust the orientation. We’ll rotate the pivot entity to the inverse of the orientation for the tapped target.

// Get the orientation of the tapped entity
let entityOrientation = value.entity.orientation

// To make the use face the same direction as the entity we need to rotate the pivot in the opposite direction
let inverseOrientation = entityOrientation.inverse

// set the orientation on the pivot entity to handle scene rotation
scenePivot.orientation = inverseOrientation

This works well, but there is an issue. We are only using TapGesture for this, which does not give us access to the input device pose. In other words, we don’t know if the user has physically turned to face another direction, so we can’t take that into account. In a future lab we will get the device pose and use it as part of these calculations.

Video Demo

Full Lab Code

struct Lab035: View {

    // Create a new pivot entity. We'll add our scene root as a child to this.
    @State var scenePivot: Entity = Entity()
    @State var sceneContent: Entity?

    var body: some View {
        RealityView { content in
            if let scene = try? await Entity(named: "TeleportLabs", in: realityKitContentBundle) {
                content.add(scene)
                content.add(self.scenePivot)

                // Get the scene content and stash it in state
                if let sceneContent = scene.findEntity(named: "Root") {
                    // Add the root level scene content as a child of the scene pivot
                    self.scenePivot.addChild(sceneContent)
                    self.sceneContent = sceneContent
                }

            }
        }
        .gesture(teleportTapViewpoint)
    }

    var teleportTapViewpoint: some Gesture {
        TapGesture()
            .targetedToAnyEntity()
            .onEnded { value in

                guard let sceneContent = self.sceneContent else { return }

                // Calculate the vector from the origin to the tapped position
                let vectorToTap = value.entity.position

                // Normalize the vector to get a direction from the origin to the tapped position
                let direction = normalize(vectorToTap)

                // Calculate the distance (or magnitude) between the origin and the tapped position
                let distance = length(vectorToTap)

                // Calculate the new position by inverting the direction multiplied by the distance
                let newPosition = -direction * distance

                // Move the sceneContent relative to the scenePivot
                // Update sceneOffset's X and Z components, leave Y as it is
                sceneContent.setPosition([newPosition.x, 0, newPosition.z], relativeTo: scenePivot)

                // Get the orientation of the tapped entity
                let entityOrientation = value.entity.orientation

                // To make the use face the same direction as the entity we need to rotate the world in the opposite direction
                let inverseOrientation = entityOrientation.inverse

                // set the orientation on the pivot entity to handle scene rotation
                scenePivot.orientation = inverseOrientation

            }
    }
}

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

Download the Xcode project with this and many more labs from Step Into Vision.

Questions or feedback?