The following RealityView ModelEntity animated text works in visionOS 1.0. In visionOS 2.0, when running the same piece of code, the model entity move duration does not seem to work. Are there changes to the way it works that I am missing? Thank you in advance.
RealityView { content in
let textEntity = generateMovingText()
content.add(textEntity)
_ = try? await arkitSession.run([worldTrackingProvider])
} update: { content in
guard let entity = content.entities.first(where: { $0.name == .textEntityName}) else { return }
if let pose = worldTrackingProvider.queryDeviceAnchor(atTimestamp: CACurrentMediaTime()) {
entity.position = .init(
x: pose.originFromAnchorTransform.columns.3.x,
y: pose.originFromAnchorTransform.columns.3.y,
z: pose.originFromAnchorTransform.columns.3.z
)
}
if let modelEntity = entity as? ModelEntity {
let rotation = Transform(rotation: simd_quatf(angle: -.pi / 6, axis: [1, 0, 0])) // Adjust angle as needed
modelEntity.transform = Transform(matrix: rotation.matrix * modelEntity.transform.matrix)
let animationDuration: Float = 60.0 // Adjust the duration as needed
let moveUp = Transform(scale: .one, translation: [0, 2, 0])
modelEntity.move(to: moveUp, relativeTo: modelEntity, duration: TimeInterval(animationDuration), timingFunction: .linear)
}
}
The source is available at the following:
Hi @jamesboo
Really cool project!
I took a look and this appears to be a race condition. For the first few frames worldTrackingProvider.queryDeviceAnchor
returns a position of (0, 0, 0). I'm not sure if this is or isn't a bug. In the meantime, the path forward is to wait until worldTrackingProvider.queryDeviceAnchor
returns a transform with a non zero position. Here's the modified code to do this. For brevity I'm omitting some the unchanged code.
struct ImmersiveView: View {
let worldTrackingProvider = WorldTrackingProvider()
let arkitSession = ARKitSession()
@State var textPosition:simd_float3?
var body: some View {
ZStack {
RealityView { content in
let textEntity = generateCrawlingText()
content.add(textEntity)
} update: { content in
guard let textPosition,
let entity = content.entities.first(where: { $0.name == .textEntityName}) else { return }
entity.position = textPosition
if let modelEntity = entity as? ModelEntity {
let rotation = Transform(rotation: simd_quatf(angle: -.pi / 6, axis: [1, 0, 0])) // Adjust angle as needed
modelEntity.transform = Transform(matrix: rotation.matrix * modelEntity.transform.matrix)
let animationDuration: Float = 60.0 // Adjust the duration as needed
let moveUp = Transform(scale: .one, translation: [0, 2, 0])
modelEntity.move(to: moveUp, relativeTo: modelEntity, duration: TimeInterval(animationDuration), timingFunction: .linear)
}
}
Starfield()
}
.task {
// Wait up to 2 seconds for a non zero position.
let pauseInMilliseconds:UInt64 = 100
let maxTries = 20
_ = try? await arkitSession.run([worldTrackingProvider])
for _ in 0..<maxTries {
if let transform = worldTrackingProvider.queryDeviceAnchor(atTimestamp: CACurrentMediaTime())?.originFromAnchorTransform {
let proposedTextPosition = simd_make_float3(transform.columns.3)
if proposedTextPosition != .zero {
textPosition = proposedTextPosition
break
}
}
try? await Task.sleep(nanoseconds: 1_000_000 * pauseInMilliseconds)
}
}
}
}