Improve exam flow and WebGL build

This commit is contained in:
yangbear
2026-07-17 05:42:52 +08:00
parent 9b4690d396
commit bd76dfd9ec
34 changed files with 89955 additions and 8244 deletions
+146 -54
View File
@@ -52,6 +52,10 @@ public class PageController : MonoBehaviour
// 一开始就隐藏鞋子,以免在主页或开场动画时穿帮
HideShose();
var splintFlow = EnsureSplintPracticeFlow();
if (splintFlow != null) splintFlow.StopPractice();
var initialExamFlow = EnsureExamFlow();
if (initialExamFlow != null) initialExamFlow.StopExam();
// 关掉多余AudioListener(只留Main Camera的)
var cam2 = GameObject.Find("camera2"); if (cam2 != null) { var al = cam2.GetComponent<AudioListener>(); if (al != null) al.enabled = false; }
@@ -147,7 +151,7 @@ public class PageController : MonoBehaviour
var audioManager = FindObjectOfType<AudioManager>();
if (audioManager != null)
{
if (page == Page.Experiment) audioManager.PlayExperimentBg();
if (page == Page.Experiment) audioManager.StopBgMusic();
else audioManager.PlayHomeBg();
}
@@ -211,6 +215,7 @@ public class PageController : MonoBehaviour
if (_uiManager != null)
{
_uiManager.gameObject.SetActive(true);
_uiManager.HideTip();
}
var stepManager = FindObjectOfType<StepManager>();
@@ -218,16 +223,22 @@ public class PageController : MonoBehaviour
{
stepManager.InitializeFromGameManager();
}
PrepareInitialInteractionObjects();
StartCoroutine(StartInitialVoiceAndCameraRoutine());
});
}
else
{
ShowPage(Page.Experiment);
if (_uiManager != null) _uiManager.gameObject.SetActive(true);
if (_uiManager != null)
{
_uiManager.gameObject.SetActive(true);
_uiManager.HideTip();
}
var stepManager = FindObjectOfType<StepManager>();
if (stepManager != null) stepManager.InitializeFromGameManager();
PrepareInitialInteractionObjects();
StartCoroutine(StartInitialVoiceAndCameraRoutine());
}
}
@@ -236,9 +247,8 @@ public class PageController : MonoBehaviour
{
if (GameManager.Instance == null) return;
if (GameManager.Instance.CurrentMode != ExperimentMode.Exam) return;
if (GameManager.Instance.CurrentFixationMethod != FixationMethod.LimbFixation) return;
var examFlow = FindObjectOfType<ExamFlowManager>();
var examFlow = EnsureExamFlow();
if (examFlow != null) examFlow.BeginExam();
}
@@ -247,6 +257,31 @@ public class PageController : MonoBehaviour
ReturnToHome();
}
public void RestartExperimentWithMode(ExperimentMode mode)
{
FixationMethod fixation = GameManager.Instance != null
? GameManager.Instance.CurrentFixationMethod
: FixationMethod.LimbFixation;
Debug.Log($"PageController: 切换到 {mode},并从开场动画重新开始");
ReturnToHome();
StartCoroutine(RestartExperimentAfterReset(mode, fixation));
}
private System.Collections.IEnumerator RestartExperimentAfterReset(ExperimentMode mode, FixationMethod fixation)
{
yield return null;
if (GameManager.Instance != null)
{
GameManager.Instance.CurrentMode = mode;
GameManager.Instance.CurrentExperimentType = ExperimentType.FractureFixation;
GameManager.Instance.CurrentFixationMethod = fixation;
}
OnFixationSelected(fixation);
}
public void ReturnToHome()
{
if (_lastReturnHomeFrame == Time.frameCount)
@@ -261,6 +296,11 @@ public class PageController : MonoBehaviour
StopAllCoroutines();
ActionHistory.Clear();
var splintFlow = EnsureSplintPracticeFlow();
if (splintFlow != null) splintFlow.StopPractice();
var examFlow = EnsureExamFlow();
if (examFlow != null) examFlow.StopExam();
if (_uiManager != null)
{
_uiManager.HideVideo();
@@ -272,6 +312,7 @@ public class PageController : MonoBehaviour
HideCanvasPanel("DialogueBubble");
HideCanvasPanel("GradeResult");
HideCanvasPanel("TipPanel");
HideCanvasPanel("DetailPanel");
// --- 核心修复:退出时彻底清理并隐藏所有交互物体 ---
foreach (var handler in GetSceneObjectsOfType<PingClickHandler>())
@@ -399,20 +440,74 @@ public class PageController : MonoBehaviour
{
float waitTime = 1f; // 默认等1秒
var am = FindObjectOfType<AudioManager>();
// 隐藏视频面板
var allT = Resources.FindObjectsOfTypeAll<Transform>();
foreach (var t in allT)
if (am != null) am.PlayExperimentBg();
// 1. 强制播放第一句语音
if (am != null)
{
if (t.name == "VideoPanel" && t.parent != null && t.parent.name == "Canvas")
#if UNITY_EDITOR
var autoClip = UnityEditor.AssetDatabase.LoadAssetAtPath<AudioClip>("Assets/Audio/people/周围环境安全,我是救护志愿者,我可以帮助您吗.mp3");
if (autoClip != null)
{
t.gameObject.SetActive(false);
am.PlayVoice(autoClip);
waitTime = autoClip.length + 1f;
}
#endif
}
// 找鞋子
Debug.Log($"PageController: 开始等待 {waitTime} 秒以切换摄像机并激活鞋子");
yield return new WaitForSeconds(waitTime);
if (am != null) am.SwitchToCamera3();
var point2 = GameObject.Find("touchArray/point2");
if (point2 == null)
{
var touchArray = GameObject.Find("touchArray");
if (touchArray != null)
{
var p2 = touchArray.transform.Find("point2");
if (p2 != null) point2 = p2.gameObject;
}
}
if (point2 != null)
{
point2.SetActive(true);
Debug.Log("PageController: 镜头切换到camera3point2 已显示");
}
if (_uiManager != null)
{
_uiManager.ShowTip("请点击伤员大腿附近,评估伤情并确认是否疑似骨折。");
}
StartExamFlowIfNeeded();
}
private void PrepareInitialInteractionObjects()
{
bool isSplintPractice = GameManager.Instance != null &&
GameManager.Instance.CurrentMode == ExperimentMode.Learn &&
GameManager.Instance.CurrentFixationMethod == FixationMethod.SplintFixation;
bool isSplintExam = GameManager.Instance != null &&
GameManager.Instance.CurrentMode == ExperimentMode.Exam &&
GameManager.Instance.CurrentFixationMethod == FixationMethod.SplintFixation;
var splintFlow = EnsureSplintPracticeFlow();
if (isSplintPractice && splintFlow != null)
{
splintFlow.BeginPractice();
}
else if (splintFlow != null)
{
splintFlow.StopPractice();
}
HideCanvasPanel("VideoPanel");
var shose = FindSceneObject("Shose");
var point2 = GameObject.Find("touchArray/point2");
if (point2 == null)
{
@@ -429,61 +524,38 @@ public class PageController : MonoBehaviour
var h = shose.GetComponent<ShoseClickHandler>();
if (h == null) h = shose.AddComponent<ShoseClickHandler>();
h.targetPoint = GameObject.Find("ShosePoint")?.transform;
var ping004 = FindFirstPingStepObject();
h.nextObject = ping004;
// 鞋子出现,但默认不开组件(不可点击)
if (isSplintPractice && splintFlow != null && splintFlow.ConfigureShose(h))
{
Debug.Log("PageController: 夹板固定练习 Shose 已绑定到 manMuban/ping.001");
}
else if (isSplintExam)
{
h.nextObject = null;
Debug.Log("PageController: 夹板固定考核 Shose 不直接激活肢体固定 ping");
}
else
{
h.nextObject = ping004;
}
h.enabled = false;
shose.SetActive(true);
Debug.Log("PageController: Shose 已在开场完成后立即显示");
}
if (point2 != null)
{
var oldHandler = point2.GetComponent<TouchPositionHandler>();
if (oldHandler != null) Destroy(oldHandler);
var h2 = point2.GetComponent<Point2ClickHandler>();
if (h2 == null) h2 = point2.AddComponent<Point2ClickHandler>();
if (h2.nextObject == null) h2.nextObject = shose;
point2.SetActive(false);
}
// 1. 强制播放第一句语音
if (am != null)
{
#if UNITY_EDITOR
var autoClip = UnityEditor.AssetDatabase.LoadAssetAtPath<AudioClip>("Assets/Audio/people/周围环境安全,我是救护志愿者,我可以帮助您吗.mp3");
if (autoClip != null)
{
am.PlayVoice(autoClip);
waitTime = autoClip.length + 1f;
}
#endif
}
Debug.Log($"PageController: 开始等待 {waitTime} 秒以切换摄像机并激活鞋子");
var tipPanel = GameObject.Find("TipPanel");
if (tipPanel == null)
{
foreach(var t in allT) { if (t.name == "TipPanel") { tipPanel = t.gameObject; break; } }
}
if (tipPanel != null) tipPanel.SetActive(true);
yield return new WaitForSeconds(waitTime);
if (tipPanel != null) tipPanel.SetActive(false);
if (am != null) am.SwitchToCamera3();
if (point2 != null)
{
point2.SetActive(true);
Debug.Log("PageController: 镜头切换到camera3point2 已显示");
}
StartExamFlowIfNeeded();
}
private void HideShose()
@@ -495,6 +567,26 @@ public class PageController : MonoBehaviour
}
}
private SplintPracticeFlowManager EnsureSplintPracticeFlow()
{
var flow = FindObjectOfType<SplintPracticeFlowManager>(true);
if (flow != null) return flow;
var managers = GameObject.Find("[Managers]");
if (managers == null) managers = gameObject;
return managers.AddComponent<SplintPracticeFlowManager>();
}
private ExamFlowManager EnsureExamFlow()
{
var flow = FindObjectOfType<ExamFlowManager>(true);
if (flow != null) return flow;
var managers = GameObject.Find("[Managers]");
if (managers == null) managers = gameObject;
return managers.AddComponent<ExamFlowManager>();
}
private GameObject FindFirstPingStepObject()
{
foreach (var ping in GetSceneObjectsOfType<PingClickHandler>())
+119 -5
View File
@@ -74,6 +74,17 @@ public class UIManager : MonoBehaviour
var t = canvas.Find("EndPanel");
if (t != null) _endPanel = t.gameObject;
}
if (_tipPanel == null)
{
var t = canvas.Find("TipPanel");
if (t != null) _tipPanel = t.gameObject;
}
if (_tipPanel != null && _tipText == null)
{
_tipText = _tipPanel.GetComponentInChildren<Text>(true);
}
}
private void BindNavbarButtons()
@@ -94,6 +105,20 @@ public class UIManager : MonoBehaviour
var btnReturn = navbar.Find("BtnBack");
if (btnReturn != null) { var btn = btnReturn.GetComponent<Button>(); if (btn != null) { btn.onClick.RemoveListener(OnReturnClicked); btn.onClick.AddListener(OnReturnClicked); } }
var btnPracticeMode = navbar.Find("PracticeMode");
if (btnPracticeMode != null)
{
var btn = btnPracticeMode.GetComponent<Button>();
if (btn != null)
{
btn.onClick.RemoveListener(OnSwitchModeClicked);
btn.onClick.AddListener(OnSwitchModeClicked);
}
}
bool isExamMode = GameManager.Instance != null && GameManager.Instance.CurrentMode == ExperimentMode.Exam;
SetNavbarModeVisual(isExamMode);
}
private void BindButtons()
@@ -121,7 +146,26 @@ public class UIManager : MonoBehaviour
private void UpdateModeUI(bool isExamMode)
{
if (_btnSwitchModeLabel != null)
_btnSwitchModeLabel.text = isExamMode ? "切换到:练习模式" : "切换到:考核模式";
_btnSwitchModeLabel.text = isExamMode ? "练习模式" : "考核模式";
SetNavbarModeVisual(isExamMode);
UpdateFullScreenButtonVisual();
}
private void SetNavbarModeVisual(bool isExamMode)
{
var experimentPanel = GameObject.Find("ExperimentPanel");
if (experimentPanel == null) return;
var modeButton = experimentPanel.transform.Find("Navbar/PracticeMode");
if (modeButton == null) return;
var practice = modeButton.Find("Practice");
var examine = modeButton.Find("Examine");
if (practice != null) practice.gameObject.SetActive(isExamMode);
if (examine != null) examine.gameObject.SetActive(!isExamMode);
SetChildTexts(practice, "练习模式");
SetChildTexts(examine, "考核模式");
}
private void OnStepChanged(int stepIndex)
@@ -132,10 +176,21 @@ public class UIManager : MonoBehaviour
private void UpdateTipContent(string text)
{
AutoFindPanels();
if (_tipPanel != null) _tipPanel.SetActive(!string.IsNullOrEmpty(text));
if (_tipText != null) _tipText.text = text;
}
public void ShowTip(string text)
{
UpdateTipContent(text);
}
public void HideTip()
{
UpdateTipContent("");
}
private void UpdateDialogueContent(string text)
{
if (_dialogueBubble == null)
@@ -164,10 +219,24 @@ public class UIManager : MonoBehaviour
private void UpdateDetailContent(string text)
{
if (IsSplintPracticeMode())
{
if (_detailPanel != null) _detailPanel.SetActive(false);
if (_detailText != null) _detailText.text = "";
return;
}
if (_detailPanel != null) _detailPanel.SetActive(!string.IsNullOrEmpty(text));
if (_detailText != null) _detailText.text = text;
}
private bool IsSplintPracticeMode()
{
return GameManager.Instance != null &&
GameManager.Instance.CurrentMode == ExperimentMode.Learn &&
GameManager.Instance.CurrentFixationMethod == FixationMethod.SplintFixation;
}
private void ToggleVideo(string fileName, bool active)
{
if (active && !string.IsNullOrEmpty(fileName)) ShowVideo(fileName);
@@ -314,10 +383,43 @@ public class UIManager : MonoBehaviour
ShowEndPanel();
}
private void ToggleFullScreen() { Screen.fullScreen = !Screen.fullScreen; }
private void ToggleFullScreen()
{
bool targetFullScreen = !Screen.fullScreen;
Screen.fullScreenMode = FullScreenMode.FullScreenWindow;
Screen.SetResolution(1920, 1080, targetFullScreen);
Screen.fullScreen = targetFullScreen;
UpdateFullScreenButtonVisual();
}
private void UpdateFullScreenButtonVisual()
{
var experimentPanel = GameObject.Find("ExperimentPanel");
Transform fullScreenButton = experimentPanel != null ? experimentPanel.transform.Find("Navbar/FullScreen") : null;
if (fullScreenButton == null) return;
Text label = fullScreenButton.GetComponentInChildren<Text>(true);
if (label != null) label.text = Screen.fullScreen ? "退出全屏" : "全屏模式";
}
private void SetChildTexts(Transform root, string value)
{
if (root == null) return;
Text[] texts = root.GetComponentsInChildren<Text>(true);
for (int i = 0; i < texts.Length; i++)
{
if (texts[i] != null) texts[i].text = value;
}
}
private void OnNextClicked()
{
if (IsSplintPracticeMode())
{
return;
}
if (_stepManager == null) _stepManager = FindObjectOfType<StepManager>();
if (_stepManager != null && _stepManager.Mode == ExperimentMode.Exam)
@@ -337,10 +439,22 @@ public class UIManager : MonoBehaviour
private void OnSwitchModeClicked()
{
if (_stepManager == null) return;
var oldMode = _stepManager.Mode;
if (_stepManager == null) _stepManager = FindObjectOfType<StepManager>();
ExperimentMode oldMode = GameManager.Instance != null ? GameManager.Instance.CurrentMode :
(_stepManager != null ? _stepManager.Mode : ExperimentMode.Learn);
var newMode = oldMode == ExperimentMode.Learn ? ExperimentMode.Exam : ExperimentMode.Learn;
_stepManager.SwitchMode(newMode);
SetNavbarModeVisual(newMode == ExperimentMode.Exam);
var pageCtrl = FindObjectOfType<PageController>();
if (pageCtrl != null)
{
pageCtrl.RestartExperimentWithMode(newMode);
return;
}
if (GameManager.Instance != null) GameManager.Instance.CurrentMode = newMode;
if (_stepManager != null) _stepManager.SwitchMode(newMode);
}
private void OnReturnClicked()