其他
在 visionOS 上用 TabletopKit…
适用于在 visionOS 上用 TabletopKit 构建带棋盘、棋子、卡牌、骰子、座位/回合、手势交互、RealityKit 渲染和 FaceTime Group Activities 多人同步的空间桌游。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 35/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 39/100 ×0.15
适用场景
在 visionOS 上用 Tablet特定
解决思路
- 按可用性矩阵选择 API:visionOS 2.0+ 使用核心 gameplay、equipment、seats、actions、rendering、Group Activities;visionOS 2.2+ 使用 TabletopInteraction.Configuration;visionOS 26.0+ 使用 custom actions/state、registration、advanced toss outcomes、discarded-action observation;Simulator 只支持单人布局测试,不支持多人。
- 在定义游戏逻辑的源文件中 import TabletopKit;用 RealityKit 实体渲染时 import RealityKit。
- 多人游戏在 Signing & Capabilities 添加 Group Activities capability。
- 在 RealityKit content bundle 提供 table、piece、card、dice 的 USDZ 资产。
- 记住关键类型角色:TabletopGame 是中央游戏管理器,拥有 setup、actions、observers、rendering;TableSetup 是传给 TabletopGame init 的配置对象;Tabletop/EntityTabletop 是桌面协议;Equipment/EntityEquipment 是交互游戏件协议;TableSeat/EntityTableSeat 是玩家座位协议;TabletopAction 修改状态;TabletopInteraction 处理手势交互;TabletopGame.Observer 响应已确认 action;TabletopGame.RenderDelegate/EntityRenderDelegate 处理视觉更新。
- 按顺序构建并验证游戏:定义 tabletop、equipment、seats;配置 TableSetup 并注册每个自定义 action 类型;创建 game,附加 observer 与 renderer,claim seat,建立自动或手动 update handling;启动多人前检查 snapshot 中必要 equipment IDs、parents、seats、counters,若不变量失败则修复 setup 并重建。
- 若未通过 .tabletopGame(_:parent:automaticUpdate:) 启用自动更新,每帧调用 update(deltaTime:);用 withCurrentSnapshot(_:) 安全读取状态。
- 创建 table:conform EntityTabletop,提供 shape(round 或 rectangular)和 RealityKit Entity;用 TabletopShape.round(center:radius:thickness:in:) 或 TabletopShape.rectangular(entity:) 设置桌面形状;加载资产可用 Entity.load(named: table/game_table, in: contentBundle)。
- 所有交互对象 conform Equipment 或 EntityEquipment;每个 piece 有 id(EquipmentIdentifier)和 initialState。
- 按用途选择 equipment 状态类型:BaseEquipmentState 用于通用 pieces、pawns、tokens;CardState 用于扑克牌并跟踪 faceUp/face-down;DieState 用于骰子并跟踪 integer value;RawValueState 用于编码为 UInt64 的自定义数据;CustomEquipmentState 用于 BaseEquipmentState 加游戏数据的自定义状态。
- 定义 pawn 时用 BaseEquipmentState(parentID:seatControl:pose:entity:);定义 card 时用 .faceDown(parentID:seatControl:pose:entity:);定义 die 时用 DieState(value:parentID:seatControl:pose:entity:);加载资产可用 Entity.load(named: pieces/pawn, in: contentBundle)、Entity.load(named: cards/card, in: contentBundle)、Entity.load(named: dice/d6, in: contentBundle)。
- 用 seatControl 限制可交互玩家:.any、.restricted([seatID1, seatID2])、.restrictedCurrent([seatID1, seatID2])、.current、.inherited。
- 允许 equipment parent 到其他 equipment;override layoutChildren(for:visualState:) 定位子项;返回 .planarStacked(layout:animationDuration:)、.planarOverlapping(layout:animationDuration:) 或 .volumetric(layout:animationDuration:)。
- 定义 seat:conform EntityTableSeat,提供 id、initialState、entity,并用 TableSeatState(pose:context:) 提供围绕桌子的 pose。
- 交互前 claim seat:game.claimAnySeat()、game.claimSeat(matching:) 或 game.releaseSeat();通过 TabletopGame.Observer.playerChangedSeats 观察变化。
- 用 TabletopAction 工厂方法修改游戏状态:.moveEquipment(matching:childOf:pose:)、.updateEquipment(card, faceUp:)、.updateEquipment(die, value:)、.setTurn(matching:)、.updateCounter(matching:value:)、.createBookmark(id:)。
- 自定义游戏逻辑 conform CustomAction;其 application 和 validation 只能依赖 action data 与 supplied TableState/TableSnapshot,确保每个 peer 得到相同结果;在 setup 期间用 setup.register(action: CollectCoin.self) 注册,再 game.addAction(CollectCoin(...))。
- Score counters:setup.add(counter: ScoreCounter(id:value:));用 .updateCounter(matching:value:) 更新;用 snapshot.counter(matching:)?.value 读取。
- State bookmarks:用 .createBookmark(id:) 保存,用 game.jumpToBookmark(matching:) 恢复;恢复异步且网络有序;等待 stateDidResetToBookmark,随后用 withCurrentSnapshot 读取,从权威 callback state 重建本地 UI 并验证恢复不变量;不要在 jumpToBookmark 后立即检查,也不要因不匹配继续 enqueue jump。
- 实现 TabletopGame.Observer,用当前 snapshot 协调 confirmed、rolled-back、discarded、bookmark-reset 结果。
- 从 .tabletopGame modifier 返回 interaction delegate 处理玩家在 equipment 上的手势;用 interaction.value.gesture 获取 gesture-specific state,避免 deprecated gesturePhase;destination 控制优先用 interaction.setConfiguration(.init(allowedDestinations: ...)),而不是 deprecated setAllowedDestinations(_:) 或 value.allowedDestinations。
- 处理手势与掷骰:读 interaction.value.phase;.started 时 interaction.setConfiguration(.init(allowedDestinations: .any));.update 且 interaction.value.gesture?.phase == .ended 时 interaction.toss(equipmentID: interaction.value.controlledEquipmentID, as: .cube(height: 0.02, in: .meters));.ended 或 .cancelled 不处理。
- 在 onTossStart(interaction:outcomes:) 中遍历 outcomes,用 outcome.tossableRepresentation.face(for: outcome.restingOrientation) 得到 face,再 interaction.addAction(.updateEquipment(die, rawValue: face.rawValue, pose: outcome.pose));不要随机生成结果。
- 骰子物理形状用 Tossable Representations:.cube(d6)、.tetrahedron(d4)、.octahedron(d8)、.decahedron(d10)、.dodecahedron(d12)、.icosahedron(d20)、.sphere;均接受 height:in:(sphere 用 radius:in:)和可选 restitution:。
- 从代码启动交互:game.startInteraction(onEquipmentID: pieceID)。
- RealityKit 渲染:conform EntityRenderDelegate,提供 root entity;TabletopKit 自动定位 EntityEquipment;在 onUpdate(timeInterval:snapshot:visualState:) 做自定义视觉更新。
- 在 SwiftUI 的 RealityView 上用 .tabletopGame(_:parent:automaticUpdate:) 连接;把 game.renderer.root append 到 content.entities;modifier closure 返回 interaction delegate。
- 调试轮廓用 game.tabletopGame.debugDraw(options: [.drawTable, .drawSeats, .drawEquipment]);发布前禁用。
- Group Activities:定义 GroupActivity 并实现 metadata,设置 meta.type = .generic 和 meta.title;监听 BoardGameActivity.sessions(),对每个 session 调用 tabletopGame.coordinateWithSession(session);deinit 中调用 tabletopGame.detachNetworkCoordinator();TabletopKit 自动同步 equipment state、seat assignments、actions、interactions,无需手动 message passing。
- 实现 TabletopGame.MultiplayerDelegate:joinAccepted()、playerJoined(_:)、didRejectPlayer(_:reason:)、multiplayerSessionFailed(reason:);自定义 network coordinators 和 arbiter role management 参考 references。
- 避免常见错误:未 claim seat 会导致 action 被拒;不要直接修改 equipment 属性;每个 custom action 必须先用 setup.register(action:) 注册;处理 action rollback,实现 actionWasRolledBack(_:snapshot:) 恢复 UI;在重要时实现 actionWasDiscarded(_:) 处理本地 action 无法入队;parentID 必须引用有效 equipment ID,否则 piece 消失;toss 后从 outcome.tossableRepresentation.face(for:) 读取结果;不要在 Simulator 测试多人,Group Activities 不支持,需 2+ Apple Vision Pro 在 FaceTime。
- 按 Review Checklist 自检:应用平台/可用性矩阵;TableSetup 使用 Tabletop/EntityTabletop conforming type;所有 equipment 状态类型正确;添加 seats 并在开始 claim;所有 custom actions 注册;TabletopGame.Observer 协调 confirmed、rolled-back、discarded、bookmark-reset 结果与当前 snapshot;连接 EntityRenderDelegate 或 RenderDelegate;在 RealityView 添加 .tabletopGame modifier;定义 GroupActivity 并 coordinateWithSession(_:);多人构建添加 Group Activities capability;发布前禁用 debugDraw;记录 Simulator 仅单人和多人需 2+ Apple Vision Pro 在 FaceTime。
- 扩展模式参考 references/tabletopkit-patterns.md:observer 实现、custom actions、dice simulation、card overlap、network coordination;必要时查看 Apple Documentation: TabletopKit、Creating tabletop games、Synchronizing group gameplay、Simulating dice rolls、Implementing playing card overlap、WWDC24 session 10091。
适用边界 · 注意事项
- 目标平台不是 visionOS,或不想依赖 Apple 的 TabletopKit、RealityKit、Group Activities 时。
- 只需单机或 Simulator 布局测试,不需要 FaceTime 多人同步或 2+ 台 Apple Vision Pro 时。
- 需要跨平台或非 Apple 生态的实时多人网络同步时。