Highlight
Amanda Han 用一个数字雕刻应用演示了空间配件的完整开发流程。
核心内容
visionOS 一直坚持眼睛加双手的输入范式。但雕刻、CAD、绘画、隔空击球这类场景,靠捏合手势始终差一点:缺少按钮,缺少压感,缺少触觉反馈,更缺少笔尖那种确定的物理边界。开发者想做精细创作工具,只能眼看着手势 API 在毫米级精度上掉链子。
WWDC25 给出了答案:visionOS 26 正式支持两款空间配件——PlayStation VR2 Sense 控制器和 Logitech Muse。前者带按钮、摇杆、扳机,适合游戏(Resolution Games 的 Pickle Pro 已经能在空间里打匹克球);后者笔尖和侧键带压感传感器,外加触觉反馈,专为生产力和创意场景设计(01:22)。两类配件都能在 shared space 与 full space 中使用,由 Apple Vision Pro 摄像头加配件自身的惯性传感器联合追踪。
Amanda Han 在 session 里用一个数字雕刻 app 把整条链路串起来:用 GameController 框架建立连接,用 RealityKit AnchorEntity 把虚拟刀头锚到笔尖,用 SpatialTrackingSession 拿到 transform 来切割虚拟黏土,用 haptics engine 播放每一刀的触感反馈,最后从 ARKit AccessoryAnchor 拿到「左手还是右手握」这种附加信息,让工具栏自动避让握持手。
详细内容
工程配置
第一步在 Xcode capabilities editor 里勾选 Spatial Gamepad,并在 plist 的 Accessory Tracking Usage 字段填写用途说明(例如「Tracking accessory movements to carve into virtual clay」),用户首次启动会看到这段描述的授权弹窗(03:11)。
发现配件连接
GameController 框架里能携带空间追踪能力的两个类是 GCController 和 GCStylus,都遵循 GCDevice 协议。注册连接通知后,必须用 productCategory 二次校验是否为空间配件(04:56):
// Check spatial accessory support
NotificationCenter.default.addObserver(forName: NSNotification.Name.GCControllerDidConnect, object: nil, queue: nil) {
notification in
if let controller = notification.object as? GCController,
controller.productCategory == GCProductCategorySpatialController {
}
}
关键点:
GCControllerDidConnect:监听控制器接入事件;笔类配件用GCStylusDidConnect。notification.object as? GCController:把通知里的对象拆包成具体设备。productCategory == GCProductCategorySpatialController:判断这台 controller 支持空间追踪;不是所有 game controller 都支持。Stylus 对应的常量是GCProductCategorySpatialStylus。- 断连用对称的
GCControllerDidDisconnect/GCStylusDidDisconnect,要在回调里清理虚拟内容,避免出现「幽灵刀头」。
把虚拟内容锚到配件
每款配件给出自己的命名锚点:PS VR2 Sense 暴露 aim、grip、grip surface;Logitech Muse 只暴露 aim。雕刻 app 选择 aim(06:34):
// Anchor virtual content to an accessory
func setupSpatialAccessory(device: GCDevice) async throws {
let source = try await AnchoringComponent.AccessoryAnchoringSource(device: device)
guard let location = source.locationName(named: "aim") else {
return
}
let sculptingEntity = AnchorEntity(.accessory(from: source, location: location),
trackingMode: .predicted)
}
关键点:
AccessoryAnchoringSource(device:):从GCDevice抽出可锚定源。locationName(named: "aim"):拿配件自定义的锚点;如果配件不支持该位置,返回 nil 直接退出。AnchorEntity(.accessory(from:location:), trackingMode:):把AnchorEntity钉到该锚点。trackingMode: .predicted:用预测模型补偿渲染延迟,剧烈动作可能过冲(视频里第 4 帧紫色预测框偏离灰色真实框);需要精确坐标的场景换成.continuous,延迟更高但不会过冲(07:46)。
拿到 transform:SpatialTrackingSession 加 .accessory
去年的 SpatialTrackingSession 可以为已配置的 AnchorEntity 提供 transform,今年新增了 .accessory 配置项(09:01):
// Get in-app transforms
let session = SpatialTrackingSession()
let configuration = SpatialTrackingSession.Configuration(tracking: [.accessory])
await session.run(configuration)
关键点:
SpatialTrackingSession():建立追踪会话。Configuration(tracking: [.accessory]):声明要追踪空间配件类型;可以同时跟其他类型组合。await session.run(configuration):启动会话。运行起来后,雕刻 app 才能在每帧从 anchor entity 读到刀头的世界坐标,进而判定是否切到虚拟黏土。
给雕刻加触感
切到黏土时同步播放触觉反馈,沉浸感立刻不一样(09:48):
// Add haptics to an accessory
let stylus: GCStylus = ...
guard let haptics = stylus.haptics else {
return
}
guard let hapticsEngine: CHHapticEngine = haptics.createEngine(withLocality: .default) else {
return
}
try? hapticsEngine.start()
关键点:
stylus.haptics:不是每个配件都有触觉,先判空。createEngine(withLocality: .default):基于CHHapticEngine,触觉模式的设计可以参考 Advancements in Game Controllers。try? hapticsEngine.start():启动后就能在切割回调里 fire 一段 haptic pattern。
从 RealityKit 桥接到 ARKit AccessoryAnchor
RealityKit AnchorEntity 拿不到「左手还是右手握」、「相对运动」、「追踪质量」这些状态。今年开放了 ARKitAnchorComponent,只要 SpatialTrackingSession 在跑,就能从 entity 取到对应的 ARKit anchor(11:54):
// Access ARKit anchors from AnchorEntity
func getAccessoryAnchor(entity: AnchorEntity) -> AccessoryAnchor? {
if let component = entity.components[ARKitAnchorComponent.self],
let accessoryAnchor = component.anchor as? AccessoryAnchor {
return accessoryAnchor
}
return nil
}
关键点:
entity.components[ARKitAnchorComponent.self]:从 entity 上取出桥接组件。component.anchor as? AccessoryAnchor:拆包成AccessoryAnchor,里面有heldChirality(哪只手)、相对运动、相对旋转、追踪状态四个属性(10:43)。- 雕刻 app 用
heldChirality:左手握就把工具栏放到正 x 方向,右手反之,没握住就不偏移(12:35)。
走纯 ARKit 的路径
如果 app 用自定义渲染,可以绕开 RealityKit:用 GCStylus 或 GCController 创建 Accessory,再用 accessory tracking provider 订阅 anchor 更新。配件接入或断开时要用新的配置 re-run ARKit session,否则会拿到失效的 anchor(14:06)。
核心启发
1. 在创作类 app 里加一个「空间配件模式」
为什么值得做:visionOS 26 的笔类配件 Logitech Muse 自带压感和触觉,是数字绘画、3D 雕刻、CAD 标注的天然输入。比手势精度高一个数量级,并且物理握持能让长时间操作不疲劳。
怎么开始:从勾选 Spatial Gamepad capability 入手,监听 GCStylusDidConnect,把当前手势工具的 hit-test 入口替换成 anchor entity 的 transform。先做一支虚拟笔尖跟着真实笔走,再叠加压感映射到笔触粗细。
2. 给游戏加一个「双握控制器」分支
为什么值得做:PS VR2 Sense controller 在 visionOS 上能像 sport racket 一样追踪。已有的 visionOS 游戏只要支持双手 controller anchor,就能从「捏合点击」升级成「实体挥拍」级别的体感。
怎么开始:实现 productCategory == GCProductCategorySpatialController 分支,挂两个 AnchorEntity 到 aim 锚点;旧的捏合输入保留作为 fallback,让没买配件的玩家依然能玩。在 App Store 配上「Spatial game controller support」徽章。
3. 在 full space 沉浸应用里隐藏上肢和系统 UI
为什么值得做:使用配件的画面里,玩家想看到虚拟工具而不是自己的手腕和真实笔身;同时 home indicator 会破坏沉浸感。
怎么开始:用 .persistentSystemOverlays(.hidden) 隐藏 home indicator,用 .upperLimbVisibility(.hidden) 隐藏上肢和配件本体。在 SwiftUI view 上挂 .receivesEventsInView,让同一个视图同时接受 spatial event gesture 和 game controller 事件,无需为按钮和手势写两套逻辑(15:54)。
4. 测量类需求改用 Continuous 模式或 metric anchor
为什么值得做:.predicted 模式在快速动作下会过冲。CAD 标注、空间测量、医疗示意这类场景,过冲就是误差。
怎么开始:渲染层和「快速命中检测」继续用 .predicted;真正写入坐标的笔画或测量值切到 .continuous,或直接用 ARKit 的 metric anchor API(参考 Coordinate spaces 文档)。
关联 Session
- Bring your SceneKit project to RealityKit — SceneKit 弃用后,把现有 3D 项目迁到 RealityKit 的实操路径。
- What’s new in RealityKit — 本文用到的
ARKitAnchorComponent桥接能力,在这一集里有完整介绍。 - Build a SwiftUI app with the new design — visionOS 26 SwiftUI 新设计与系统覆盖层 API 的整体说明。
- Set the scene with SwiftUI in visionOS — 如何在 SwiftUI 中组织 shared / full space 场景,配合空间配件 app 的窗口布局。
- Engage players with the Apple Games app — Apple Games app 的入口与徽章系统,决定空间配件支持如何在商店中展示。
评论
GitHub Issues · utterances