iOS ARKit 动画
动画是增强虛拟元素真实感和生动性的重要方面,RealityKit 支持变換动面(Transform Animation)和骨骼动画(Skeletal Animation)两种动面模式。变换动画一般程序化地执行,支持基本的平移、旋转、缩放,更复杂的动画通常由第三方模型制作软件采用骨骼绑定的方式生成,独立或者内置于模型文件中。USDZ和 Reality 文件格式都支持动画,在使用时,可以直接由该类文件将动画导人场景中。
- 变换动画 TransForm
变换动画可以实现对虚拟元素常见的基本操作,如平移、旋转、缩放,在执行时,通常使用实体类的move(to: relativeTo:duration:)方法,该方法参数 duration 用于指定动画时间。
import SwiftUI import RealityKit import ARKit struct TransformView : View { var body: some View { return ARViewContainer14().edgesIgnoringSafeArea(.all) } } struct ARViewContainer14: UIViewRepresentable { func makeUIView(context: Context) -> ARView { let arView = ARView(frame: .zero) let config = ARWorldTrackingConfiguration() config.planeDetection = .horizontal arView.session.run(config, options:[ ]) arView.session.delegate = arView arView.createPlane14() return arView } func updateUIView(_ uiView: ARView, context: Context) { } } var cubeEntity : ModelEntity? var gestureStartLocation: SIMD3<Float>? extension ARView{ func createPlane14(){ let planeAnchor = AnchorEntity(plane:.horizontal) do { let cubeMesh = MeshResource.generateBox(size: 0.1) var cubeMaterial = SimpleMaterial(color:.white,isMetallic: false) cubeMaterial.color = try SimpleMaterial.BaseColor(tint:UIColor.yellow.withAlphaComponent(0.9999), texture: MaterialParameters.Texture(TextureResource.load(named: "Box_Texture.jpg"))) cubeEntity = ModelEntity(mesh:cubeMesh,materials:[cubeMaterial]) cubeEntity!.generateCollisionShapes(recursive: false) cubeEntity?.name = "this is a cube" planeAnchor.addChild(cubeEntity!) self.scene.addAnchor(planeAnchor) self.installGestures(.all,for:cubeEntity!).forEach{ $0.addTarget(self, action: #selector(handleModelGesture)) } } catch { print("找不到文件") } } @objc func handleModelGesture(_ sender: Any) { switch sender { case let rotation as EntityRotationGestureRecognizer: rotation.isEnabled = false var transform = rotation.entity!.transform transform.rotation = simd_quatf(angle: .pi*1.5, axis: [0, 1, 0]) rotation.entity!.move(to: transform, relativeTo: nil, duration: 5.0) rotation.isEnabled = true case let translation as EntityTranslationGestureRecognizer: translation.isEnabled = false var transform = translation.entity!.transform transform.translation = SIMD3<Float>(x: 0.8, y: 0, z: 0) translation.entity!.move(to:transform,relativeTo:nil,duration:5.0) translation.isEnabled = true case let Scale as EntityScaleGestureRecognizer: Scale.isEnabled = false var scaleTransform = Scale.entity!.transform scaleTransform.scale = SIMD3<Float>(x: 2, y: 2, z: 2) Scale.entity!.move(to:scaleTransform,relativeTo:nil,duration:5.0) Scale.isEnabled = true default: break } } @objc func handleScaleGesture(_ sender : EntityScaleGestureRecognizer){ print("in scale") } } #if DEBUG struct TransformView_Previews : PreviewProvider { static var previews: some View { ARViewContainer14() } } #endif
在使用 move()方法进行变换动画之前,应当先设置需要达到的目标,利用duration 参数控制动画时长。move()方法另一个重载 move(to:relativeTo:duration:timingFunction:)版本,其参数 timingFunction为 Animation TimingFunction 类型,通过它可以指定动画效果,如线性(linear)、缓入(easeln)、缓出(easeOut)、缓入缓出(easelnOut)、三次贝赛尔曲线(cubicBezier),通过使用该方法可以改善动画体验。
- 骨骼动画
变换动画只适合于执行相对简单的动画操作,如控制灯光沿圆形轨道移动、用户单击模型时出现弹跳效果等,对于复杂的动画,一般使用第三方软件(Maya、3ds MAX 等)预先制作好骨骼动画,然后导出为USDZ 或Reality 格式文件供 ARKit 使用。在RealityKit 中,使用骨骼动画的典型代码如代码下所示。
import SwiftUI import RealityKit import ARKit struct BoneAnimationView : View { var body: some View { return ARViewContainer9().edgesIgnoringSafeArea(.all) } } struct ARViewContainer9: UIViewRepresentable { func makeUIView(context: Context) -> ARView { let arView = ARView(frame: .zero) let config = ARWorldTrackingConfiguration() config.planeDetection = .horizontal arView.session.run(config, options:[ ]) arView.session.delegate = arView arView.CreateRobot() return arView } func updateUIView(_ uiView: ARView, context: Context) { } } extension ARView{ func CreateRobot(){ let planeAnchor = AnchorEntity(plane:.horizontal) do { let robot = try ModelEntity.load(named: "toy_drummer") planeAnchor.addChild(robot) robot.scale = [0.01,0.01,0.01] self.scene.addAnchor(planeAnchor) print("Total animation count : \(robot.availableAnimations.count)") robot.playAnimation(robot.availableAnimations[0].repeat()) } catch { print("找不到USDZ文件") } } } #if DEBUG struct BoneAnimationView_Previews : PreviewProvider { static var previews: some View { BoneAnimationView() } } #endif
具体代码地址:https://github.com/duzhaoquan/ARkitDemo.git