RealityKit Entity rotation animation doesn't go beyond 180 degree?
Asked Answered
M

2

2

I use simple transform and move method to animate my entity. Something like this:

let transform = Transform(scale: .one, 
                       rotation: simd_quatf(angle: .pi, axis: [0,0,1]), 
                      translate: .zero)

myEntity.move(to: transform, relativeTo: myEntity, duration: 1)

All is well, but when I try to rotate any more than 180 degree, the rotation stays still ?

How do I animate something that wants to turn 360 degree?

Mashhad answered 19/7, 2023 at 9:25 Comment(1)
as reported here the animation will always take the shortest path to get to the correct transform. so if you set two pi it will cancel out to zero. as a solution you could chain two 180° transforms, the first will get you half way and the second full aroundMadonia
R
2

Concatenating transform animations in RealityKit

RealityKit 2023 transform animation is still imperfect today. However, you can sequentially merge several move(to:) methods to go beyond 180 degree rotation barrier. Internally several rotation animations are merged into one smooth animation. I firmly believe that the reason for the 180 degree barrier is some kind of RealityKit's quaternion limitation. This solution works in iOS and visionOS.

visionOS app version for Xcode 15 Simulator.

import SwiftUI
import RealityKit

struct ContentView: View {
        
    var body: some View {
        RealityView { content in
            let model = ModelEntity(mesh: .generateBox(size: 0.1))
            content.add(model)
        } update: { content in
            if let model = content.entities.first {
                
                // 540 degree CCW rotation in 4 seconds (about Z axis)
                model.move(to: .init(roll: .pi), relativeTo: model,
                     duration: 4.0, timingFunction: .linear)
                model.move(to: .init(roll: .pi), relativeTo: model,
                     duration: 4.0, timingFunction: .linear)
                model.move(to: .init(roll: .pi), relativeTo: model,
                     duration: 4.0, timingFunction: .linear)
            }
        }
    }
}
#Preview {
    ContentView()
}

enter image description here

This post is also helpful.


SwiftUI animation for Model3D view

Also, you can prototype a scene in Reality Composer Pro app (do not forget to apply InputTarget and Collision components to 3D model) and then you'll be able to rotate a view with tap gesture using .rotation3DEffect(...) modifier, where you can set a pivot (a.k.a. anchor) point.

import SwiftUI
import RealityKit
import RealityKitContent

struct ContentView: View {
    @State private var rotation: Angle = .zero
    
    var body: some View {
        Model3D(named: "Scene", bundle: realityKitContentBundle)
            .onTapGesture {
                withAnimation(.smooth(duration: 0.5)) {
                    rotation.degrees -= 540
                }
            }
            .rotation3DEffect(rotation, axis: .z, anchor: .center)
    }
}
#Preview {
    ContentView()
}

enter image description here

Rider answered 20/7, 2023 at 16:9 Comment(4)
This is incredibly helpful, Andy. Thank you. I am curious though, you open up a scene which likely has both the components added. How does one open a model and add those components in code? IE: Model3D(named: "skull", bundle: realityKitContentBundle)...Wreck
@Zach, please post it as a question.Rider
Done! #76796501Wreck
Thank you @AndyJazz this is super helpful I was just wondering how to do chain animations on RealityKit and of course you wrote the answer. Apple should pay you royalties.Gan
R
0

Entity current angle can be used like this:

let currentRotationAngle = transform.rotation.angle
let willBeAngle = currentRotationAngle + .pi // add .pi to the current entity rotation angle

let transform = Transform(scale: .one,
                   rotation: simd_quatf(angle: willBeAngle, axis: [0,0,1]),
                  translate: .zero)

myEntity.move(to: transform, relativeTo: nil, duration: 1). // use nil instead of entity

If you wanna continue to rotate you cannot use this approach until RealityKit will enable us in new versions hopefully. It will be better to use global variable to keep the current angle:

...
// globalRotationAngle can be defined in the ObservableObject
var globalRotationAngle:Float = 0
...

globalRotationAngle = globalRotationAngle + .pi // add .pi to the current entity rotation angle

let transform = Transform(scale: .one,
                   rotation: simd_quatf(angle: globalRotationAngle, axis: [0,0,1]),
                  translate: .zero)

myEntity.move(to: transform, relativeTo: nil, duration: 1). // use nil instead of entity
Reconsider answered 18/1, 2024 at 14:16 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.