In an earlier beta, BillboardComponent
had rotationAxis
and upDirection
properties which allowed more fine-grained control of how an entity rotates towards the camera.
Currently, it is only possible to orient the z axis of the entity.
Looking at the robot in the documentation, the rotation of its z axis causes its feet to lift off the ground.
Before, it was possible to restrain the rotation to one axis (y, for example) so that the robot's feet stayed on the ground with
billboard.upDirection = [0, 1, 0] billboard.rotationAxis = [0, 1, 0]
Is there an alternative way to achieve this? Are these properties (or similar) coming back?
Hi @eddo
You're correct that those properties are not currently available. If you'd like us to consider adding more fine-grained control to BillboardComponent
, please file an enhancement request with Feedback Assistant.
That being said, you can still achieve a similar level of control with the look(at:from:upVector:relativeTo:forward:) method. For example, you can create a custom billboard component that constrains the rotation of an entity to the y-axis (so that the robot's feet stay planted on the ground) with the following steps.
First, define a custom billboard component:
struct CustomBillboardComponent: Component { var upVector: SIMD3<Float> = [0, 1, 0] var forward: Entity.ForwardDirection = .positiveZ }
Next, create a system which finds each entity with a CustomBillboardComponent
and rotates it to face the device with the look(at:from:upVector:relativeTo:forward:) method:
struct CustomBillboardSystem: System { let billboardQuery = EntityQuery(where: .has(CustomBillboardComponent.self)) private let arkitSession = ARKitSession() private let worldTrackingProvider = WorldTrackingProvider() public init(scene: RealityKit.Scene) { runSession() } func runSession() { Task { try? await arkitSession.run([worldTrackingProvider]) } } public func update(context: SceneUpdateContext) { // Check whether the world-tracking provider is running. guard worldTrackingProvider.state == .running else { return } // Query the device position at the current time. guard let deviceAnchor = worldTrackingProvider.queryDeviceAnchor(atTimestamp: CACurrentMediaTime()) else { return } let devicePosition = simd_make_float3(deviceAnchor.originFromAnchorTransform.columns.3) // Iterate through each entity with a custom billboard component and rotate them to face the device. for entity in context.entities(matching: billboardQuery, updatingSystemWhen: .rendering) { if let customBillboardComponent = entity.components[CustomBillboardComponent.self] { // Set the look at position to have the same y coordinate as the entity. // This ensures the entity only rotates around the [0, 1, 0] axis, preventing it from tilting up or down. var lookAtPosition = devicePosition lookAtPosition.y = entity.position.y // Make the entity point toward the look at position. entity.look(at: lookAtPosition, from: entity.position(relativeTo: nil), upVector: customBillboardComponent.upVector, relativeTo: nil, forward: customBillboardComponent.forward) } } } }
This system obtains the position of device each update via queryDeviceAnchor(atTimestamp:), which allows it to face the entities toward the device.
Finally, register the CustomBillboardSystem
and add the CustomBillboardComponent
to any entities you want to face toward the user:
RealityView { content in // Register the custom billboard system. CustomBillboardSystem.registerSystem() // Add the initial RealityKit content if let immersiveContentEntity = try? await Entity(named: "Immersive", in: realityKitContentBundle), let toyRobot = immersiveContentEntity.findEntity(named: "Toy_Robot") { toyRobot.components.set(CustomBillboardComponent()) content.add(immersiveContentEntity) } }
Here, I added the "Toy_Robot" model to the default "Immersive" scene in Reality Composer Pro so that I could apply the CustomBillboardComponent
to it. You can set the upVector
and forward
properties of the CustomBillboardComponent
to further tune the effect.
Let me know if you have any other questions!