Automatically animating RealityKit entities
Invoke implicit animations by setting the entity’s desired end state.
Overview
When you build 3D experiences with RealityKit, you often animate changes to an entity’s position, rotation, scale, or other properties. You can animate entity properties explicitly by creating an AnimationResource. RealityKit can also animate changes automatically using implicit animations, which let you describe only the desired end state inside a closure.
RealityKit offers two closure-based approaches to implicit animation:
- animate(_:body:completion:)
A type method you call directly on Entity. You pass in an Animation, and RealityKit animates any property changes you make inside the
bodyclosure. Use this approach when your animation doesn’t depend on SwiftUI state.- animate(body:completion:)
An instance method on the
contentparameter inside a RealityView update closure. This approach picks up the animation from the current SwiftUI transaction. You control the animation by binding it to the State variable that triggers the update.
Both approaches can animate properties for the following component types:
Animate entities without SwiftUI state
Use animate(_:body:completion:) to animate entity properties without any SwiftUI state binding. Pass an Animation value and a closure that sets the properties you want to change. RealityKit interpolates each property from its current value to the new one using the animation you specify. Often, you can get the animation you want by using one of the static type classes on Animation, like linear or easeOut, rather than manually building a custom Animation.
The following example moves an entity to a new position over one second using a linear animation to give a consistent speed throughout:
Entity.animate(.linear(duration: 1.0)) {
entity.position = SIMD3<Float>(0, 1.5, -2)
}The animate(_:body:completion:) method works without a RealityView update closure or a SwiftUI state binding.
Animate entities in a RealityView update closure
Use animate(body:completion:) inside a RealityView update closure to animate entity changes in response to SwiftUI state changes. Because this method derives its animation from the current SwiftUI transaction, attach an animation to the State variable that triggers the update.
Bind the animation to your state variable using the animation(_:) modifier on a Binding.
@State private var isScaled = false
var body: some View {
VStack {
RealityView { content in
// Add entities during initial setup.
} update: { content in
guard let entity = content.entities.first else { return }
content.animate {
if isScaled {
entity.components[Transform.self]?.scale = SIMD3<Float>(repeating: 2)
} else {
entity.components[Transform.self]?.scale = SIMD3<Float>(repeating: 1)
}
}
}
Toggle("Scale", isOn: $isScaled.animation(.linear(duration: 1.0)))
}
}The .animation(.linear(duration: 1.0)) modifier on $isScaled attaches an animation to the binding. When isScaled changes, RealityView calls the update closure within a transaction that carries this animation. The animate(body:completion:) call applies that animation to the property changes inside the closure.
Animate multiple properties
You can change multiple properties inside a single animate closure. RealityKit creates a separate animation for each property and drives them all with the same timing.
The following example animates an entity’s position, scale, and opacity at the same time:
Entity.animate(.easeInOut(duration: 1.5)) {
entity.components[Transform.self]?.translation = SIMD3<Float>(0, 2, -3)
entity.components[Transform.self]?.scale = SIMD3<Float>(repeating: 1.5)
entity.components[OpacityComponent.self]?.opacity = 0.5
}Respond to animation completion
Both animate(_:body:completion:) and animate(body:completion:) accept an optional completion closure. RealityKit calls this closure when the animation finishes. The completion closure also runs if a new animation begins on the same property before the current animation finishes.
Use the completion handler to chain animations or trigger follow-up logic. The following code example moves the entity to a new position, and the completion handler moves the entity back to the origin:
Entity.animate(.easeIn(duration: 0.8)) {
entity.components[Transform.self]?.translation = SIMD3<Float>(0, 1.5, -2)
} completion: {
Entity.animate(.easeOut(duration: 0.8)) {
entity.components[Transform.self]?.translation = .zero
}
}