动画是增强虛拟元素真实感和生动性的重要方面,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