How to Play Timeline Animations via code

Hi everyone, I need to synchronize the playback of RealityKit Timelines via SharePlay.

To do this I am trying to get the references of the timelines using "AnimationPlaybackController" and "AnimationResource". In my realitykit scene I have configured both an animation (with blender), and a timeline, the animation starts correctly when the realitykit scene starts, the timeline not.

Below the code:

struct ContentView: View {
    
    @State private var subscriptions = [EventSubscription]()
    
    @Environment(AppModel.self)
      private var appModel
    
    let rootEntity = Entity()
    
    @State var testEntity: Entity?
    @State var testAnimation: AnimationResource?
    @State var testController: AnimationPlaybackController?
    
    init() {
        CubeComponent.registerComponent()
    }

    var body: some View {
        RealityView { content in
            content.add(rootEntity)
            if let scene = try? await Entity(named: "Room", in: realityKitContentBundle) {
                rootEntity.addChild(scene)
                playAnimations(from: content)
            }
        }
        .gesture(SpatialTapGesture().targetedToAnyEntity()
            .onEnded({ value in
                _ = value.entity.applyTapForBehaviors()
                if let testEntity, let testAnimation {
                    testController = testEntity.playAnimation(testAnimation.repeat())
                }
            })
        )
    }
    
    func playAnimations(from content: RealityViewContent) {
        subscriptions.append(content.subscribe(to: ComponentEvents.DidAdd.self, componentType: AnimationLibraryComponent.self, { event in
            let entity = event.entity
            entity.components[AnimationLibraryComponent.self]?.animations.forEach({ (key, value) in
                if value.definition is AnimationGroup {
                    if key == "/Room/TestTimeline" {
                        let controller = entity.playAnimation(value.repeat())
                        testEntity = entity
                        testAnimation = value
                        appModel.syncronizedAnimations[key] = .init(name: key, animationController: controller, entityName: entity.name)
                    }
                } else {
                    if entity.name == "SphereInteractable" {
                        let controller = entity.playAnimation(value.repeat())
                        appModel.syncronizedAnimations[key] = .init(name: key, animationController: controller, entityName: entity.name)
                    }
                }
            })
        }))
    }
}
  • the variables testEntity, testAnimation and testController are for testing purposes only. If I try to start the animations in the playAnimations function, only the animation created via blender starts (the one related to the object "SphereInteractable"), the Timeline starts only if I save a reference and I play it with a tap gesture or with a delay of ! seconds with DispatchQueue.asyncAfter called in the onAppear.

is there a better way to handle this? The goal is to have a reference of the AnimationPlaybackController of the timeline, in order to sync the animation via shareplay.

Thanks

Hello @poool88

Consider a timeline trigger. Compose interactive 3D content in Reality Composer Pro does a good job covering the subject. This post and this post contain code snippets for invoking triggers via NotificationCenter.

thanks for the reply, but I don't think this approach helps me. I need that the animations timing are synced via shareplay. If I join a shareplay session where two devices are experiencing the timeline, I have to be sure that on the third device the animation is viewed at the same time index in the other two (without restarting it on the devices that already are in the multiplayer session). With a notification trigger can I pass a timestamp from which to start the animation? I don't find any reference in documentation.

Hello @poool88

I've modified your playAnimations function to show how you can get a reference to the AnimationPlaybackController that controls your timeline animation:

func playAnimations(from content: RealityViewContent) {
    subscriptions.append(content.subscribe(to: ComponentEvents.DidAdd.self, componentType: AnimationLibraryComponent.self, { event in
        if let animationLibraryComponent = event.entity.components[AnimationLibraryComponent.self], let animationResource = animationLibraryComponent.animations["/Room/TestTimeline"] {
            let controller = event.entity.playAnimation(animationResource, transitionDuration: 0, startsPaused: false)
            // do something with controller here, such as seek to a position using `controller.time`
        }
    }))
}

If this is not working, double check that the string you're using to play your animation is correct. You are passing in "/Room/TestTimeline", and typically the default name for the highest level entity is "Root", which is why I am asking you to double check. You can iterate through the animations and print out their key to find the exact string you should use:

for animation in animationLibraryComponent.animations {
    print(animation.key)
}

Note: You can ignore anything with the suffix "__auto_generated_looping"

Let me know if that helps!

How to Play Timeline Animations via code
 
 
Q