Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
f6d3b39
docs
chiimagnus Feb 8, 2026
a92817f
docs: 添加 SaluAVP XR/3D 原生破坏性重构实施计划,明确目标、方案及验收标准
chiimagnus Feb 9, 2026
9eeb17c
feat(battle): Task 1: 建立 AVP 战斗事件消费接口
chiimagnus Feb 9, 2026
d6dd571
feat(battle): ✅Task 2: 抽离战斗渲染器,降低 `ImmersiveRootView` 复杂度
chiimagnus Feb 9, 2026
29fcf82
feat(battle): Task 3-添加战斗动画队列,优化事件处理与渲染流程
chiimagnus Feb 9, 2026
debc2c7
feat: P2✅-Enhance battle animation system with new events and UI updates
chiimagnus Feb 9, 2026
bd0e2b5
-
chiimagnus Feb 9, 2026
e078dac
P3:多敌人与目标选择(战斗可玩性补齐)
chiimagnus Feb 9, 2026
56fc04a
feat: P4✅-Add Event, Rest, and Shop Room Panels with corresponding st…
chiimagnus Feb 9, 2026
5d9b2c2
完成P4
chiimagnus Feb 9, 2026
b39203b
feat: 添加房间场景渲染器,支持房间层的创建和渲染
chiimagnus Feb 9, 2026
6a6ed51
feat: 商店购买行为现在走 3D 小摊交互,不再依赖 2D 商店面板。
chiimagnus Feb 9, 2026
13603b0
feat: 添加商店反馈机制,支持动态消息显示和金币余额标记
chiimagnus Feb 9, 2026
fe2e3d0
feat: 添加商店面板,支持商品选择和购买功能
chiimagnus Feb 9, 2026
09d9a0e
feat: 更新房间面板位置,优化商店面板的显示逻辑
chiimagnus Feb 9, 2026
beefada
fix: 商店里连续点击不同商品,确认 3D 场景不再整层刷新。
chiimagnus Feb 9, 2026
d277cf8
fix: 修复-shopRoomState.message 会跨商品选择保留,导致你点下一个商品时仍显示上一条“购买成功”。
chiimagnus Feb 9, 2026
819f572
-
chiimagnus Feb 9, 2026
294f9a2
feat: 重构奖励系统,添加奖励状态和章节结束面板,优化奖励选择逻辑
chiimagnus Feb 9, 2026
9b1daf7
docs: 更新plan文档
chiimagnus Feb 9, 2026
97f7fba
feat: P6-添加运行快照存储和恢复功能,支持保存、加载和删除快照
chiimagnus Feb 9, 2026
8f873ba
feat: 添加回放面板和运行轨迹存储功能,支持导出、加载和清除轨迹
chiimagnus Feb 9, 2026
a9c780d
Revert "feat: 添加回放面板和运行轨迹存储功能,支持导出、加载和清除轨迹"
chiimagnus Feb 9, 2026
b121603
Revert "feat: P6-添加运行快照存储和恢复功能,支持保存、加载和删除快照"
chiimagnus Feb 9, 2026
1192f09
docs: 更新文档,添加关于业务规则与结算口径的说明
chiimagnus Feb 9, 2026
c835efa
feat: 添加手动回归清单,覆盖核心玩法闭环并避免功能回归
chiimagnus Feb 9, 2026
5875d83
docs: 添加 AVP 计划任务审计报告,包含 TODO 清单和发现记录
chiimagnus Feb 9, 2026
9c7efd8
feat: 更新 AVP 战斗事件,添加目标实体 ID 支持,优化动画反馈
chiimagnus Feb 9, 2026
b9c621e
docs:审核完成
chiimagnus Feb 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
422 changes: 422 additions & 0 deletions .github/docs/DrumGo 技术文档.md

Large diffs are not rendered by default.

169 changes: 169 additions & 0 deletions .github/docs/DrumGo业务全局地图.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
# DrumGo — Business Logic Map(业务全局地图)

> 面向「不读代码的 AI」:用最少上下文描述 DrumGo 做什么、为什么这样做、以及关键入口在哪里。
> 代码与资源路径均为仓库相对路径,可直接定位。

## 1. 产品概述

DrumGo 是一款面向 **visionOS(Apple Vision Pro)** 的沉浸式架子鼓/节奏互动体验,面向想在沉浸空间里“用手打鼓、获得即时音效与特效反馈”的用户。核心体验是在 `ImmersiveSpace` 中看到一套 3D 鼓组,用户双手的鼓棒随手部追踪移动,击打鼓/镲会播放对应音效,并触发场景内的动画/灯光等特效。技术栈:SwiftUI + RealityKit + ARKit(Hand Tracking)+ Reality Composer Pro 资产包。

## 2. 架构分层

### 目录结构 → 职责

- `DrumGo/DrumGoApp.swift`:应用入口与 scene 定义(多窗口 + ImmersiveSpace)
- `DrumGo/View/`:**UI 层**(SwiftUI 窗口),负责入口、选择、打开沉浸空间/体积窗口
- `DrumGo/View/ContentView.swift`
- `DrumGo/View/SelectView.swift`
- `DrumGo/GameManage/`:**交互/业务协调层**
- `DrumGo/GameManage/AppModel.swift`:全局状态(沉浸空间开关状态机)
- `DrumGo/GameManage/ImmersiveView.swift`:沉浸空间容器(RealityView + 交互手势)
- `DrumGo/GameManage/ManageGame.swift`:游戏管理器(手追踪、碰撞、音频、谱面调度)
- `DrumGo/GameManage/Song.swift`:歌曲(mp3)+ 谱面(json)加载与绑定
- `DrumGo/GameManage/Stage.swift`:谱面数据结构与 3D 坐标映射
- `DrumGo/GameManage/IPDetailView.swift`:体积窗口展示 RealityKitContent 内的模型
- `Packages/RealityKitContent/`:**资源层(RCP)**
- `Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/IntactDrumScene.usda`:主鼓场景与行为触发器
- `Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/DrumStick*.usda`:鼓棒与碰撞球体(`StickSphere`)
- `Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/CircleAnimation.usda`:节奏圈(含粒子)
- `Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/IP1.usda`、`IP2.usda`:IP 模型

### 依赖方向(约束)

- `DrumGo/View/*` 只通过环境(`AppModel`)与系统环境值(`openImmersiveSpace/openWindow`)驱动导航;不直接操控 RealityKit 细节。
- `DrumGo/GameManage/*` 可以依赖 `RealityKitContent`(资源 bundle)与系统框架(RealityKit/ARKit/AVFAudio)。
- `Packages/RealityKitContent/*` 仅提供资源与 bundle 入口(`Bundle.module`),不应反向依赖 App 代码。

禁止(建议保持):

- UI 层直接读取/修改 `.usda` 资源细节或硬编码实体名(这些应集中在 `ManageGame.swift`)。

## 3. 核心业务流程

### 流程 A:进入沉浸空间并打鼓

1. 用户打开应用,看到首页 UI:`DrumGo/View/ContentView.swift`
2. 用户点击按钮进入选择页:`openWindow(id: "SelectView")`(`DrumGo/View/ContentView.swift`)
3. 用户在选择页点击 “Go!” 打开沉浸空间:
- UI:`DrumGo/View/SelectView.swift`
- 状态机:`DrumGo/GameManage/AppModel.swift`(`immersiveSpaceState`)
4. 系统打开 `ImmersiveSpace(id: "DrumImmersive")`:
- `DrumGo/DrumGoApp.swift` → `DrumGo/GameManage/ImmersiveView.swift`
5. 沉浸空间加载 3D 鼓场景并启动逻辑:
- 加载:`Entity(named: "IntactDrumScene", in: realityKitContentBundle)`(`ImmersiveView`)
- 启动:`GameManager.shared.start()`(`DrumGo/GameManage/ManageGame.swift`)
6. 手部追踪开始:鼓棒实体跟随手部关节移动(`ManageGame.swift: processHandUpdates()`)
7. 用户击打鼓/镲:
- 碰撞事件:`CollisionEvents.Began`(`ManageGame.swift: handle*Collision(...)`)
- 播放音效:`audioEntity.playAudio(AudioFileResource)`(`ManageGame.swift: handle*Punch(...)`)
- 触发特效:发布 `"RealityKit.NotificationTrigger"` 通知(`ManageGame.swift`)
- 资源行为响应:`IntactDrumScene.usda` 内 `OnNotification*` 根据 identifier 播放 Timeline(例如鼓面弹跳/灯光/心形特效)

### 流程 B:谱面驱动的节奏圈生成与击打

1. 启动时选择歌曲:`ManageGame.swift` 内置 `Song(name: "Missing U", ...)`
2. 读取谱面:
- json:`DrumGo/Soundjson/Missing U.json`
- 解码:`DrumGo/GameManage/Stage.swift`
3. 调度生成节奏圈:
- `ManageGame.swift: scheduleTasks(...)` 根据 note 时间 `asyncAfter` 触发
- 模板:`CircleAnimation`(`RealityKitContent` 资源)
4. 击打节奏圈:
- 碰撞判定实体名:`"Circle"`(`ManageGame.swift` 常量)
- 命中后 burst 粒子并移除实体:`ManageGame.swift: handleCirclePunch(drum:)`

### 流程 C:打开 IP 体积窗口并交互

1. 用户在选择页点击 IP 按钮:
- `DrumGo/View/SelectView.swift` → `openWindow(id: "IPModelView", value: "IP1"|"IP2")`
2. 体积窗口渲染模型:
- `DrumGo/DrumGoApp.swift` → `DrumGo/GameManage/IPDetailView.swift`
- `Model3D(named: modelName, bundle: realityKitContentBundle)`
3. 模型交互(资产内置行为):
- 例如 `IP2.usda` 自带 `OnTap` Timeline,可在窗口内点击触发动画

## 4. 模块详情

### 4.1 沉浸空间状态机(AppModel)

- 做什么:统一管理沉浸空间是否打开/切换中,避免重复触发 open/dismiss。
- 关键文件:
- `DrumGo/GameManage/AppModel.swift`
- `DrumGo/View/SelectView.swift`(驱动打开/关闭)
- `DrumGo/DrumGoApp.swift`(`onAppear/onDisappear` 作为状态最终写入点)

### 4.2 GameManager(交互中枢)

- 做什么:
- 资产加载:鼓场景模板、节奏圈模板、鼓棒实体
- 音频资源加载:鼓点音效
- 歌曲播放:`AVAudioPlayer`
- 谱面调度:根据 note 时间生成节奏圈
- 手追踪:将鼓棒实体绑定到手部关节
- 碰撞与点击:命中后播放音效并触发 RCP 行为
- 关键文件:
- `DrumGo/GameManage/ManageGame.swift`
- `DrumGo/GameManage/Song.swift`
- `DrumGo/GameManage/Stage.swift`

### 4.3 RealityKitContent(资产包)

- 做什么:提供所有 Reality Composer Pro 资源(鼓场景、鼓棒、特效、IP 模型)。
- 关键文件:
- `Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.swift`(`Bundle.module`)
- `Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/IntactDrumScene.usda`
- `Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/CircleAnimation.usda`

### 4.4 权限与系统配置

- 做什么:声明手追踪/环境感知权限,配置沉浸空间初始沉浸样式。
- 关键文件:
- `DrumGo/Info.plist`

## 5. 当前状态与待办

### 已完成

- [x] visionOS 多窗口结构(首页/选择页/体积窗口)与沉浸空间入口:`DrumGo/DrumGoApp.swift`
- [x] 加载主鼓场景 `IntactDrumScene` 并挂载运行时根实体:`DrumGo/GameManage/ImmersiveView.swift`
- [x] 手部追踪驱动鼓棒实体跟随:`DrumGo/GameManage/ManageGame.swift`
- [x] 鼓/镲碰撞触发音效播放:`DrumGo/GameManage/ManageGame.swift`
- [x] 通过 `"RealityKit.NotificationTrigger"` 触发 RCP 行为动画/灯光/特效:`ManageGame.swift` + `IntactDrumScene.usda`
- [x] 谱面 json 解码与按时间生成节奏圈:`Stage.swift` + `ManageGame.swift`
- [x] 节奏圈命中粒子 burst 与移除:`ManageGame.swift` + `CircleAnimation.usda`

### 进行中

- [ ] 选择页“鼓/场景/歌曲”与实际沉浸内容的联动(目前仅 UI 选中态,不影响 `GameManager` 或资产)

### 已知问题

- 碰撞订阅句柄覆盖:`ManageGame.swift` 只有一个 `colisionSubs`,多次订阅可能只保留最后一次(导致部分鼓不响应碰撞)。
- 沉浸视图内存在未使用的 ARKit 会话状态:`DrumGo/GameManage/ImmersiveView.swift` 中 `session/handTracking` 等未参与逻辑。
- 谱面时间基准不明确:`scheduleTasks` 中使用 `note.time * 0.5`,缺少同步依据(BPM/offset)。
- 测试缺失:`DrumGoTests/DrumGoTests.swift` 仅占位。

### 下一步计划(建议优先级)

1. 修复碰撞订阅的生命周期管理(改为数组/多订阅保存),确保所有鼓/镲都可稳定响应。
2. 把 `SelectView` 的选择结果写入可共享状态(例如扩展 `AppModel` 或新增 `GameSettings`),并让 `GameManager` 根据选择切换:
- 不同鼓模型/不同主题场景/不同歌曲与谱面
3. 明确谱面时间与歌曲的同步策略(offset、倍速、BPM),将魔法系数参数化。
4. 补最小单测:`Stage` 解码、`generateStart(note:)` 映射、`ImmersiveSpaceState` 状态机转换。

## 6. 设计决策记录

### 决策:用 Reality Composer Pro 行为(NotificationTrigger)驱动场景动画,而不是在 Swift 中逐帧控制

- 背景:鼓面弹跳、灯光、特效等更适合在可视化工具中调参;代码只需触发事件。
- 选项:
- A) Swift/RealityKit 代码中管理动画资源并手动播放
- B) RCP 内定义 Timeline + 通过通知触发(当前方案)
- 决定:选择 B
- 原因:资产侧可快速迭代视觉效果;代码侧只维护 identifier 对齐与触发时机,耦合更低。

## 7. 构建与测试

- Build:用 Xcode 打开 `DrumGo.xcodeproj`,选择 scheme `DrumGo`,运行到 Vision Pro Simulator 或真机。
- Test:`DrumGoTests/DrumGoTests.swift`(Testing 框架)目前为空,需要补用例后再作为质量门槛。

156 changes: 156 additions & 0 deletions .github/docs/OpenvisionOS 业务.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
# OpenvisionOS 业务全局地图(business-logic)

## 产品概述
OpenvisionOS 是一个面向 visionOS 开发者与设计探索者的 3D 体验示例应用集合。目标用户是想快速学习 SwiftUI + RealityKit 场景组织方式的人。核心体验是在同一应用内打开多个独立窗口,分别浏览不同主题的 3D 模型与动效展示。技术栈为 Swift 5 + SwiftUI + RealityKit + 本地 USDZ 资产(含本地 Swift Package `RealityKitContent`)。

## 架构分层
目录结构 → 职责说明 → 依赖方向

- `OpenvisionOS/OpenvisionOSApp.swift` → 应用编排层:注册窗口与入口场景
- `OpenvisionOS/AirPodsMax/`、`OpenvisionOS/Own3DModel/`、`OpenvisionOS/NewYearFireworks/` → 场景展示层:每个模块负责一个主题场景 UI + 模型加载
- `OpenvisionOS/**/*.usdz`、`OpenvisionOS/Assets.xcassets` → 资源层:模型与视觉资源
- `Packages/RealityKitContent/` → 内容包层:作为 RealityKit 内容包与未来复用入口
- `OpenvisionOSTests/` → 测试层:预留测试入口(当前为模板)

依赖方向:

- 应用编排层 → 场景展示层
- 场景展示层 → 资源层 / RealityKit / SwiftUI
- 测试层 → 应用编排层与场景展示层(`@testable import OpenvisionOS`)

禁止(建议约束):

- 禁止跨场景模块直接互相依赖(例如 AirPods 模块直接调用 Flower 模块实现)
- 禁止把业务流程写入资源层(资源层只放素材,不放逻辑)

## 核心业务流程

### 流程 1:用户进入并浏览 AirPods Max 动画窗口
1. 用户在系统中打开应用后,看到三个可用窗口入口(由 `OpenvisionOS/OpenvisionOSApp.swift` 注册)。
2. 用户进入 “AirPods Max” 窗口,加载 `OpenvisionOS/AirPodsMax/AirPodsMaxAnimation.swift`。
3. 视图调用 `Model3D(named: "Airpods_Max_Pink")` 查找并加载 `OpenvisionOS/AirPodsMax/Airpods_Max_Pink.usdz`。
4. 加载期间显示 `ProgressView` 占位;加载成功后显示模型。
5. `phaseAnimator` 驱动持续旋转,用户看到 3D 模型循环转动并显示底部电量装饰信息。

### 流程 2:用户浏览 Flower Pot 组合场景
1. 用户进入 “Flower Pot View” 窗口,渲染 `OpenvisionOS/Own3DModel/FlowerPotView.swift`。
2. 场景在 `ZStack` 中分层加载多个模型:`pointSparkle`、`Flower-Port`、`BgSparcle`。
3. 通过缩放与偏移控制前景点缀位置,背景模型增强空间氛围。
4. 用户最终看到前中后景叠加的组合式 3D 视觉效果。

### 流程 3:用户浏览 New Year 主题场景
1. 用户进入 “New Year Fireworks” 窗口,渲染 `OpenvisionOS/NewYearFireworks/NewYearFireworksTwentyFour.swift`。
2. 视图加载 `newYear` 模型(`OpenvisionOS/NewYearFireworks/newYear.usdz`)。
3. 场景按适配比例展示并留白,形成单主题沉浸展示页。

## 模块详情

### 模块 A:应用编排模块
- 做什么:定义应用入口与窗口结构,决定用户可访问的体验集合。
- 关键实现:通过多个 `WindowGroup` 并行注册体验,而不是单 Tab 内切。
- 相关文件:
- `OpenvisionOS/OpenvisionOSApp.swift`
- `OpenvisionOS/Info.plist`
- 单测:无专门单测。

### 模块 B:AirPods Max 场景模块
- 做什么:展示 AirPods 模型并持续旋转,作为动效示例。
- 关键实现:使用 `phaseAnimator + rotation3DEffect` 形成无限旋转;工具栏添加设备状态感 UI。
- 相关文件:
- `OpenvisionOS/AirPodsMax/AirPodsMaxAnimation.swift`
- `OpenvisionOS/AirPodsMax/Airpods_Max_Pink.usdz`
- `OpenvisionOS/AirPodsMax/Airpods_Max.usdz`
- `OpenvisionOS/AirPodsMax/kulaklıksketchfab.usdz`
- 单测:无专门单测。

### 模块 C:Flower Pot 场景模块
- 做什么:构建多模型叠加场景,演示组合式空间画面搭建。
- 关键实现:使用 `ZStack` 构建前后景,前景粒子点缀 + 主体花盆 + 背景闪烁层。
- 相关文件:
- `OpenvisionOS/Own3DModel/FlowerPotView.swift`
- `OpenvisionOS/Own3DModel/Flower-Port.usdz`
- `OpenvisionOS/Own3DModel/pointSparkle.usdz`
- `OpenvisionOS/Own3DModel/BgSparcle.usdz`
- 单测:无专门单测。

### 模块 D:New Year Fireworks 场景模块
- 做什么:提供节日主题场景展示页。
- 关键实现:单模型加载 + 基础缩放布局,当前不含复杂交互。
- 相关文件:
- `OpenvisionOS/NewYearFireworks/NewYearFireworksTwentyFour.swift`
- `OpenvisionOS/NewYearFireworks/newYear.usdz`
- 单测:无专门单测。

### 模块 E:RealityKitContent 包模块
- 做什么:提供 RealityKit 内容包与 bundle 访问点。
- 关键实现:当前仅导出 `Bundle.module`,作为后续共享资源或工具层预留。
- 相关文件:
- `Packages/RealityKitContent/Package.swift`
- `Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.swift`
- 单测:无。

### 模块 F:测试模块
- 做什么:承载未来自动化测试。
- 关键实现:目前仍是 Xcode 默认模板,未落地业务测试。
- 相关文件:
- `OpenvisionOSTests/OpenvisionOSTests.swift`
- 单测:当前文件本身为模板测试入口。

## 当前状态与待办

### 已完成
- [x] 完成多窗口架构(AirPods Max、Flower Pot、New Year 三个独立窗口)
- [x] 完成三个基础 3D 场景视图与本地模型加载
- [x] 完成 AirPods 模块的持续旋转动效实现
- [x] 完成核心 USDZ 资源打包与工程资源绑定

### 进行中
- [ ] 场景工程化改造(复用组件、资源常量化、错误态展示)— 当前仍以演示代码为主,尚未抽象公共层

### 已知问题
- 缺少有效单测,场景正确性依赖手工验证
- 模型名硬编码为字符串,资源重命名有运行时风险
- 场景异常处理较弱,仅有加载占位,缺少失败态与可观测日志
- 三个场景存在重复 `Model3D` 加载模式,后续维护成本会增加

### 下一步计划
1. 建立统一模型资源索引(例如 `enum` 常量)并替换硬编码字符串
2. 抽取通用 `Model3D` 渲染容器(统一占位、错误态、缩放策略)
3. 为每个场景补最小可运行测试(至少覆盖视图可构建与关键资源可解析)
4. 增加新场景模板,验证模块化扩展路径与代码复用效果
5. 评估将窗口注册改为配置驱动,降低入口文件修改成本

## 设计决策记录

### 决策 1:采用“多 WindowGroup”而不是“单 Window + Tab”作为默认入口
- 背景:项目定位为 visionOS 多体验示例集合,需支持并行展示多个主题场景。
- 选项:
- A. 单窗口 + Tab 切换
- B. 多窗口(多个 `WindowGroup`)
- 决定:选 B(多窗口)。
- 原因:更贴近 visionOS 空间多窗口使用方式,也便于独立演示每个场景。

### 决策 2:采用“模块就近资源存放”,而不是“全局资源大目录”
- 背景:当前项目主要是主题化演示模块,单模块内强依赖专属模型。
- 选项:
- A. 所有 USDZ 集中到一个全局目录
- B. 模块目录内就近存放本模块资源
- 决定:选 B(就近存放)。
- 原因:提高可读性与迁移便利性,新增/删除场景时改动边界更清晰。

### 决策 3:优先使用 SwiftUI `Model3D` 直接渲染,而非先构建复杂 RealityKit ECS
- 背景:项目目标是快速验证视觉展示与动画效果,非复杂交互仿真。
- 选项:
- A. 从一开始就搭建完整实体组件系统(ECS)
- B. 先用 `Model3D` + SwiftUI 组合实现核心体验
- 决定:选 B(`Model3D` 优先)。
- 原因:实现路径更短,便于快速迭代展示;后续若交互复杂再下沉到更底层 RealityKit 架构。

## 构建与测试
- Build:
- `xcodebuild -project OpenvisionOS.xcodeproj -scheme OpenvisionOS -configuration Debug -destination 'generic/platform=visionOS' -derivedDataPath ./.derived build`
- Test:
- `xcodebuild -project OpenvisionOS.xcodeproj -scheme OpenvisionOS -destination 'generic/platform=visionOS Simulator' -derivedDataPath ./.derived test`
- Lint:
- 当前未配置专门 lint 工具(如 SwiftLint)。

Loading
Loading