Lab 045 – Entity Actions

Byte sized actions we can animate on entities.

Overview

I was looking over the RealityKit documentation and came across Entity actions. I recognized the names of the actions right away. These are the actions we can use in the Timelines feature of Reality Composer Pro. It hadn’t occurred to me to check of there was a code-version of these actions, but there is! Having access to these in code will simplify things for me. Timelines are still the best bet for sequencing multiple actions, but sometimes I just need a single action.

Basics:

  • action: what are we going to do to the entity? See the full list of actions
  • animation: pass the action to an Animation Resource using the type method makeActionAnimation. This method has a ton of options we can use to customize the animation
  • play the animation

Example: this simple spin action will rotate the subject entity around the Y axis

// Create the action
let action = SpinAction(revolutions: 1,
                        localAxis: [0, 1, 0],
                        timingFunction: .easeInOut,
                        isAdditive: false)

// Use the action in an animation
let animation = try AnimationResource.makeActionAnimation(for: action,
                                                          duration: 1,
                                                          bindTarget: .transform)

// Play the animation
subject.playAnimation(animation)

This lab contains the spin animation above, as well as examples to emphasize and adjust opacity. The FromToByAction is particularly interesting. We can use this to change a value over time. I’m using this to adjust the opacity, but we can also change other values like transform.

In the future, I hope Apple adds a lot more actions. I’d love to see a version of FromToByAction that can change a value on a component. This could cut down on the code where we get a component from an entity, change the value, and add it back.

Video Demo

Lab Code

struct Lab045: View {

    // Create the subject
    @State var subject: Entity = {
        let entity = ModelEntity(
            mesh: .generateBox(size: 0.2),
            materials: [SimpleMaterial(color: .stepRed, isMetallic: false)]
        )
        let collision = CollisionComponent(shapes: [.generateBox(size: .init(repeating: 0.2))])
        let input = InputTargetComponent()
        let hover = HoverEffectComponent()
        entity.components.set([collision, input, hover])
        entity.position.y = -0.3
        return entity
    }()

    @State var isFaded: Bool = false

    var body: some View {
        RealityView { content in

            // Add the subject to the scene
            content.add(subject)
        }

        // Create a toolbar with a button to perform the action
        .toolbar {
            ToolbarItem(placement: .bottomOrnament, content: {
                HStack {
                    Button(action: {
                        spinSubject()
                    }, label: {
                        Text("Spin")
                    })
                    Button (action: {
                        emphasizeSubject()
                    }, label: {
                        Text("Emphasize")
                    })
                    Button (action: {
                        fadeSubject()
                    }, label: {
                        Text("Fade")
                    })
                    Button (action: {
                        fadeBounceSubject()
                    }, label: {
                        Text("Delay Fade Bounce")
                    })
                }
            })
        }

    }

    func spinSubject() {
        Task {
            // Create the action
            let action = SpinAction(revolutions: 1,
                                        localAxis: [0, 1, 0],
                                        timingFunction: .easeInOut,
                                        isAdditive: false)

            // Use the action in an animation
            let animation = try AnimationResource.makeActionAnimation(for: action,
                                                                          duration: 1,
                                                                          bindTarget: .transform)

            // Play the animation
            subject.playAnimation(animation)
        }
    }

    func emphasizeSubject() {
        Task {
            let action = EmphasizeAction(motionType: .float, style: .basic)

            let animation = try! AnimationResource.makeActionAnimation(for: action,
                                                                       duration: 1,
                                                                       bindTarget: .transform)
            subject.playAnimation(animation)
        }
    }

    func fadeSubject() {
        isFaded.toggle()
        Task {
            let action = FromToByAction<Float>(to: isFaded ? 0.1 : 1,
                                               timing: .easeOut,
                                               isAdditive: false)

            let animation = try! AnimationResource.makeActionAnimation(for: action,
                                                                       duration: 1,
                                                                       bindTarget: .opacity)
            subject.playAnimation(animation)
        }
    }

    func fadeBounceSubject() {
        Task {
            let action = FromToByAction<Float>(to: 0.1,
                                               timing: .easeOut,
                                               isAdditive: false)

            let animation = try! AnimationResource.makeActionAnimation(for: action,
                                                                       duration: 1,
                                                                       bindTarget: .opacity,
                                                                       repeatMode: .autoReverse,
                                                                       delay: 1)

            subject.playAnimation(animation)
        }
    }

}

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?

One Comment

  1. Oh wow, I really like this syntax. I’ve been learning animations in Flutter / Dart recently and they’re waaaay more convoluted than that. That looks pretty elegant.