Files
VFSUnity/Assets/Scripts/UI/UIManager.cs
T
yangbear 4540ff4942 fix: 移除UIManager重复按钮绑定+InteractionSystem误触发——根因修复
根因: UIManager._btnLimbFixation/_btnSplintFixation调用SwitchFixation
与PageController的OnFixationSelected双重触发,StepManager._mode被设为Learn
导致OnStepInteractionComplete→NextStep→WomanStepNext自动跳过步骤。

修复:
1. UIManager.BindButtons移除_btnLimbFixation/_btnSplintFixation的SwitchFixation绑定
2. ExperimentFlowCoordinator.OnAnywhereClicked增加无Config步骤时忽略的注释守卫
2026-06-30 21:59:47 +08:00

341 lines
12 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// UI 中央调度器:引用所有子面板,绑定 StepManager 事件。
/// 按钮策略:同时绑定 Inspector 引用(旧 RightButtons)和 ExperimentPanel/Navbar 按钮(新 UI),
/// 两者均可触发,互不冲突。
/// </summary>
public class UIManager : MonoBehaviour
{
[Header("管理器引用")]
[SerializeField] private StepManager _stepManager;
[SerializeField] private InteractionSystem _interactionSystem;
[Header("=== 顶部提示栏 ===")]
[SerializeField] private GameObject _tipPanel;
[SerializeField] private Text _tipText;
[Header("=== 详细操作说明面板 ===")]
[SerializeField] private GameObject _detailPanel;
[SerializeField] private Text _detailText;
[Header("=== 对话气泡 ===")]
[SerializeField] private GameObject _dialogueBubble;
[SerializeField] private Text _dialogueText;
[Header("=== 画中画视频 ===")]
[SerializeField] private VideoController _videoController;
[Header("=== 仿真导航栏按钮(旧引用 + Navbar===")]
[SerializeField] private Button _btnFullScreen;
[SerializeField] private Button _btnPrevious;
[SerializeField] private Button _btnNext;
[SerializeField] private Button _btnSwitchMode;
[SerializeField] private Button _btnReturn;
[SerializeField] private Text _btnSwitchModeLabel;
[Header("=== 底部进度条 ===")]
[SerializeField] private Slider _progressBar;
[SerializeField] private Text _progressText;
[Header("=== 弹窗 ===")]
[SerializeField] private ExamPopup _examPopup;
[SerializeField] private ScoreDisplay _scoreDisplay;
[Header("=== 底部图标 ===")]
[SerializeField] private Button _btnCPR;
[SerializeField] private Button _btnFracture;
[SerializeField] private Button _btnSprain;
[SerializeField] private Button _btnBleeding;
[SerializeField] private Button _btnTransport;
[Header("=== 固定方式选择(仅骨折固定) ===")]
[SerializeField] private GameObject _fixationPanel;
[SerializeField] private Button _btnLimbFixation;
[SerializeField] private Button _btnSplintFixation;
private void Start()
{
Debug.Log("=== UIManager 初始化开始 ===");
if (_stepManager == null)
{
_stepManager = FindObjectOfType<StepManager>();
if (_stepManager != null) Debug.Log("UIManager: _stepManager 自动查找成功");
else Debug.LogError("UIManager: _stepManager 未找到!");
}
if (_interactionSystem == null)
{
_interactionSystem = FindObjectOfType<InteractionSystem>();
if (_interactionSystem != null) Debug.Log("UIManager: _interactionSystem 自动查找成功");
}
BindEvents();
BindButtons(); // 旧引用(RightButtons 等)
BindNavbarButtons(); // 新 Navbar 按钮
Debug.Log("=== UIManager 初始化完成 ===");
}
#region Navbar 按钮绑定(始终执行,不依赖 Inspector 引用)
private void BindNavbarButtons()
{
Debug.Log("--- UIManager: 开始绑定 Navbar 按钮 ---");
var experimentPanel = GameObject.Find("ExperimentPanel");
if (experimentPanel == null)
{
Debug.LogError("UIManager: ExperimentPanel 未找到!无法绑定 Navbar 按钮!");
return;
}
var navbar = experimentPanel.transform.Find("Navbar");
if (navbar == null)
{
Debug.LogError("UIManager: ExperimentPanel/Navbar 未找到!无法绑定 Navbar 按钮!");
return;
}
// FullScreen
var btnFS = navbar.Find("FullScreen");
if (btnFS != null)
{
var btn = btnFS.GetComponent<Button>();
if (btn != null)
{
btn.onClick.AddListener(ToggleFullScreen);
Debug.Log("UIManager: Navbar/FullScreen 绑定成功 -> ToggleFullScreen");
}
}
else Debug.LogWarning("UIManager: Navbar/FullScreen 未找到");
// Previous
var btnPrev = navbar.Find("Previous");
if (btnPrev != null)
{
var btn = btnPrev.GetComponent<Button>();
if (btn != null)
{
btn.onClick.AddListener(OnPreviousClicked);
Debug.Log("UIManager: Navbar/Previous 绑定成功 -> OnPreviousClicked");
}
}
else Debug.LogWarning("UIManager: Navbar/Previous 未找到");
// Next
var btnNext = navbar.Find("Next");
if (btnNext != null)
{
var btn = btnNext.GetComponent<Button>();
if (btn != null)
{
btn.onClick.AddListener(OnNextClicked);
Debug.Log("UIManager: Navbar/Next 绑定成功 -> OnNextClicked");
}
}
else Debug.LogWarning("UIManager: Navbar/Next 未找到");
// PracticeMode
var btnMode = navbar.Find("PracticeMode");
if (btnMode != null)
{
var btn = btnMode.GetComponent<Button>();
if (btn != null)
{
btn.onClick.AddListener(OnSwitchModeClicked);
Debug.Log("UIManager: Navbar/PracticeMode 绑定成功 -> OnSwitchModeClicked");
}
}
else Debug.LogWarning("UIManager: Navbar/PracticeMode 未找到");
// BtnBack(返回按钮)
var btnBack = navbar.Find("BtnBack");
if (btnBack != null)
{
var btn = btnBack.GetComponent<Button>();
if (btn != null)
{
btn.onClick.AddListener(OnReturnClicked);
Debug.Log("UIManager: Navbar/BtnBack 绑定成功 -> OnReturnClicked");
}
}
else Debug.LogWarning("UIManager: Navbar/BtnBack 未找到");
Debug.Log("--- UIManager: Navbar 按钮绑定完成 ---");
}
#endregion
#region 事件绑定
private void BindEvents()
{
if (_stepManager == null) return;
_stepManager.OnStepChanged.AddListener(OnStepChanged);
_stepManager.OnTipChanged.AddListener(UpdateTip);
_stepManager.OnDialogueChanged.AddListener(UpdateDialogue);
_stepManager.OnDetailedContent.AddListener(UpdateDetailContent);
_stepManager.OnExamPopup.AddListener(ShowExamPopup);
_stepManager.OnExperimentComplete.AddListener(OnExperimentComplete);
_stepManager.OnVideoToggle.AddListener(ToggleVideo);
Debug.Log("UIManager: StepManager 事件绑定完成");
}
private void BindButtons()
{
if (_btnFullScreen != null) { _btnFullScreen.onClick.AddListener(ToggleFullScreen); Debug.Log("UIManager: _btnFullScreen 绑定成功"); }
if (_btnPrevious != null) { _btnPrevious.onClick.AddListener(OnPreviousClicked); Debug.Log("UIManager: _btnPrevious 绑定成功"); }
if (_btnNext != null) { _btnNext.onClick.AddListener(OnNextClicked); Debug.Log("UIManager: _btnNext 绑定成功"); }
if (_btnSwitchMode != null) { _btnSwitchMode.onClick.AddListener(OnSwitchModeClicked); Debug.Log("UIManager: _btnSwitchMode 绑定成功"); }
if (_btnReturn != null) { _btnReturn.onClick.AddListener(OnReturnClicked); Debug.Log("UIManager: _btnReturn 绑定成功"); }
// 注意: _btnLimbFixation/_btnSplintFixation 不再在这里绑定 SwitchFixation
// 固定方式选择由 PageController 的 OnFixationSelected 统一处理,避免双重触发污染 StepManager 状态。
}
#endregion
#region UI 更新回调
private void OnStepChanged(int index)
{
Debug.Log($"UIManager: [OnStepChanged] 步骤 = {index + 1}");
UpdateProgressBar();
UpdateNavigationButtons();
}
private void UpdateTip(string text)
{
if (_tipPanel != null) _tipPanel.SetActive(!string.IsNullOrEmpty(text));
if (_tipText != null) _tipText.text = text;
}
private void UpdateDialogue(string text)
{
if (_dialogueBubble != null) _dialogueBubble.SetActive(!string.IsNullOrEmpty(text));
if (_dialogueText != null) _dialogueText.text = text;
}
private void UpdateDetailContent(string text)
{
if (_detailPanel != null) _detailPanel.SetActive(!string.IsNullOrEmpty(text));
if (_detailText != null) _detailText.text = text;
}
private void ToggleVideo(string fileName, bool active)
{
Debug.Log($"UIManager: [ToggleVideo] file={fileName}, active={active}");
if (_videoController == null) return;
if (active && !string.IsNullOrEmpty(fileName))
_videoController.PlayVideo(fileName);
else
_videoController.StopVideo();
}
private void UpdateProgressBar()
{
if (_stepManager == null) return;
int cur = _stepManager.CurrentStepIndex + 1;
int total = _stepManager.TotalSteps;
if (_progressBar != null) _progressBar.value = (float)cur / total;
if (_progressText != null) _progressText.text = cur + "/" + total;
}
private void UpdateNavigationButtons()
{
if (_stepManager == null) return;
if (_btnPrevious != null) _btnPrevious.interactable = _stepManager.CurrentStepIndex > 0;
if (_btnNext != null)
{
var label = _btnNext.GetComponentInChildren<Text>();
bool isLast = _stepManager.CurrentStepIndex >= _stepManager.TotalSteps - 1;
if (label != null) label.text = isLast ? "完成" : "下一步";
}
}
#endregion
#region 考核弹窗
private void ShowExamPopup(StepData step)
{
Debug.Log($"UIManager: [ShowExamPopup] 题目={step.ExamQuestionText}");
if (_examPopup == null) return;
_examPopup.Show(step, OnExamAnswer);
}
private void OnExamAnswer(float proportion, string feedback)
{
Debug.Log($"UIManager: [OnExamAnswer] proportion={proportion}, feedback={feedback}");
_stepManager?.OnStepInteractionComplete(proportion, feedback);
_stepManager?.NextStep();
}
#endregion
#region 实验完成
private void OnExperimentComplete()
{
Debug.Log("UIManager: [OnExperimentComplete] 实验完成");
var gm = GameManager.Instance;
if (gm == null) return;
if (gm.CurrentMode == ExperimentMode.Exam)
_scoreDisplay?.ShowExamScore();
else
_scoreDisplay?.ShowNotesOnly();
}
#endregion
#region 按钮回调
private void ToggleFullScreen()
{
Screen.fullScreen = !Screen.fullScreen;
Debug.Log($"UIManager: [ToggleFullScreen] 全屏 = {Screen.fullScreen}");
}
private void OnPreviousClicked()
{
Debug.Log("UIManager: [OnPreviousClicked] -> 上一步");
_stepManager?.PreviousStep();
}
private void OnNextClicked()
{
Debug.Log("UIManager: [OnNextClicked] -> 下一步");
if (_stepManager != null && _stepManager.Mode == ExperimentMode.Exam)
{
var step = _stepManager.GetCurrentStep();
if (step != null && step.CompleteType != StepCompleteType.MultipleChoice)
_stepManager.OnStepInteractionComplete(1f);
}
_stepManager?.NextStep();
}
private void OnSwitchModeClicked()
{
if (_stepManager == null) return;
var oldMode = _stepManager.Mode;
var newMode = oldMode == ExperimentMode.Learn ? ExperimentMode.Exam : ExperimentMode.Learn;
Debug.Log($"UIManager: [OnSwitchModeClicked] 切换 {oldMode} -> {newMode}");
_stepManager.SwitchMode(newMode);
}
private void OnReturnClicked()
{
Debug.Log("UIManager: [OnReturnClicked] -> 返回首页");
var pageCtrl = FindObjectOfType<PageController>();
if (pageCtrl != null)
pageCtrl.ReturnToHome();
else
Debug.LogWarning("UIManager: 未找到 PageController。");
}
#endregion
}