在 SharePlay 中自定义 spatial Persona 模板
Customize spatial Persona templates in SharePlay
2024年6月10日
一句话判断
visionOS 2 让你可以自定义 FaceTime 中 spatial Persona 的座位布局——如果你的 SharePlay App 需要区分角色(比如棋手和观众、庄家和玩家),这个功能直接决定了用户体验的自然程度。
这场 Session 讲了什么
在 visionOS 的 FaceTime 中,spatial Personas 让参与者感觉像在同一个房间里。当有人启动 SharePlay 活动时,系统会将所有人按照一个 spatial template(空间模板)重新排列——定义每个人相对于共享 App 的初始位置。visionOS 1 提供了三种内置模板:side-by-side(并排弧线排列,适合视频类)、conversational(开放式圆圈,适合音乐类)、surround(封闭圆圈围绕共享内容,适合 3D 建模类)。
visionOS 2 新增了自定义 spatial template 能力。开发者可以精确定义每个”座位”在 3D 空间中的位置和朝向,并为每个座位指定角色标签(如”蓝队”、“红队”、“观众”)。当参与者在 FaceTime 中加入 SharePlay 活动后,系统会根据 template 将他们的 spatial Persona 放到对应位置。
演讲者 Ethan 用一个叫 “Guess Together” 的猜词游戏做完整演示。这个游戏有四个阶段(欢迎、分类选择、组队、游戏),每个阶段使用不同的 template——分类选择用系统默认的 side-by-side,组队和游戏阶段用自定义 template 来区分蓝队/红队/当前玩家/对方队伍的座位。
值得深挖的点
自定义 Template 的设计逻辑
自定义 template 不是随意的 3D 空间布局。它需要考虑 FaceTime 的空间感知机制:当用户按下 Digital Crown 重新定位时,他们会被放回 template 定义的初始座位。所以每个座位的朝向必须让参与者能自然地看到共享内容和彼此。
Guess Together 的游戏阶段 template 是一个很好的设计范例。当前玩家被放在计分板窗口左侧,面前有一个讲台——这个位置让玩家的注意力自然集中在需要猜测的队友身上。队友坐在共享 App 右侧,正对当前玩家。对手坐在计分板正前方,既能看到分数又能观察对方。这种布局在真实桌游场景中也很常见:出题人、猜题人、旁观者各有各的位置,不需要解释就知道谁在做什么。
关键的设计约束是:template 定义的是”起始座位”,不是”固定位置”。参与者随时可以自由移动、调整距离。template 只是确保活动开始时所有人有一个合理的初始排列。
模拟器中的 FaceTime 测试支持
visionOS 2 在 Xcode 模拟器中新增了对模拟 FaceTime 通话的支持。这对 SharePlay 开发来说是重大改进——以前你至少需要两台 Vision Pro 设备才能测试多人场景。现在你可以在模拟器中启动模拟 FaceTime 通话,测试你的 spatial template 是否按预期工作、参与者的位置关系是否合理、UI 是否被 Persona 遮挡等。
测试时需要注意:模拟器中的 Persona 位置和真实设备上可能略有差异,最终布局需要在真实设备上验证。但在开发阶段,模拟器的 FaceTime 支持已经足够让你完成大部分 template 调试工作。
代码片段
定义自定义 Spatial Template
import GroupActivities
import SwiftUI
// 为猜词游戏定义空间座位
struct GuessTogetherSpatialTemplate: SpatialTemplate {
// 座位的方向:面向共享内容的中心
static let frontCenter = SpatialTemplate.Element(
role: .currentPlayer, // 当前出题人
position: .init(x: -0.8, y: 0, z: -0.5),
direction: .init(x: 0.8, y: 0, z: 0.5)
)
static let teammate = SpatialTemplate.Element(
role: .sameTeam, // 同队队友
position: .init(x: 0.8, y: 0, z: -0.5),
direction: .init(x: -0.8, y: 0, z: 0.5)
)
static let opponent = SpatialTemplate.Element(
role: .opposingTeam, // 对方队伍
position: .init(x: 0, y: 0, z: -1.5),
direction: .init(x: 0, y: 0, z: 1.5)
)
var body: some SpatialTemplate {
// 定义座位及其容量
SpatialTemplateSeat(
element: frontCenter,
capacity: 1 // 只有一个人出题
)
SpatialTemplateSeat(
element: teammate,
capacity: .maximum // 同队可以多人
)
SpatialTemplateSeat(
element: opponent,
capacity: .maximum // 对方可以多人
)
}
}
坑点:position 和 direction 使用的是相对于共享 scene 中心的坐标,单位是米。方向向量指示的是座位”看向”的方向。如果你的共享内容是一个 window,z 轴负方向通常朝向内容正面。
根据活动阶段切换 Template
// 在 GroupSession 中根据游戏阶段切换 template
class GameManager: ObservableObject {
var session: GroupSession<GuessTogether>?
func transitionToPhase(_ phase: GamePhase) {
switch phase {
case .categorySelection:
// 使用系统默认的 side-by-side template
session?.spatialTemplatePreferences = .systemDefault
case .teamSelection:
// 切换到组队 template
session?.spatialTemplatePreferences = .init(
preferred: TeamSelectionTemplate.self
)
case .playing:
// 切换到游戏 template,按角色分配座位
session?.spatialTemplatePreferences = .init(
preferred: GuessTogetherSpatialTemplate.self
)
// 系统会根据每个参与者的角色重新排列 Persona
}
}
}
坑点:template 切换是异步的,参与者从旧位置移动到新位置有一个过渡动画。不要在短时间内频繁切换 template,否则会出现参与者在多个位置间反复跳转的情况。
为参与者分配角色
// 使用 GroupSessionParticipant 的 metadata 传递角色信息
struct PlayerInfo: Codable {
let team: Team
let role: Role
}
enum Team: String, Codable {
case blue, red
}
enum Role: String, Codable {
case player, spectator
}
// 在 template 中根据角色匹配座位
extension SpatialTemplate.Element.Role {
static let currentPlayer = SpatialTemplate.Element.Role("currentPlayer")
static let sameTeam = SpatialTemplate.Element.Role("sameTeam")
static let opposingTeam = SpatialTemplate.Element.Role("opposingTeam")
}
坑点:角色的分配需要在所有参与者之间同步——使用 GroupSession 的 messaging 机制确保每个人的角色信息一致。如果角色信息不一致,不同设备上的 template 匹配结果会不同。
最佳实践
设计自定义 template 时,先在纸上画出俯视图。标出共享内容的位置,然后根据真实桌游/会议的座位习惯安排参与者的位置。核心原则:每个参与者应该能同时看到共享内容和需要交互的人。如果一个座位需要”转头才能看到关键信息”,说明位置安排有问题。
template 的切换应该和游戏/活动的阶段转换同步。不要在中间状态的过渡期间切换 template——比如玩家正在选择队伍时不要改变座位,等选择完成后一次性切换到新布局。这样避免了参与者在不稳定状态下被移动的困惑。
还有什么值得关注
- 如果你没有 Apple Watch App,Live Activity 点击后会展开成系统提供的全屏视图,里面有按钮可以直接在 iPhone 上打开你的 App。
- 自定义 template 需要配合 “Build custom experiences with Group Activities” 和 “Build spatial SharePlay experiences” 两个前置 session 的内容,建议先看那两个。
- 模拟器中的 FaceTime 测试支持是 visionOS 2 SDK 的新功能,确保你的 Xcode 版本是最新的。