visionOS 开发 / 使用 RealityKit 播放动画(二)

了解如何使用 RealityKit 在 3D 模型上播放动画。

visionOS 开发 / 使用 RealityKit 播放动画(二)
Photo by Shubham Dhage / Unsplash

在上一篇文章中,我们已经了解了如何使用 RealityKit 播放 USDZ 模型的内置动画。本文将介绍如何为模型添加自定义的空间移动动画。

添加空间移动动画

你也可以使用 RealityKit 定义自定义动画,所有动画的本质都是对 modelEntity.transform 属性的修改/更新,包括:

  • translation(位置)
  • scale(缩放)
  •  rotation(旋转)

RealityKit 提供了两种为 Entity 对象添加空间移动动画的方式。对于简单的变换,可以使用 Entity 自带的基本方法。而对于高自定义、复杂的动画,可以使用 AnimationResource 来实现。

基本方法

常见的基本方法包括 movelook(at:)scale(to:)rotate(by:)

  • 适用于快速实现简单变换的场景。
  • 当需要单一的变换(位置、旋转、缩放)时,使用这些方法更方便。

例如,使用.move 方法来实现 3D 模型的移动,我们首先需要声明一个 transform 变量,并将 Entity 对象的 transform 属性赋值给它。这样可以确保在后续修改时,除了被修改的变量外,其他属性保持不变。

以下代码实现让 robot 模型沿 Z 轴方向移动 5 米:

// 更新模型位置的变换
var transform = robot.transform
transform.translation = [0.0, 0.0, 5.0] // 目标位置

// 使用 DispatchQueue.main.async 确保在主线程上执行
DispatchQueue.main.async {
    print("Moving robot to position: \(transform.translation)")
    if let parent = robot.parent {
        robot.move(to: transform, relativeTo: nil, duration: 25.0)
    } else {
        robot.move(to: transform, relativeTo: nil, duration: 25.0)
    }
}
0:00
/0:04

除了对 transform.translation 属性进行控制,我们还能同时控制 transform.scaletransform.rotation 属性。

通过调整,robot 模型将在 25 秒内平滑地移动到目标位置 [0.0, 0.0, 5.0],并同时放大到 2 倍,并沿着 Y 轴旋转方向:

/ 更新模型位置的变换
var transform = robot.transform
transform.translation = [0.0, 0.0, 5.0] // 目标位置
transform.rotation = simd_quatf(angle: .pi, axis: [0, 1, 0])
 transform.scale = [10.0, 10.0, 10.0] // 目标缩放

// 使用 DispatchQueue.main.async 确保在主线程上执行
DispatchQueue.main.async {
    print("Moving robot to position: \(transform.translation)")
    if let parent = robot.parent {
        robot.move(to: transform, relativeTo: nil, duration: 25.0)
    } else {
        robot.move(to: transform, relativeTo: nil, duration: 25.0)
    }
}
0:00
/0:05

look(at:)scale(to:)rotate(by:) 也可以方便的实现基础动画效果,我们在下一篇文章中再详细介绍使用方法。

AnimationResource

RealityKit 提供了多种动画类型,包括 FromToByAnimationBlendTreeAnimationOrbitAnimationSampledAnimation

  • 适用于需要复杂动画效果的场景。
  • 当需要组合多个动画或创建复杂动画序列时,使用这些动画资源可以提供更多的控制和灵活性。
💡
需要特别注意「动画类型」和「动画资源 (AnimationResource)」的区别。

FromToByAnimation 是一种动画类型,而 AnimationResource 是一种用来管理和播放这些动画的资源。要使用这些动画类型,通常需要创建动画定义,然后生成 AnimationResource,再将其应用到实体上。

下面这段代码,通过 FromToByAnimation 实现和 .move 方法相同的效果:

// 更新模型位置的变换
let initialTransform = robot.transform
var targetTransform = initialTransform
transform.translation = [0.0, 0.0, 5.0] // 目标位置
transform.rotation = simd_quatf(angle: .pi, axis: [0, 1, 0])
transform.scale = [10.0, 10.0, 10.0] // 目标缩放

// 定义 FromToByAnimation 动画
let animationDefinition = FromToByAnimation(
    to: targetTransform,
    duration: 25,
    bindTarget: .transform
)

// 生成 AnimationResource 并播放

let animationResource = try AnimationResource.generate(with: animationDefinition)

// 播放动画
robot.playAnimation(animationResource)
0:00
/0:04

AnimationResource 的一个主要优势在于它的复用性。一旦创建了一个动画资源,可以将其应用到多个模型上,而不需要为每个模型单独定义动画。这使得动画的管理和应用更加高效和灵活。

如果我加载了另一个 car 实体,我也可以直接将 animationResource 应用到该模型上:

// 更新模型位置的变换
let initialTransform = robot.transform
var targetTransform = initialTransform
transform.translation = [0.0, 0.0, 5.0] // 目标位置
transform.rotation = simd_quatf(angle: .pi, axis: [0, 1, 0])
transform.scale = [10.0, 10.0, 10.0] // 目标缩放

// 定义 FromToByAnimation 动画
let animationDefinition = FromToByAnimation(
    to: targetTransform,
    duration: 25,
    bindTarget: .transform
)

// 生成 AnimationResource 并播放

let animationResource = try AnimationResource.generate(with: animationDefinition)

// 播放动画
robot.playAnimation(animationResource)
car.transform.translation = [-0.5, 0, 0]
car.playAnimation(animationResource)
0:00
/0:04

灵活运用 FromToByAnimation 方法,可以创建更复杂、有趣的动画效果。通过学习 FromToByAnimation.move() 方法,你已经掌握了如何使用实体自带的基本方法以及更高阶的 AnimationResource 来为 3D 模型添加动画效果。

在下一篇文章中,我将更详细介绍 RealityKit 提供的其他基本方法和动画类型,例如 look(at:)OrbitAnimationSampledAnimation


使用 RealityKit,你可以轻松地播放内置动画或定义自定义动画,以创建动态和引人入胜的增强现实体验。