在 visionOS 应用开发中,交互性和沉浸感是提升用户体验的关键因素。通过为 3D 模型添加手势控制,用户可以自然地与虚拟对象互动,使应用更具吸引力。
常见的手势交互包括缩放、平移和旋转,如何在应用中优雅地实现这种旋转手势?本文将详细介绍旋转手势,并提供一个易于实现且可复用的解决方案。
最终实现效果预览
我们将创建一个 dragRotation 修饰器组件,你可以将它应用到任何 SwiftUI 视图,或 RealityKit 渲染的 3D 模型上,使用户能够通过拖动手势旋转实体对象。以下是这个修饰器的实现效果:
用户可以左右旋转 3D 对象,也可以上下旋转。你也可以设置为,当用户尝试上下旋转时,组件会尝试自动复位。
创建 dragRotation 修饰器组件
手势识别涉及到计算和状态管理,实现代码量比较大。我们通过创建一个自定义的 dragRotation 修饰器组件,将手势操作封装起来,以便后续可以方便的使用它。
建议在 Xcode 项目中,创建一个 Gestures 文件夹,专用用于管理手势控制相关的组件。在这个文件夹中,创建 DragRotationModifier.swift 文件。
完整代码如下(該组件参考自 Apple 提供的 HelloWorld 演示项目,其提供了优秀的用户体验):
上述代码定义一个 SwiftUI 扩展 dragRotation,它是一个自定义的修饰器组件,允许用户通过拖动手势旋转对象。这个组件可以限制旋转的角度(yawLimit 和 pitchLimit),并根据用户的拖动动作调整旋转的灵敏度。
为 RealityView 添加 dragRotation
下面我像你演示,如何为 RealityKit 对象添加旋转控制,它非常的简单。
添加 .dragRotation() 修饰器
例如,我们使用 RealityView 加载一个模型:
将 .dragRotation()修改器添加到 RealityView,以启用它。
此时模型仍然不会对我们的手势做出响应,我们还需要为模型实体添加 InputTarget 和 Collision 组件,以便它能够检测到我们的手势输入。
理解 InputTarget 和 Collision 组件
在 RealityKit 中,一个 Entity 是一个基本的构建块,可以附加多个组件来扩展其功能。例如,为了让 Entity 能够渲染形状,我们可以将 .usdz 模型添加到 ModelComponent 组件。如果要实现空间音频,可以为 Entity 添加 SpatialAudio 组件。
同样的,为了让 Entity 能够正确地接收并响应用户输入,我们必须为模型添加 InputTarget 和 Collision 组件:
• InputTargett 使实体成为可以接受输入的目标。
• Collision 为实体提供了检测输入位置的必要边界。
InputTarget 组件说明
InputTargetComponent 是 RealityKit 中用于处理用户输入(例如点击、拖动、旋转等)的组件。当你将这个组件添加到实体时,你正在告诉 RealityKit,这个实体是可以接受输入的目标。
Collision 组件说明
generateCollisionShapes(recursive: true) 是用来为实体生成碰撞形状的。碰撞形状是虚拟的边界或体积,用于检测用户与实体的交互。例如,当你点击或拖动实体时,系统需要知道用户的输入是否与该实体发生了碰撞。
添加 InputTarget 和 Collision 组件
添加的方式有两种:
• 直接通过 Xcode 编程方式添加
• 通过 Reality Composer Pro 图形化方式添加
通过 Xcode 编程添加,我们只需添加两行代码。
通过将 InputTargetComponent 添加到实体,并设置 allowedInputTypes 为 .all,你允许该实体响应所有类型的用户输入(例如触摸、点击、拖动等)。如果没有这行代码,实体将不会响应任何用户输入,因为 RealityKit 不知道这个实体是一个可以处理输入的目标。
通过设置generateCollisionShapes生成碰撞形状使得 RealityKit 可以检测到用户输入的确切位置,并相应地更新模型的状态或动作。如果没有生成碰撞形状,即使实体能够接收输入(通过 InputTargetComponent),系统也无法正确检测到实体的边界,这意味着手势可能不会被正确处理或根本没有效果。
完整代码如下:
此时,模型应该已经可以响应手势输入:
通过 Reality Composer Pro 添加 InputTarget 和 Collision 组件
在 Reality Composer Pro 中选中你的模型文件,点击右侧 Add Component:
搜索并添加「Input Target」和「Collision」组件并保存:
回到 Xcode,即使我们注释掉添加 component 的代码,模型也能对我们的输入做出响应:
如果你在 Xcode 中选中模型,并选择查看源代码,会发现 Reality Composer Pro 也还是为 Entity 添加了这两个组件:
为 dragRotation 添加旋转限制
.dragRotation() 修饰器支持 yawLimit 和 pitchLimit(旋转限制)参数,这些参数用于设置旋转角度的限制,分别控制模型在 Y 轴(水平)和 X 轴(垂直)上的旋转范围。传入的值是一个 Angle 类型,当用户拖动模型超过该限制时,模型的旋转角度将不会继续增加。
当你希望用户只能在一定范围内查看模型,而不是无限制地旋转模型时,设置这些限制是非常有用的。例如,展示一个汽车模型时,你可能希望用户只能查看车身的正面和两侧,而不希望看到底部。
添加纵向限制:
添加纵向及横向限制:
控制旋转灵敏度
.dragRotation() 修饰器支持 sensitivity(灵敏度),这个参数控制旋转的灵敏度,即用户拖动多少距离会导致模型旋转多少角度。传入的值是一个 Double 类型,值越高,旋转越敏感;值越低,旋转越缓慢。
• 低灵敏度:sensitivity 的值较低,例如在 1 到 5 之间。
• 中等灵敏度:sensitivity 值在 5 到 20 之间。
• 高灵敏度:sensitivity 的值较高,例如 20 以上。
在需要更精细控制旋转时,增大灵敏度可以帮助用户更容易旋转模型。而在需要更大范围旋转时,可以降低灵敏度,防止模型因为微小的手势而快速旋转。
添加自动旋转控制
.dragRotation() 修饰器支持 axRotateClockwise 和 axRotateCounterClockwise 两个布尔类型的参数,分别用于控制模型顺时针和逆时针的轴向旋转。这两个参数可以用于实现自动旋转效果,通过外部事件(如按钮点击或其他触发条件)控制模型的旋转方向。
• axRotateClockwise 使模型顺时针旋转
• axRotateCounterClockwise 则让模型逆时针旋转。例如,在 UI 上提供按钮,用户点击按钮后,模型可以自动顺时针或逆时针旋转一定角度。这对于展示模型的不同角度或自动播放旋转动画非常有帮助。
希望这篇文章能帮助您在 visionOS 中更好地使用 Stack 组件进行布局。