diff --git a/Doc/04_美术资源对接与场景配置指南.md b/Doc/04_美术资源对接与场景配置指南.md new file mode 100644 index 0000000..4a42567 --- /dev/null +++ b/Doc/04_美术资源对接与场景配置指南.md @@ -0,0 +1,374 @@ +# VFS 美术资源对接与场景配置指南 + +> 给你的美术和场景搭建同学看这份文档,他们就知道要做什么、怎么做、命名规则是什么。 +> 你作为项目负责人,拿到美术交付物后,按本文逐项检查即可。 + +--- + +## 一、你需要交付的美术资源清单 + +### 1.1 场景模型 + +| 资源 | 格式 | 要求 | +|------|------|------| +| 室内田径馆 | FBX/GLB | 包含跑道、看台、跳高架、跳高垫。比例正确(真人 1.7m 对应 Unity 1.7 单位)。Pivot 在地面中心。 | +| 跳高架 | FBX/GLB | 可独立放置,带横杆 | +| 跳高垫 | FBX/GLB | 软材质效果,1m × 3m × 0.5m | + +### 1.2 角色模型 + +| 资源 | 格式 | 骨骼 | 备注 | +|------|------|------|------| +| 伤者(男性、运动装) | FBX + 骨骼绑定 | Humanoid,65 根骨骼以内 | Mixamo 自动绑骨亦可,必须带 Humanoid Avatar | +| 施救者(女性、急救员装束) | FBX + 骨骼绑定 | Humanoid,65 根骨骼以内 | 同上 | + +### 1.3 道具模型 + +| 资源 | 格式 | 数量 | 备注 | +|------|------|------|------| +| 急救箱(带红十字) | FBX/GLB | 1 | 可放场景角落,静态物件 | +| 绷带/绑带(蓝色) | FBX/GLB | 4 条 | 分段模型,每条独立,绑定到腿时可缩放/旋转 | +| 三角巾 | FBX/GLB | 1 | | +| 夹板(木板) | FBX/GLB | 2 块 | 内侧夹板 80cm,外侧夹板 120cm | +| 衬垫(白色软垫) | FBX/GLB | 2 块 | 放关节和骨突处用 | +| 左鞋(运动鞋) | FBX/GLB | 1 | 独立于角色,可被脱掉/隐藏 | +| 右鞋(运动鞋) | FBX/GLB | 1 | 同上 | +| 8 字绑带(脚踝用) | FBX/GLB | 1 | | + +### 1.4 动画(如果做) + +| 资源 | 时长 | 循环 | 备注 | +|------|------|------|------| +| 伤者_跑步 | 1s | 是 | | +| 伤者_跳高起跳 | 1.5s | 否 | | +| 伤者_空中姿态 | 0.8s | 否 | | +| 伤者_落地摔倒 | 2s | 否 | 落地→倒地→静止 | +| 伤者_躺地(受伤) | 2s | 是 | 最终 Idle | +| 施救者_站立 | 1s | 是 | | +| 施救者_蹲下 | 1s | 否 | | +| 施救者_检查伤情 | 2s | 否 | 手部触摸伤腿 | +| 施救者_脱鞋 | 2s | 否 | 手伸向鞋子 | +| 施救者_包扎(手部特写) | 3s | 否 | | + +> 没有动捕可以用 Mixamo 下载基础动画拼接。 + +### 1.5 视频 + +| 资源 | 分辨率 | 时长 | 文件名 | +|------|--------|------|--------| +| 真人演示·放置衬带 | 1080p | 30s | `bandage_demo.mp4` | +| 真人演示·夹板固定 | 1080p | 30s | `splint_demo.mp4` | + +放到 `StreamingAssets/Videos/` 下。 + +### 1.6 音频(可选) + +| 资源 | 格式 | 文件名示例 | +|------|------|-----------| +| 背景音乐 | MP3 320kbps | `bgm_experiment.mp3` | +| 语音_环境安全 | WAV 16bit | `voice_01_safe.wav` | +| 语音_伤情检查 | WAV 16bit | `voice_02_injury.wav` | +| ... | ... | ... | + +放到 `StreamingAssets/Voice/` 下。 + +### 1.7 UI 素材 + +| 资源 | 尺寸 | 格式 | 备注 | +|------|------|------|------| +| 图标_心肺复苏 | 128×128 | PNG 透明 | | +| 图标_骨折固定 | 128×128 | PNG 透明 | | +| 图标_扭伤包扎 | 128×128 | PNG 透明 | | +| 图标_出血包扎 | 128×128 | PNG 透明 | | +| 图标_伤员搬运 | 128×128 | PNG 透明 | | +| 按钮_全屏 | 64×64 | PNG/SVG | | +| 按钮_上一步 | 64×64 | PNG/SVG | | +| 按钮_下一步 | 64×64 | PNG/SVG | | +| 按钮_返回 | 64×64 | PNG/SVG | | +| 按钮_确定 | 200×60 | PNG 含文字 | | +| 面板_半透明底 | 可拉伸 9-slice | PNG | | +| 提示框背景 | 可拉伸 9-slice | PNG | | + +--- + +## 二、Unity 场景搭建步骤 + +拿到所有资源后,按以下顺序操作。 + +### 2.1 导入资产 + +1. 将模型 FBX 拖入 `Assets/Models/` 文件夹 +2. 将贴图拖入 `Assets/Textures/`(如有) +3. 将视频拖入 `StreamingAssets/Videos/` +4. 将音频拖入 `StreamingAssets/Voice/` +5. 将 UI 图片拖入 `Assets/UI/Sprites/` +6. 将动画 FBX 拖入 `Assets/Animations/` + +### 2.2 创建 Experiment 场景 + +File → New Scene → 命名为 `Experiment` → 保存到 `Assets/Scenes/` + +### 2.3 设置光照 + +- Directional Light:模拟室内顶灯,Intensity 0.8,Color 暖白 (#FFF5E6) +- 可选:Light Probe Group 放在场景四角和中心 + +### 2.4 放置场景 + +1. 把田径馆模型拖入 Hierarchy → Position (0, 0, 0) +2. 把跳高架放在跑道区域 → Position 根据模型调整 +3. 把跳高垫放在跳高架落地区 + +### 2.5 放置角色 + +1. 伤者 → Hierarchy → Position:躺在跑道上(大致 X:2, Y:0, Z:5) + - 初始姿态:仰卧位,身体平放,头朝跑道方向 + - 挂 `CharacterAnimController.cs` → Role 选 `Patient` + - 挂 `Animator` 组件,配置好动画状态机 +2. 施救者 → Position:站在伤者右侧(X:3, Y:0, Z:5) + - 挂 `CharacterAnimController.cs` → Role 选 `Rescuer` + - 挂 `Animator` 组件 + +### 2.6 放置道具 + +| 道具 | 位置 | 说明 | +|------|------|------| +| 急救箱 | 伤者身旁地面 (X:3.5, Y:0, Z:4.5) | 静态,无交互 | +| 左鞋 | 伤者左脚位置 | 初始穿在脚上,可被隐藏/移动 | +| 右鞋 | 伤者右脚位置 | 同上 | +| 绑带 ×4 | 急救箱旁 | 之后拖到腿上 | +| 衬垫 ×2 | 急救箱旁 | | +| 夹板 ×2 | 急救箱旁 | 夹板固定模式用 | +| 8 字绑带 | 急救箱旁 | 踝关节固定用 | + +### 2.7 最关键:给物体加 Collider 和 Tag + +**这是代码能找到物体的唯一方式,必须做对。** + +| GameObject | Collider 类型 | Tag 名称 | 说明 | +|------------|-------------|----------|------| +| 伤者左大腿 | Box Collider(包裹股骨区域) | `InjuryLeg` | 初始伤情检查点击区域 | +| 伤者左大腿·视觉检查区 | Box Collider | `InjuryVisual` | 伤情检查子项 1:肿胀变形 | +| 伤者左大腿·肿胀触摸区 | Box Collider | `InjurySwelling` | 伤情检查子项 2:肿胀畸形 | +| 伤者胸口 | Box Collider | `InjuryPain` | 伤情检查子项 3:疼痛评估(模拟听诊/询问) | +| 伤者手腕 | Box Collider | `InjuryVitalSigns` | 伤情检查子项 4:生命体征(模拟测脉搏) | +| 伤者头部 | Box Collider | `InjuryNeuro` | 伤情检查子项 5:神经评估 | +| 左鞋 | Box Collider | `Shoe` | 脱鞋步骤 | +| 右鞋 | Box Collider | `Shoe` | 同上 | +| 绑带位置 1(近心端·大腿上部) | Box Collider | `BandagePos1` | 绑带固定位置 | +| 绑带位置 2(远心端·大腿下部) | Box Collider | `BandagePos2` | | +| 绑带位置 3(膝关节下) | Box Collider | `BandagePos3` | | +| 衬垫位置(两腿间) | Box Collider | `PaddingPos` | 拖拽衬垫吸附目标 | +| 踝关节 | Sphere Collider | `Ankle` | 8 字固定 | +| 夹板左侧位置 | Box Collider | `SplintLeft` | 夹板拖拽吸附 | +| 夹板右侧位置 | Box Collider | `SplintRight` | 夹板拖拽吸附 | +| 场景地面 | Box Collider(大面积) | `Ground` | ClickAnywhere 用 | + +> **Tag 名称必须完全一致**,包括大小写。代码用 `tag == "InjuryLeg"` 匹配。 + +### 2.8 给可交互物体加脚本组件 + +| GameObject | 挂载脚本 | 关键设置 | +|------------|---------|---------| +| 绑带物件 ×4 | `DraggableObject.cs` | `SnapTarget` 拖入对应的 BandagePos 空物体 | +| 衬垫 | `DraggableObject.cs` | `SnapTarget` 拖入 PaddingPos | +| 夹板 ×2 | `DraggableObject.cs` | `SnapTarget` 拖入对应夹板位置 | +| 伤者左大腿 | `HighlightEffect.cs` | 考核模式可设 AlwaysHighlighted = true | +| 所有带 Collider 的交互物体 | `HighlightEffect.cs`(可选) | 悬停时会变色,方便用户找到 | + +### 2.9 创建管理器空物体 + +Hierarchy → Create Empty → 命名为 `[Managers]` + +在 `[Managers]` 上依次 Add Component: +1. `GameManager.cs` — 无需配置,Awake 时自动挂载 +2. `StepManager.cs` — 后续在 Inspector 拖入 Config +3. `ExperimentFlowCoordinator.cs` — 后续拖入各子系统引用 +4. `AudioManager.cs` — 拖入 BGMClip + +### 2.10 配置相机 + +1. 主相机(Main Camera)→ 挂在 `[Managers]` 下或独立 + - Position:面对伤者,俯角 30°,距离约 3 米 + - 挂载 `InteractionSystem.cs` + - `Interactable Mask` 选包含所有交互物体的 Layer +2. 特写相机(可选):用于包扎特写,放在 `ExperimentFlowCoordinator` 里管理 + +### 2.11 搭建 UI Canvas + +Hierarchy → UI → Canvas(命名为 `UICanvas`) + +**内部结构(父子关系):** +``` +UICanvas +├── [挂 UIManager.cs] +│ +├── TipPanel(顶部提示栏) +│ └── TipText(TMP_Text) +│ +├── DetailPanel(详细操作说明面板 · 学习模式) +│ └── DetailText(TMP_Text,多行可滚动) +│ +├── DialogueBubble(对话气泡) +│ └── DialogueText(TMP_Text) +│ +├── VideoPanel(画中画视频) +│ ├── RawImage(挂 VideoController.cs) +│ └── VideoPlayer(子物体或被 VideoController 引用) +│ +├── RightButtons(右侧功能按钮栏) +│ ├── BtnFullScreen(Button) +│ ├── BtnPrevious(Button) +│ ├── BtnNext(Button) +│ ├── BtnSwitchMode(Button + TMP_Text 标签) +│ └── BtnReturn(Button) +│ +├── ProgressBar(底部进度条) +│ ├── Slider +│ └── ProgressText(TMP_Text 显示 "3/8") +│ +├── ExamPopup(考核弹窗 · 挂 ExamPopup.cs) +│ ├── QuestionText(TMP_Text) +│ ├── OptionToggles(Toggle × 4) +│ ├── OptionLabels(TMP_Text × 4) +│ ├── FeedbackText(TMP_Text 红色) +│ ├── OperationPanel(操作题提示) +│ │ └── OperationHint(TMP_Text) +│ └── ConfirmButton(Button) +│ +├── ScoreDisplay(成绩面板 · 挂 ScoreDisplay.cs) +│ ├── ExamResultRoot +│ │ ├── ScoreTitle(TMP_Text) +│ │ ├── ScoreValue(TMP_Text) +│ │ └── AnalysisListParent(Scroll View → Content) +│ │ └── AnalysisItemPrefab(预制体:TMP_Text) +│ ├── NotesRoot(注意事项面板) +│ │ └── NotesText(TMP_Text) +│ └── ConfirmButton +│ +└── BottomIcons(底部图标栏) + ├── BtnCPR + ├── BtnFracture + ├── BtnSprain + ├── BtnBleeding + └── BtnTransport +``` + +### 2.12 Inspector 连线 + +打开 `[Managers]` → `ExperimentFlowCoordinator`,拖入: +| 字段 | 拖入对象 | +|------|---------| +| Step Manager | `[Managers]`(自身的 StepManager) | +| Interaction System | Main Camera(自身的 InteractionSystem) | +| UI Manager | `UICanvas`(自身的 UIManager) | +| Step Animation Handler | `StepAnimationHandler` 所在物体 | +| Audio Manager | `[Managers]`(自身的 AudioManager) | + +打开 `UICanvas` → `UIManager`,拖入所有子面板引用。 + +--- + +## 三、创建 MainMenu 场景 + +File → New Scene → 命名为 `MainMenu` → 保存。 + +### 3.1 结构 + +``` +UICanvas +├── [挂 MainMenu.cs] +├── TitleText(TMP_Text:"运动伤害现场急救处理虚拟仿真实验") +├── TipText(TMP_Text:"Tip:请选择实验模式") +├── ExperimentButtons +│ ├── BtnCPR(Button + 图标) +│ ├── BtnFracture(Button + 图标) +│ ├── BtnSprain(Button + 图标) +│ ├── BtnBleeding(Button + 图标) +│ └── BtnTransport(Button + 图标) +├── ModePanel +│ ├── BtnLearnMode(Button) +│ ├── BtnExamMode(Button) +│ └── SelectedModeLabel(TMP_Text) +├── FixationPanel(骨折固定专属) +│ ├── BtnLimbFixation(Button) +│ ├── BtnSplintFixation(Button) +│ └── SelectedFixationLabel(TMP_Text) +└── BtnStart(Button "开始实验") +``` + +### 3.2 空物体 + +Create Empty → 命名 `[GameManager]` → 挂 `GameManager.cs` + +### 3.3 连线 + +MainMenu.cs 的 Inspector 中拖入对应的 Button 和 TMP_Text 引用。 + +--- + +## 四、检查清单(交付前逐项打勾) + +### 模型 +- [ ] 田径馆模型比例正确 +- [ ] 伤者 Humanoid Avatar 配置完成(Inspector → Rig → Animation Type = Humanoid) +- [ ] 施救者 Humanoid Avatar 配置完成 +- [ ] 所有道具 FBX 正确导入,材质不丢失 + +### Collider & Tag +- [ ] 伤者左大腿 Collider 覆盖正确区域,Tag = `InjuryLeg` +- [ ] 5 个伤情检查子项 Collider 各覆盖正确部位,Tag 正确 +- [ ] 鞋子 Collider + Tag = `Shoe` +- [ ] 绑带位置 1 2 3 空物体有 Collider + Tag = `BandagePos1/2/3` +- [ ] 衬垫位置空物体 Collider + Tag = `PaddingPos` +- [ ] 踝关节 Collider + Tag = `Ankle` +- [ ] 夹板位置 Collider + Tag = `SplintLeft` / `SplintRight` +- [ ] 场景地面 Collider + Tag = `Ground` +- [ ] 所有交互物体的 Layer 在 `InteractionSystem.InteractableMask` 范围内 + +### 脚本挂载 +- [ ] `[Managers]` 挂 GameManager + StepManager + ExperimentFlowCoordinator + AudioManager +- [ ] Main Camera 挂 InteractionSystem +- [ ] UICanvas 挂 UIManager +- [ ] ExamPopup 面板挂 ExamPopup.cs +- [ ] ScoreDisplay 面板挂 ScoreDisplay.cs +- [ ] VideoPanel 挂 VideoController.cs +- [ ] 绑带/衬垫/夹板挂 DraggableObject.cs,SnapTarget 已拖入 + +### 连线 +- [ ] ExperimentFlowCoordinator 所有字段已拖入 +- [ ] UIManager 所有字段已拖入 +- [ ] MainMenu 所有字段已拖入 + +### ScriptableObject +- [ ] `Create → VFS → Step Data` 创建了 8 个步骤(肢体固定)或更多 +- [ ] 每个 StepData 的 ScoreWeight 已按评分标准填入(5, 20, 10, 10, 10, 20, 15, 10) +- [ ] 每个 StepData 的 DetailedContent 已填入(至少 CheckInjury 步骤) +- [ ] `Create → VFS → Experiment Config` 创建了配置 +- [ ] LearnStepsLimb / LearnStepsSplint / ExamStepsLimb / ExamStepsSplint 四个数组已填入 +- [ ] StepManager.Config 已拖入 ExperimentConfig + +### 验证 +- [ ] Play 进入 Experiment 场景,UIManager 能显示第一步 Tip 文案 +- [ ] 点击伤者大腿 → InteractionSystem 输出 Debug.Log 包含 `Tag=InjuryLeg` +- [ ] 考核模式下弹窗正常弹出 +- [ ] 答题后 ScoreDisplay 正常显示 + +--- + +## 五、常见问题 + +**Q:FBX 导入后材质是紫色的?** +A:材质丢失。在 FBX 的 Import Settings → Materials → Extract Textures 重新提取。 + +**Q:骨骼动画播放时角色扭曲?** +A:Avatar 未正确配置。Inspector → Rig → Configure Avatar,检查骨骼映射是否正确。 + +**Q:点击 3D 物体没反应?** +A:检查 (1) Collider 是否勾选了 `Is Trigger`(不要勾),(2) Layer 是否在主相机的 Culling Mask 和 InteractionSystem 的 InteractableMask 范围内,(3) Tag 是否正确。 + +**Q:Canvas 上的 UI 遮挡了 3D 点击?** +A:InteractionSystem 已内置 `IsPointerOverGameObject()` 检测,不会同时触发。确保 Canvas 的 Graphic Raycaster 正常工作。 + +**Q:成绩显示 0 分?** +A:检查 StepData 的 ScoreWeight 是否已填入非 0 值,考核模式下 `RecordExamResult` 的 proportion 参数是否传了 1f。