Highlight
visionOS 游戏输入有三个层级:系统手势(默认选择)、组合手势(更精细控制)、自定义手势(ARKit hand tracking,仅 Full Space 可用),物理外设作为补充。
核心内容
做 visionOS 游戏时,输入方式的选择直接决定玩家能不能”拿起来就玩”。Apple Vision Pro 上,人们用眼睛和手来操作一切,但”手”的用法有两种:direct(伸手直接触碰虚拟物体)和 indirect(看一眼目标,手指捏合远距离操作)。Direct 适合高能量游戏,但长时间举手会很累;indirect 允许手放在身侧,小动作放大为大操作,更适合长时间游玩。Session 里 Loona 拼图游戏就是 indirect 的典型——看着拼图块捏住、拖动、双指轻点归位,站着坐着都能玩。Super Fruit Ninja 则走 direct 路线,直接挥砍飞来的水果,配合闪电拖尾和爆汁音效弥补没有 haptics 的缺憾。
Apple 的建议很直接:系统手势应该是默认选择。玩家已经会用系统手势操作整个平台,不需要你教;不需要额外设备,开箱即用;从 Shared Space 到 Full Space 全部支持。如果系统手势不够用,可以用 simultaneously(with:) 把多个手势组合起来。只有当系统手势和组合手势都无法满足需求时,才考虑用 ARKit 的 hand skeleton tracking 做自定义手势——但自定义手势只能在 Full Space 里使用,Shared Space 下 ARKit 不可用。物理外设(Xbox、PlayStation 手柄、蓝牙键盘/触控板)适合需要同时两个以上输入、或从其他平台移植的游戏,GameController 框架在 iOS、iPadOS、macOS、visionOS 上完全一致。
详细内容
系统手势:最简单的起点
visionOS 提供的系统手势包括:单次 tap、双次 tap、pinch and hold、pinch and drag,以及双手缩放和旋转。要让 RealityView 里的实体响应 tap,需要两步:给实体添加 InputTargetComponent 和 CollisionComponent,然后给 RealityView 挂上 tap 手势。 (05:16)
struct ContentView: View {
var body: some View {
RealityView { content in
// For entity targeting to work, entities must have an InputTargetComponent
// and a CollisionComponent!
}
.gesture(TapGesture().targetedToAnyEntity().onEnded { value in
print("Tapped entity \(value.entity)!")
})
}
}
关键点:
InputTargetComponent让实体成为可交互目标CollisionComponent为实体提供碰撞检测范围targetedToAnyEntity()表示 RealityView 中任何实体都可触发onEnded闭包在 tap 结束时执行,value.entity拿到被点击的实体
组合手势:拖动 + 缩放 + 旋转同时进行
用 simultaneously(with:) 可以让多个手势同时生效。Session 演示了一个卫星操作场景:拖动移动、捏合缩放、手腕旋转同时进行,系统在每个手势发生时即时识别。 (07:08)
var manipulationGesture: some Gesture<AffineTransform3D> {
DragGesture()
.simultaneously(with: MagnifyGesture())
.simultaneously(with: RotateGesture3D())
.map { gesture in
let (translation, scale, rotation) = gesture.components()
return AffineTransform3D(
scale: scale,
rotation: rotation,
translation: translation
)
}
}
关键点:
simultaneously(with:)让多个手势并行检测,前一个手势未结束时也能识别新手势gesture.components()解构出平移、缩放、旋转三个分量AffineTransform3D把三个变换合成为一个统一的 3D 仿射变换SpatialEventGesture提供更低层的访问,可追踪 Tap Begins/Moves/Ends,适合双手同时操作两个不同目标
自定义手势:ARKit hand skeleton tracking
自定义手势需要 Full Space。Shared Space 下 ARKit 不可用,这是硬限制。Blackbox 游戏是混合使用系统手势和自定义手势的范例:自定义手势用来创建新气泡(进出关卡),系统手势用来点击气泡。Session 演示了一个”双手指尖画圈”的自定义手势。 (09:33)
let leftHandIndexFingerTip = leftHandAnchor.skeleton.joint(named: .handIndexFingerTip)
// ...
let leftHandIndexFingerTipWorldPosition = matrix_multiply(leftHandAnchor.originFromAnchorTransform,
leftHandIndexFingerTip.anchorFromJointTransform).columns.3.xyz
// ...
let isCircleShapeGesture = indexFingersDistance < 0.04 && thumbsDistance < 0.04
if isCircleShapeGesture {
// respond to gesture
}
关键点:
skeleton.joint(named:)获取指定关节的锚点originFromAnchorTransform和anchorFromJointTransform相乘,得到关节在世界坐标系中的位置.columns.3.xyz从 4x4 矩阵提取平移分量(世界坐标 xyz)- 双手食指尖距离和拇指尖距离均小于 4 厘米时,识别为 circle 手势
- 自定义手势必须有视觉和音效反馈,让玩家知道自己在正确执行
游戏手柄:GameController 框架
visionOS 支持与 iOS/macOS 相同的游戏手柄,包括 Xbox Series X 和 PlayStation DualSense。检测手柄连接用 GCControllerDidConnect 通知。 (14:00)
NotificationCenter.default.addObserver(
forName: NSNotification.Name.GCControllerDidConnect,
object: nil, queue: nil) { note in
guard let _controller = note.object as? GCController else { return }
_controller.physicalInputProfile[GCInputButtonA]?.valueChangedHandler = {
//...
}
}
关键点:
GCControllerDidConnect通知在游戏启动时也会触发已连接设备,不会漏掉physicalInputProfile[GCInputButtonA]通过键路径访问具体按钮- 可以设置
valueChangedHandler回调,也可以主动poll按钮状态 - 还需要监听
GCControllerDidDisconnect,手柄断开时回退到手势输入
在 visionOS 2 中,还需要给 RealityView 添加 .handlesGameControllerEvents(matching:) 修饰符,表示这个视图接收手柄输入。 (14:24)
struct ContentView: View {
var body: some View {
RealityView { content in
// Tag your RealityView to respond to controller input events.
}
.handlesGameControllerEvents(matching .gamepad)
}
}
关键点:
.handlesGameControllerEvents(matching:)让 RealityView 接收指定类型的控制器事件.gamepad匹配标准手柄输入,还有.extendedGamepad等选项- 这是 visionOS 2 新增的必要步骤,不加这个修饰符手柄输入不会路由到你的视图
输入方式与空间类型的对应关系
| 输入方式 | Shared Space | Full Space |
|---|---|---|
| 系统手势 | 支持 | 支持 |
| 游戏手柄/键盘/触控板 | 支持 | 支持 |
| 自定义手势(ARKit) | 不可用 | 支持 |
无障碍
无论使用哪种输入方式,都要为不同能力的玩家提供替代方案。Synth Riders 提供了单手模式选项。RealityKit 可使用原生无障碍框架,Unity 可用 Apple Accessibility 插件。
核心启发
-
做什么:为新项目优先实现 indirect input + 系统手势。为什么值得做:indirect input 让手放在身侧就能操作,长时间游玩不累;系统手势零学习成本,玩家拿起就用。怎么开始:给实体加
InputTargetComponent和CollisionComponent,挂TapGesture().targetedToAnyEntity(),确保 Shared Space 和 Full Space 都能运行。 -
做什么:用
simultaneously(with:)组合 Drag + Magnify + Rotate 手势。为什么值得做:3D 空间中的物体操作天然需要同时移动、缩放、旋转,组合手势让操作无缝衔接。怎么开始:按 Session 的manipulationGesture模式,用.map把三个分量合成为AffineTransform3D。 -
做什么:为手柄输入做自动检测和切换,而不是让玩家去设置里翻找。为什么值得做:Wylde Flowers 的做法——手柄一连接就自动切换输入,这是玩家期望的体验。怎么开始:监听
GCControllerDidConnect/GCControllerDidDisconnect,连接时切换到手柄,断开时回退手势,RealityView 加.handlesGameControllerEvents(matching .gamepad)。 -
做什么:为所有输入方式添加视觉反馈和音效。为什么值得做:visionPro 没有 haptics,视觉和音效是告诉玩家”你碰到东西了”的唯一途径。怎么开始:手势触发时播放音效(AVFoundation)+ 实体上播放视觉动画,手柄输入用 CoreHaptics 触发震动。
关联 Session
- Create custom hover effects in visionOS — 用视线悬停触发视觉反馈,配合手势输入提升交互体验
- Create custom visual effects with SwiftUI — SwiftUI 视觉效果实现,可用于手势触发的动态反馈
- Elevate your tab and sidebar experience in iPadOS — iPadOS 导航系统更新,跨平台输入设计参考
- Add personality to your app through UX writing — 交互文案设计,自定义手势教学引导可参考
评论
GitHub Issues · utterances