大多数浏览器和
Developer App 均支持流媒体播放。
-
在 visionOS 中探索游戏输入
探索如何在 visionOS 中为你的游戏设计并实现出色的输入体验。了解如何通过系统手势为玩家提供顺畅的游戏互动体验。探索有关支持自定手势和游戏控制器的推荐做法。
章节
- 0:00 - Introduction
- 0:56 - Design around gestures
- 3:28 - System gestures
- 6:14 - Combination gestures
- 7:43 - Custom gestures
- 11:24 - Physical controllers
资源
- Composing SwiftUI gestures
- Forum: Graphics & Games
- Human Interface Guidelines: Game controls
- Human Interface Guidelines: Gestures
相关视频
WWDC24
WWDC23
WWDC22
WWDC21
-
下载
大家好 我叫 Charlyn 来自技术布道团队 今天 我要谈谈如何在 visionOS 中 实现出色的游戏输入方式
为 visionOS 开发游戏时 你可以非常灵活地选择 游戏类型 以及用于开发这些游戏的框架
在 Apple Vision Pro 上 玩家可以使用眼睛和手 来浏览系统中安装的 App 和游戏 此外 visionOS 还支持 外接物理输入设备 围绕手势设计的游戏输入方式 无需使用任何额外的设备 最容易让玩家直接开始畅玩游戏 我将展示可供选择的不同手势类型 然后介绍 visionOS 支持的 一些物理输入设备 以及在决定添加 对这些设备的支持后 需要牢记的一些注意事项 围绕 visionOS 手势设计的输入方式 可以是直接的 也可以是间接的 直接输入意味着玩家将会 伸出手去直接与虚拟物体交互 请务必记住 由于 visionOS 提供了无边画布 因此玩家可能需要大幅度移动手臂 才能触及游戏中的所有对象 间接输入意味着玩家将会 隔着一定距离对这些对象进行操作 通过注视和轻点对象进行选择来实现 玩家的手可以更靠近自己的身体 通过间接输入 玩家做出的 小幅度手臂动作 可以放大为大幅度动作 所以如果玩家要穿过观看区域 触及相隔较远的对象 或者重复做出大幅度动作 间接输入可以大大提升 长时间玩游戏时的舒适度 例如 这是 visionOS 上的 益智游戏“Loona” 你可以通过间接输入来操控 与你相隔一段距离的每件物品 只需注视就能选中物品 然后可以用手指移动选中的物品 你可以将它直接放置在空间拼图中 也可以双指互点两下 使它卡入到位 使用手势旋转或移动空间拼图 即可从各种角度进行观察 通过这种方式 无论站着玩 还是坐着玩 你都会感到很舒适 间接输入可以带来轻松惬意的 游戏体验 但有时直接输入是合理的 原因可能是你的游戏 有意要提供高能体验 此外 直接与虚拟物体交互 还能带来很多乐趣 这是“Super Fruit Ninja” 玩家在游戏中 需要切碎飞舞的水果 以及划过菜单按钮 这个游戏是围绕直接输入设计的 如果这类输入适合你的游戏 请务必提供大量反馈 以便让每一次与对象的接触 都能带给玩家满足感
你可以使用视觉反馈组合 比如手部后方的那道闪电 加上四处飞溅的果肉 还可以添加各种声音 比如挤压声和泼溅声 即使没有触觉反馈 玩家也可以知道 自己有没有与对象接触 无论对象是 UI 按钮 还是飞舞的菠萝! 最好在游戏中同时支持 直接和间接这两种手势 可以通过以下几种不同的方式来实现 你的游戏可以响应 内建的系统手势 你也可以将两个或更多 系统手势组合在一起 从而更精细地控制 游戏会响应什么样的输入 如果需要 可以使用 ARKit 中的手部追踪功能 来创建自己的自定手势 在做出决定之前 请记住 使用内建的系统手势 可以带来一些显著优势 一方面 玩家已经了解了 系统手势的具体使用方法 因为他们已经在使用这些手势 浏览平台上的所有其他 App 和游戏了 你无需教会玩家如何与游戏交互 而且无需使用额外的设备 你的游戏开箱就能畅玩! 玩家可以随时开始畅玩 此外 无论你的游戏 在 visionOS 上的什么位置运行 系统手势都会受到支持 这些手势适用于各种空间 包括共享空间和完全沉浸式空间 有多种系统手势可供使用 最简单的手势是注视一个对象 然后同时轻点同一只手的两根手指 如果你的游戏在 2D 屏幕上 通过轻点一下进行输入 则只需替换成 visionOS 中的 轻点手势即可
你也可以响应双指互点两下手势 还有双指捏住手势 和捏合并拖移手势 这两个手势都可以 直接或间接作用于对象 玩家可以用两只手来放大和缩小 或者旋转对象 这个 visionOS 游戏叫 “What the Golf”是使用系统手势 间接控制游戏中对象的绝佳示例 我注视这个球 然后使用 双指捏住手势将它激活 现在 我可以移动手部 来控制球的方向 以及球在被我松开后的力度
由于我的手部掌管了输入 因此我可以随意环顾当前关卡 观察瞄准的位置
松开捏合的双指后 球就会飞起来 我打出了一杆进洞!
响应系统手势非常简单 包含在 RealityView 中的任意实体 如果要具备轻点功能就需要拥有 InputTargetComponent 和 CollisionComponent 然后需要将手势附加到 包含实体的 RealityView 然后表明这个视图中的 任意实体都是可轻点的 检测到手势后 可以 在手势处理程序中做出响应 要进一步了解如何响应 系统手势 请观看去年的视频 “开发你的第一款沉浸式 App” 并访问 developer.apple.com/cn/ 查看关于交互操作的文档 如果你的游戏是在 Unity 中开发的 则会使用 Unity Input System 来检测手势并做出响应 要接收轻点事件 请为游戏对象添加输入碰撞器 轻点数据会以 World Touch Event 的形式提供给你 观看“打造沉浸式 Unity App” 可了解更多详情 系统手势为你提供了 大量开箱即用的选项 如果你需要更高的灵活性 可以将多个系统手势组合在一起 实现独一无二的操作 可以将两个或更多系统手势配对 并检测它们何时同时发生 或按顺序逐个发生 也可以将手势与 键盘修饰符结合使用 还可以使用 SpatialEventGesture 来实现 Tap Begins、Moves 或 Ends 等手势事件的 低级别访问权限 如果玩家需要同时使用两只手 来操作两个不同的对象 那么这种方法将会非常有用 使用 SpatialEventGesture 来追踪 每个操作的各个目标 在这个示例中 你可以选中这个卫星 然后对它同时执行 移动、调整大小和旋转操作 系统可以识别每个手势的发生 即使上一个手势尚未结束 也没有问题 下面介绍如何通过代码实现这一操作 当玩家使用 Drag 手势 移动对象时 添加 simultaneously(with:) 表明 如果 Magnify 和 Rotate 同时发生 则 App 应该同时侦听这两个手势 这意味着 多个手势之间 可以顺畅无缝地过渡 要进一步了解如何将手势进行组合 请观看去年的视频 “将 SwiftUI 提升至新的维度” 并查看相关文档 由于玩家非常熟悉 系统手势和组合手势 因此这些手势应该成为你在 visionOS 上首选的游戏输入方式 但如果你需要更丰富的输入方式 则可以定义自己的自定手势 你为游戏创建的任何自定手势 都需要让玩家 容易学习、容易记忆 要确保手势非常适合游戏中的动作 这样手势才具有直观性 当你设计自定手势时 可以通过添加同时包含 视觉效果和声音的适量反馈 让玩家知道自己做出了正确的操作 要定义你自己的手势 你可以通过 ARKit 使用完整手部骨骼追踪功能 你可以在同一游戏中将系统手势 与自定手势进行组合 这是“Blackbox” 是以富有创意的方式 将这些手势进行组合的绝佳示例 在新手引导过程中 “Blackbox”会教玩家 做出这个自定手势 在环境中创建新的气泡 这个手势简单直观 意味着易于学习和记忆 它也非常重要 因为它是进入和退出 游戏关卡的主要方式 但不要过度使用自定手势 请谨慎使用 凡是在系统手势适用的情况下 “Blackbox”都使用了系统手势 例如 我可以注视气泡 然后用手指轻点以使它弹出
“Blackbox”在 visionOS 上的 全空间中运行 这一点很重要 如果你的游戏在共享空间中 和其他 visionOS App 一起同时运行 你就无法使用 ARKit 和手部追踪功能 现在 在全空间中 你的游戏 是这里显示的唯一一个 App 无论采用 mixed、progressive 还是 full 样式 都是如此 玩家可能会看到四周环境 就像在“Blackbox”中一样 或者 你的游戏也可以 用新环境取代四周环境 创建自定手势时 ARKit 和完整手部骨骼追踪功能 会在全空间中运行 下面介绍如何创建 构成一个圆形的自定手势 检查进行手部追踪所需的所有关节 并获取它们在现实场景坐标中的位置 然后 对于这个圆形手势 检查双手食指指尖之间 以及双手拇指指尖之间的距离 如果距离小于 4 厘米 系统就会识别出这个手势 然后你的游戏就能做出响应 如果使用 Unity 开发游戏 则可以 使用 Unity 的手部追踪软件包 来访问玩家的关节信息 观看这些视频 可以进一步了解 如何将 Unity 游戏 移植到 visionOS 上 无论你使用哪类手势 无论是直接还是间接 无论是系统手势还是自定手势 你都需要确保内建一种替代方式 以便照顾到能力各异的玩家 例如“Synth Riders” 设计的核心输入方式 需要用双手双臂一起接触对象 但假设某个玩家只能 使用一只手臂来玩游戏 这个游戏中的 “Comfort and Accessibility”设置 提供了一个选项 你可以选择 以单手模式玩游戏 还可以选择在单手模式下 使用左手还是右手 现在 我可以只用右手玩游戏了 务必要思考这些辅助功能用例类型 并为玩家提供一些 内建的输入回退功能 这一点非常重要 如果你使用的是 RealityKit 就可以使用原生辅助功能框架 请观看视频 “创造无障碍的空间体验” 进一步了解如何让游戏设计 照顾到能力各异的玩家 如果你是 Unity 开发者 则可以通过 Apple Accessibility 插件 使用所有辅助功能 查看“即插即玩” 了解更多详情 得益于系统手势和自定手势的灵活性 在游戏输入方式的设计方面 你已经有了很多选择 但在某些情况下 你可能需要 添加适用于 visionOS 的 物理输入设备的支持 iOS 和 macOS 支持的所有游戏控制器 visionOS 也同样支持 其中包括 Xbox Series X 和 Sony Playstation 的 DualSense 游戏手柄 如果需要一次性完成 两个以上的输入 或者要从其他平台 移植过来的游戏已经 采用了以控制器为核心的输入方式 则支持游戏控制器可能是正确的选择 visionOS 还支持蓝牙键盘和触控板 因此在适用的情况下 你的游戏可以选择这一输入方式
对于通过蓝牙连接到 visionOS 的 物理控制器、键盘和触控板 响应来自这些设备的游戏输入很简单 在 iOS、iPadOS、macOS 和 visionOS 支持相同的物理输入设备 而且所有这些平台也共用 同一个游戏控制器框架! 所以 如果你要将游戏 移植到多种设备上 相同的代码会适用于所有这些设备 实体控制器适用于 visionOS 中的每一类体验 从窗口式游戏 到完全沉浸式空间都包括在内 Arcade 游戏“Wylde Flowers” 提供了出色的控制器支持 如果玩家已将游戏控制器 与 Vision Pro 配对并连接 则游戏输入会自动使用相应的控制器 做到这一点非常重要! 在检测到控制器已连接后 将输入方式切换到相应的控制器 这种方法远胜于让玩家 在游戏的“设置”菜单中 搜索要切换的输入方式 “Wylde Flowers” 还有一件事也做得很好 那就是确保玩家可以使用控制器 完成游戏中的所有操作 如果预设玩家会使用某个系统手势 来访问部分 UI 元素 就会造成不便 也会让玩家困惑不已 在设计输入方式时 要让所有游戏内容 甚至包括 UI 都可以通过游戏控制器访问
你可以轻松检测玩家是否 已连接游戏控制器 只需为 GCControllerDidConnect 设置观察器 如果有任何游戏控制器 连接到系统 观察器就会提醒你 此外 请务必侦听 GCControllerDidDisconnect 事件 以便在没有游戏控制器的情况下 回退到其他输入形式 (比如系统手势) 收到游戏控制器已连接的提醒后 可以轮询控制器上特定按钮的状态 你还可以根据自己的喜好 设置更改处理程序 以便在输入状态更新时收到通知 来自键盘和触控板的输入 有着相同的流程 你需要侦听它们的连接 然后对它们进行轮询以获取输入 或者设置更改处理程序 以下是用于检测游戏控制器 并做出响应的代码 设置观察器 然后等待接收关于 游戏控制器已连接的通知 如果控制器在游戏启动之前已经连接 则每当有设备连接到 visionOS 你仍会在此处收到通知 收到通知后 要么设置 输入更改处理程序 要么轮询设备以获取预期的输入 还有最后一个步骤 是 visionOS 2 所特有的 将游戏控制器交互修饰符 添加到包含游戏对象的 RealityView 这表明已定向到 RealityView 的 游戏控制器输入 00:14:37.473 --> 00:14:39.210 将由你的游戏进行处理
此外 请务必像使用手势时一样 向玩家提供操作反馈 这一点非常重要 使用 CoreHaptics 框架 触发控制器的震动声 使它与屏幕上的操作相匹配 还有 记得添加声音效果 并在玩家操作成功时 提供即时的视觉反馈 如果你不熟悉游戏控制器框架 有一些很棒的资源 可以帮助你入门 如果你的游戏 依赖于 Unity 引擎 则可以使用 Unity 的 游戏控制器插件 来添加对游戏控制器输入的支持 观看这些视频 可以了解更多详情 下面简要介绍一下哪些空间 支持不同的输入类型 大多数输入类型 包括系统手势、 传统游戏控制器和键盘 都得到了所有空间类型的支持 这些空间包括共享空间中的窗口或 空间容器、全空间体验等 如果你想响应来自自定手势的输入 则需要针对全空间来设计游戏 简单回顾一下 要想为你的游戏 设计出适合玩家的输入方式 你有很多选择 visionOS 中内建的系统手势 对于玩家来说简单自然、顺手易用 如果你需要提高灵活性 则可以考虑加入一两个自定手势 对于某些类型的游戏 支持物理控制器 还可以扩展输入选项 最后一点 如果你已经在 iOS 或 iPadOS 上发布了游戏 请观看“将 iOS 或 iPadOS 游戏 移植到 visionOS” 进一步了解 如何对现有游戏进行转换 以便在 visionOS 中 增强游戏体验的沉浸感 谢谢观看!
-
-
5:16 - Respond to a tap gesture
// Respond to a tap gesture. 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)!") }) } }
-
7:08 - Combine dragging, magnification, and 3D rotation gestures
// Gesture combining dragging, magnification, and 3D rotation all at once. var manipulationGesture: some Gesture<AffineTransform3D> { DragGesture() .simultaneously(with: MagnifyGesture()) .simultaneously(with: RotateGesture3D()) .map {bgesture in let (translation, scale, rotation) = gesture.components() return AffineTransform3D( scale: scale, rotation: rotation, translation: translation ) } }
-
9:33 - Create and detect a custom circle gesture
// Create and detect a custom circle gesture. // Get all required joints and check if they are tracked. let leftHandIndexFingerTip = leftHandAnchor.skeleton.joint(named: .handIndexFingerTip) // ... // Get the position of all joints in world coordinates. let leftHandIndexFingerTipWorldPosition = matrix_multiply(leftHandAnchor.originFromAnchorTransform, leftHandIndexFingerTip.anchorFromJointTransform).columns.3.xyz // ... // Circle gesture detection is true when the distance between the index finger tips centers // and the distance between the thumb tip centers is each less than four centimeters. let isCircleShapeGesture = indexFingersDistance < 0.04 && thumbsDistance < 0.04 if isCircleShapeGesture { // respond to gesture }
-
14:00 - Detect a connected game controller
// Detect connected game controller. // Add handler for when controller connects. NotificationCenter.default.addObserver( forName: NSNotification.Name.GCControllerDidConnect, object: nil, queue: nil) { (note) in guard let _controller = note.object? as GCController else { return } // Add controller input change handlers. _controller.physicalInputProfile[GCInputButtonA]?.valueChangedHandler = { //... } } // Poll for controller input if controller.physicalInputProfile[GCInputButtonA]?.pressed {... } if controller.physicalInputProfile[GCInputButtonB]?.pressed {... }
-
14:24 - Tag a RealityView to handle controller input
// Tag a RealityView to handle controller input. struct ContentView: View { var body: some View { RealityView { content in // Tag your RealityView to respond to controller input events. } .handlesGameControllerEvents(matching .gamepad) } }
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。