Implement limb fixation exam flow

This commit is contained in:
yangbear
2026-07-12 20:34:49 +08:00
parent 1d60eebb4f
commit 79e1fbb70b
71 changed files with 65272 additions and 2574 deletions
+688
View File
@@ -0,0 +1,688 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ExamFlowManager : MonoBehaviour
{
private const int StepCount = 7;
private readonly int[] _stepScores = { 6, 22, 11, 11, 11, 22, 17 };
private readonly bool[] _answered = new bool[StepCount];
private readonly bool[] _correct = new bool[StepCount];
private readonly int[] _earned = new int[StepCount];
private readonly string[] _feedback = new string[StepCount];
private readonly HashSet<string> _placedPings = new HashSet<string>();
private readonly HashSet<string> _wrappedPings = new HashSet<string>();
private Transform _dialogueBubble;
private GameObject _gradeResult;
private GameObject _endPanel;
private Button _dialogueYesButton;
private Button _dialogueBackButton;
private Text _gradeContent;
private Text _gradeScoreText;
private int _currentStep;
private bool _examActive;
private bool _waitingForPoint2;
private bool _waitingForShose;
private bool _waitingForWrapClicks;
private void Awake()
{
AutoBind();
}
private void Start()
{
AutoBind();
BindButtons();
EnsureToggleGroups();
HideAllPanels();
}
private void Update()
{
bool shouldRun = GameManager.Instance != null &&
GameManager.Instance.CurrentMode == ExperimentMode.Exam &&
GameManager.Instance.CurrentFixationMethod == FixationMethod.LimbFixation &&
IsExperimentPageVisible();
if (!shouldRun && _examActive)
{
ResetExamState(false);
}
}
public void BeginExam()
{
if (GameManager.Instance == null ||
GameManager.Instance.CurrentMode != ExperimentMode.Exam ||
GameManager.Instance.CurrentFixationMethod != FixationMethod.LimbFixation ||
!IsExperimentPageVisible())
{
ResetExamState(false);
return;
}
AutoBind();
BindButtons();
EnsureToggleGroups();
ResetExamState(true);
_examActive = true;
_waitingForPoint2 = true;
if (GameManager.Instance != null)
{
GameManager.Instance.InitExamRecord(StepCount);
}
ForceHideExamOnlyOutlines();
MoveWomanToExamPoint();
ShowStep(1);
Debug.Log("[ExamFlow] 考核模式流程启动");
}
public void StopExam()
{
ResetExamState(false);
}
public void ShowStep(int stepNumber)
{
AutoBind();
_currentStep = Mathf.Clamp(stepNumber, 1, StepCount);
if (_dialogueBubble == null) return;
_dialogueBubble.gameObject.SetActive(true);
EnsureStepPanelsDoNotBlockButtons();
for (int i = 0; i < _dialogueBubble.childCount; i++)
{
Transform child = _dialogueBubble.GetChild(i);
if (child.name.StartsWith("step"))
{
child.gameObject.SetActive(child.name == "step" + _currentStep);
}
}
ApplyStepText(_currentStep);
if (_currentStep == 3 || _currentStep == 7)
{
ResetStepToggles(_currentStep);
}
Debug.Log("[ExamFlow] 显示 step" + _currentStep);
}
public void OnObjectClicked(Component clicked)
{
if (!_examActive || clicked == null) return;
if (clicked is Point2ClickHandler)
{
if (_waitingForPoint2 && clicked.name == "point2")
{
_waitingForPoint2 = false;
RecordStep(1, true, "已按要求完成前置操作起始步骤。");
StartCoroutine(ShowStepDelayed(2, 1f));
}
return;
}
if (clicked is ShoseClickHandler)
{
if (_waitingForShose || _currentStep <= 2)
{
_waitingForShose = false;
RecordStep(2, true, "已完成下肢固定前置处理。");
StartCoroutine(ShowStepDelayed(3, 1f));
}
return;
}
var ping = clicked as PingClickHandler;
if (ping != null)
{
bool isWrapClick = ping.chanraoObject != null && ping.chanraoObject.activeSelf;
if (isWrapClick)
{
_wrappedPings.Add(ping.name);
Debug.Log("[ExamFlow] 系带点击 " + _wrappedPings.Count + "/4: " + ping.name);
if (_waitingForWrapClicks && _wrappedPings.Count >= 4)
{
_waitingForWrapClicks = false;
RecordStep(5, true, "已按由远端到近端的顺序完成系带。");
ShowStep(6);
}
}
else
{
_placedPings.Add(ping.name);
Debug.Log("[ExamFlow] 放带点击 " + _placedPings.Count + "/4: " + ping.name);
}
return;
}
if (clicked is ChanraoTuibuHandler)
{
if (!_answered[3])
{
RecordStep(4, _placedPings.Count >= 4, _placedPings.Count >= 4
? "已按要求完成四条绑带摆放。"
: "绑带尚未全部摆放完成。");
}
_waitingForWrapClicks = true;
ShowStep(5);
}
}
private IEnumerator ShowStepDelayed(int stepNumber, float delay)
{
yield return new WaitForSeconds(delay);
ShowStep(stepNumber);
}
private void OnDialogueYesClicked()
{
if (!_examActive)
{
HideDialogue();
return;
}
switch (_currentStep)
{
case 1:
HideDialogue();
_waitingForPoint2 = true;
break;
case 2:
HideDialogue();
_waitingForShose = true;
break;
case 3:
RecordStep(3, GetSelectedToggleIndex(3) == 1, "正确答案:10cm。");
ShowStep(4);
ActivateFirstPing();
break;
case 4:
HideDialogue();
break;
case 5:
HideDialogue();
_waitingForWrapClicks = true;
break;
case 6:
RecordStep(6, false, "错误操作:在受伤腿一侧打结。正确做法是在未受伤腿位置打结。");
ShowStep(7);
break;
case 7:
RecordStep(7, GetSelectedToggleIndex(7) == 1, "正确答案:足背。");
ShowGradeResult();
break;
default:
HideDialogue();
break;
}
}
private void ShowGradeResult()
{
AutoBind();
BindResultButtons();
BindEndPanelButtons();
HideDialogue();
int totalScore;
string gradeContent = BuildGradeContent(out totalScore);
if (_gradeResult != null) _gradeResult.SetActive(true);
if (_gradeContent != null) _gradeContent.text = gradeContent;
if (_gradeScoreText != null) _gradeScoreText.text = totalScore.ToString();
}
private string BuildGradeContent(out int total)
{
total = 0;
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.AppendLine("考核结果汇总");
sb.AppendLine();
for (int i = 0; i < StepCount; i++)
{
total += _earned[i];
bool answered = _answered[i];
bool correct = answered && _correct[i];
sb.AppendFormat("考核点{0}{1}分):{2}", i + 1, _stepScores[i], correct ? "正确" : "错误");
if (!answered) sb.Append("(未完成)");
sb.AppendLine();
sb.AppendLine("题目:" + GetQuestionText(i + 1));
sb.AppendLine("答案:" + GetAnswerText(i + 1));
sb.AppendFormat("得分:{0}/{1}", _earned[i], _stepScores[i]);
sb.AppendLine();
if (!string.IsNullOrEmpty(_feedback[i]) && (!correct || i == 5))
{
sb.AppendLine("说明:" + _feedback[i]);
}
sb.AppendLine();
}
sb.AppendFormat("最终得分:{0}/100", total);
return sb.ToString();
}
private void RecordStep(int oneBasedStep, bool isCorrect, string feedback)
{
int index = oneBasedStep - 1;
if (index < 0 || index >= StepCount || _answered[index]) return;
_answered[index] = true;
_correct[index] = isCorrect;
_earned[index] = isCorrect ? _stepScores[index] : 0;
_feedback[index] = feedback;
if (GameManager.Instance != null)
{
GameManager.Instance.RecordExamResult(index, _stepScores[index], isCorrect ? 1f : 0f, feedback);
}
}
private void ActivateFirstPing()
{
GameObject firstPing = FindSceneObject("ping.004");
if (firstPing == null)
{
foreach (var ping in Resources.FindObjectsOfTypeAll<PingClickHandler>())
{
if (ping != null && ping.gameObject.scene.IsValid() && ping.pingIndex == 0)
{
firstPing = ping.gameObject;
break;
}
}
}
if (firstPing != null)
{
firstPing.SetActive(true);
var handler = firstPing.GetComponent<PingClickHandler>();
if (handler != null) handler.ResetState(true);
}
}
private void AutoBind()
{
Transform canvas = FindSceneObject("Canvas")?.transform;
if (canvas == null) return;
if (_dialogueBubble == null) _dialogueBubble = canvas.Find("DialogueBubble");
if (_gradeResult == null)
{
Transform t = canvas.Find("GradeResult");
if (t != null) _gradeResult = t.gameObject;
}
if (_endPanel == null)
{
Transform t = canvas.Find("EndPanel");
if (t != null) _endPanel = t.gameObject;
}
if (_dialogueBubble != null)
{
_dialogueYesButton = _dialogueBubble.Find("BtnYes")?.GetComponent<Button>();
_dialogueBackButton = _dialogueBubble.Find("BtnBack")?.GetComponent<Button>();
}
if (_gradeResult != null)
{
Transform grade = _gradeResult.transform.Find("Grade");
if (grade != null) _gradeScoreText = grade.GetComponent<Text>();
_gradeContent = FindGradeDetailText(_gradeResult.transform);
}
}
private Text FindGradeDetailText(Transform root)
{
if (root == null) return null;
Transform scrollView = root.Find("Scroll View");
if (scrollView != null)
{
Transform viewportContent = scrollView.Find("Viewport/Content");
Text text = EnsureTextOnContent(viewportContent);
if (text != null) return text;
text = FindTextByName(scrollView, "Content");
if (text != null) return text;
text = FindTextByName(scrollView, "content");
if (text != null) return text;
}
Transform legacyContent = root.Find("content");
return legacyContent != null ? legacyContent.GetComponent<Text>() : null;
}
private Text EnsureTextOnContent(Transform content)
{
if (content == null) return null;
Text text = content.GetComponent<Text>();
if (text == null)
{
text = content.GetComponentInChildren<Text>(true);
}
if (text == null)
{
text = content.gameObject.AddComponent<Text>();
text.font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
text.fontSize = 24;
text.color = Color.black;
text.alignment = TextAnchor.UpperLeft;
text.horizontalOverflow = HorizontalWrapMode.Wrap;
text.verticalOverflow = VerticalWrapMode.Overflow;
}
return text;
}
private Text FindTextByName(Transform root, string objectName)
{
if (root == null) return null;
Text direct = root.name == objectName ? root.GetComponent<Text>() : null;
if (direct != null) return direct;
for (int i = 0; i < root.childCount; i++)
{
Text childText = FindTextByName(root.GetChild(i), objectName);
if (childText != null) return childText;
}
return null;
}
private void BindButtons()
{
if (_dialogueYesButton != null)
{
_dialogueYesButton.onClick.RemoveAllListeners();
_dialogueYesButton.interactable = true;
_dialogueYesButton.enabled = true;
_dialogueYesButton.onClick.AddListener(OnDialogueYesClicked);
}
if (_dialogueBackButton != null)
{
_dialogueBackButton.onClick.RemoveAllListeners();
_dialogueBackButton.interactable = true;
_dialogueBackButton.enabled = true;
_dialogueBackButton.onClick.AddListener(HideDialogue);
}
BindResultButtons();
BindEndPanelButtons();
}
private void BindResultButtons()
{
if (_gradeResult == null) return;
Button yes = _gradeResult.transform.Find("BtnYes")?.GetComponent<Button>();
if (yes != null)
{
yes.onClick.RemoveAllListeners();
yes.onClick.AddListener(() =>
{
_gradeResult.SetActive(false);
if (_endPanel != null) _endPanel.SetActive(true);
});
}
Button back = _gradeResult.transform.Find("BtnBack")?.GetComponent<Button>();
if (back != null)
{
back.onClick.RemoveAllListeners();
back.onClick.AddListener(() => _gradeResult.SetActive(false));
}
}
private void BindEndPanelButtons()
{
if (_endPanel == null) return;
Button yes = _endPanel.transform.Find("BtnYes")?.GetComponent<Button>();
if (yes != null)
{
yes.onClick.RemoveAllListeners();
yes.onClick.AddListener(ReturnToHome);
}
}
private void ReturnToHome()
{
PageController page = FindObjectOfType<PageController>();
if (page != null) page.ReturnToHome();
}
private void ApplyStepText(int stepNumber)
{
Transform step = _dialogueBubble?.Find("step" + stepNumber);
if (step == null) return;
Text title = step.Find("Title")?.GetComponent<Text>();
Text content = step.Find("content")?.GetComponent<Text>();
if (title != null) title.text = "考核点" + ToChineseNumber(stepNumber);
if (content != null) content.text = GetQuestionText(stepNumber);
if (stepNumber == 3)
{
SetToggleLabel(step, "Toggle1", "5cm");
SetToggleLabel(step, "Toggle2", "10cm");
SetToggleLabel(step, "Toggle3", "20cm");
}
else if (stepNumber == 7)
{
SetToggleLabel(step, "Toggle1", "足底");
SetToggleLabel(step, "Toggle2", "足背");
}
}
private string GetQuestionText(int stepNumber)
{
switch (stepNumber)
{
case 1: return "点击确认后,请按正确顺序进行操作。";
case 2: return "点击确认后,请按正确顺序操作下肢固定前置处理。";
case 3: return "请选择合适的带子宽度。";
case 4: return "请选择正确的放带子顺序。";
case 5: return "请选择正确的系带子顺序。";
case 6: return "请选择带子打结位置。";
case 7: return "请选择踝关节打结位置。";
default: return "";
}
}
private string GetAnswerText(int stepNumber)
{
switch (stepNumber)
{
case 1: return "先评估伤员、取出急救用品、脱除伤侧鞋子。";
case 2: return "摆放夹板衬垫、摆正伤肢、保护踝关节。";
case 3: return "10cm。";
case 4: return "由远心端(脚踝)向近心端(大腿根部)依次摆放绑带。";
case 5: return "先固定远端脚踝,再依次向大腿近端收紧捆绑。";
case 6: return "应在未受伤腿的位置打结,不压迫伤肢,方便后续拆除。";
case 7: return "足背。";
default: return "";
}
}
private string ToChineseNumber(int number)
{
string[] values = { "", "一", "二", "三", "四", "五", "六", "七" };
return number >= 0 && number < values.Length ? values[number] : number.ToString();
}
private void SetToggleLabel(Transform root, string toggleName, string label)
{
Transform toggle = root.Find(toggleName);
Text text = toggle?.Find("Label")?.GetComponent<Text>();
if (text != null) text.text = label;
}
private int GetSelectedToggleIndex(int stepNumber)
{
Transform step = _dialogueBubble?.Find("step" + stepNumber);
if (step == null) return -1;
Toggle[] toggles = step.GetComponentsInChildren<Toggle>(true);
for (int i = 0; i < toggles.Length; i++)
{
if (toggles[i].isOn) return i;
}
return -1;
}
private void ResetStepToggles(int stepNumber)
{
Transform step = _dialogueBubble?.Find("step" + stepNumber);
if (step == null) return;
Toggle[] toggles = step.GetComponentsInChildren<Toggle>(true);
for (int i = 0; i < toggles.Length; i++)
{
toggles[i].isOn = false;
}
}
private void EnsureToggleGroups()
{
EnsureToggleGroup(3);
EnsureToggleGroup(7);
}
private void EnsureStepPanelsDoNotBlockButtons()
{
if (_dialogueBubble == null) return;
for (int i = 0; i < _dialogueBubble.childCount; i++)
{
Transform child = _dialogueBubble.GetChild(i);
if (!child.name.StartsWith("step")) continue;
Image image = child.GetComponent<Image>();
if (image != null) image.raycastTarget = false;
}
}
private void EnsureToggleGroup(int stepNumber)
{
Transform step = _dialogueBubble?.Find("step" + stepNumber);
if (step == null) return;
ToggleGroup group = step.GetComponent<ToggleGroup>();
if (group == null) group = step.gameObject.AddComponent<ToggleGroup>();
group.allowSwitchOff = true;
Toggle[] toggles = step.GetComponentsInChildren<Toggle>(true);
for (int i = 0; i < toggles.Length; i++)
{
toggles[i].group = group;
}
}
private void HideDialogue()
{
if (_dialogueBubble != null) _dialogueBubble.gameObject.SetActive(false);
}
private void HideAllPanels()
{
HideDialogue();
if (_gradeResult != null) _gradeResult.SetActive(false);
if (_endPanel != null) _endPanel.SetActive(false);
}
private void ResetExamState(bool keepActive)
{
StopAllCoroutines();
_examActive = keepActive;
_currentStep = 0;
_waitingForPoint2 = false;
_waitingForShose = false;
_waitingForWrapClicks = false;
_placedPings.Clear();
_wrappedPings.Clear();
for (int i = 0; i < StepCount; i++)
{
_answered[i] = false;
_correct[i] = false;
_earned[i] = 0;
_feedback[i] = "";
}
HideAllPanels();
}
private void ForceHideExamOnlyOutlines()
{
ShoseClickHandler shose = FindObjectOfType<ShoseClickHandler>(true);
if (shose != null) shose.ForceHideOutline();
ChanraoTuibuHandler chanraoTuibu = FindObjectOfType<ChanraoTuibuHandler>(true);
if (chanraoTuibu != null) chanraoTuibu.ForceHideOutline();
}
private void MoveWomanToExamPoint()
{
GameObject woman = FindSceneObject("woman");
GameObject pointArray = FindSceneObject("womanPointArray");
Transform target = pointArray != null ? pointArray.transform.Find("point1") : null;
if (woman == null || target == null) return;
woman.transform.position = target.position;
woman.transform.rotation = target.rotation;
Animator anim = woman.GetComponent<Animator>();
if (anim != null)
{
anim.SetBool("IsCheck", false);
anim.SetBool("isCrouch", false);
}
}
private bool IsExperimentPageVisible()
{
GameObject panel = FindSceneObject("ExperimentPanel");
return panel == null || panel.activeInHierarchy;
}
private GameObject FindSceneObject(string objectName)
{
GameObject found = GameObject.Find(objectName);
if (found != null) return found;
Transform[] all = Resources.FindObjectsOfTypeAll<Transform>();
for (int i = 0; i < all.Length; i++)
{
Transform t = all[i];
if (t != null && t.gameObject.scene.IsValid() && t.name == objectName)
{
return t.gameObject;
}
}
return null;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 84a33ccf7ef244c58818ebbc9ac716ae
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bb952fba68f6e41e8982a839745b41be
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -46,7 +46,8 @@ public class ChanraoTuibuHandler : MonoBehaviour
CreateOutlineMesh();
_isFinished = false;
StopAllCoroutines();
StartCoroutine(BlinkOutline());
bool isExam = GameManager.Instance != null && GameManager.Instance.CurrentMode == ExperimentMode.Exam;
if (!isExam) { StartCoroutine(BlinkOutline()); }
Debug.Log($"[ChanraoTuibu] OnEnable outline={_outlineMesh!=null}");
}
@@ -101,6 +102,7 @@ public class ChanraoTuibuHandler : MonoBehaviour
Debug.Log($"[ChanraoTuibu] {name} 被点击");
ActionHistory.Push(ActionHistory.ActionType.ChanraoTuibu, this);
var examMgr = FindObjectOfType<ExamFlowManager>(); if (examMgr != null) examMgr.OnObjectClicked(this);
_isFinished = true;
StopAllCoroutines();
if (_outlineMesh != null) _outlineMesh.enabled = false;
@@ -130,10 +132,15 @@ public class ChanraoTuibuHandler : MonoBehaviour
var mr = GetComponent<MeshRenderer>(); if (mr != null) mr.enabled = false;
var smr = GetComponent<SkinnedMeshRenderer>(); if (smr != null) smr.enabled = false;
foreach (var r in GetComponentsInChildren<Renderer>(true)) { if (_outlineMesh != null && r == _outlineMesh) continue; r.enabled = false; }
if (_outlineMesh != null) _outlineMesh.enabled = startBlink;
if (_outlineMesh != null)
{
bool isExam = GameManager.Instance != null && GameManager.Instance.CurrentMode == ExperimentMode.Exam;
_outlineMesh.enabled = startBlink && !isExam;
}
StopAllCoroutines();
if (startBlink && isActiveAndEnabled)
bool examMode = GameManager.Instance != null && GameManager.Instance.CurrentMode == ExperimentMode.Exam;
if (startBlink && isActiveAndEnabled && !examMode)
{
StartCoroutine(BlinkOutline());
}
@@ -171,12 +171,16 @@ public class PingClickHandler : MonoBehaviour
var sm = FindObjectOfType<StepManager>();
if (sm != null) sm.RecordPingStep(pingIndex, false);
var examMgr = FindObjectOfType<ExamFlowManager>();
if (examMgr != null) examMgr.OnObjectClicked(this);
}
else
{
if (chanraoObject != null) { chanraoObject.SetActive(true); Debug.Log($"[PingClick] {name} → {chanraoObject.name}"); }
HideSelf();
ActionHistory.Push(ActionHistory.ActionType.PingSecond, this);
var examMgr = FindObjectOfType<ExamFlowManager>();
if (examMgr != null) examMgr.OnObjectClicked(this);
var sm = FindObjectOfType<StepManager>();
if (sm != null) sm.RecordPingStep(pingIndex, true);
@@ -188,11 +192,14 @@ public class PingClickHandler : MonoBehaviour
remaining++;
}
bool isExam = GameManager.Instance != null && GameManager.Instance.CurrentMode == ExperimentMode.Exam;
if (remaining == 0)
{
if (!_hasPlayedFinishVoice)
{
_hasPlayedFinishVoice = true;
if (isExam) return;
Debug.Log("[PingClick] 所有的 ping 都已经绑成 chanrao 了!播放完成语音。");
var am = FindObjectOfType<AudioManager>();
if (am != null)
@@ -39,7 +39,8 @@ public class Point2ClickHandler : MonoBehaviour
_clicked = false;
StopAllCoroutines();
StartCoroutine(BlinkOutline());
bool isExam = GameManager.Instance != null && GameManager.Instance.CurrentMode == ExperimentMode.Exam;
if (!isExam) { StartCoroutine(BlinkOutline()); }
}
System.Collections.IEnumerator BlinkOutline()
@@ -64,6 +65,7 @@ public class Point2ClickHandler : MonoBehaviour
Debug.Log("[Point2Click] ★ 被点击 ★");
ActionHistory.Push(ActionHistory.ActionType.Point2, this);
var examMgr = FindObjectOfType<ExamFlowManager>(); if (examMgr != null) examMgr.OnObjectClicked(this);
StopAllCoroutines();
if (_meshRenderer != null) _meshRenderer.enabled = false;
@@ -126,7 +128,8 @@ public class Point2ClickHandler : MonoBehaviour
StopAllCoroutines();
gameObject.SetActive(startBlink);
if (startBlink && isActiveAndEnabled)
bool isExam = GameManager.Instance != null && GameManager.Instance.CurrentMode == ExperimentMode.Exam;
if (startBlink && isActiveAndEnabled && !isExam)
{
StartCoroutine(BlinkOutline());
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3413ba9009ddb4571af3b54a0643b697
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -45,7 +45,8 @@ public class ShoseClickHandler : MonoBehaviour
CreateOutlineMesh();
StopAllCoroutines();
StartCoroutine(BlinkOutline());
bool isExam = GameManager.Instance != null && GameManager.Instance.CurrentMode == ExperimentMode.Exam;
if (!isExam) { StartCoroutine(BlinkOutline()); }
_clicked = false;
}
@@ -88,6 +89,7 @@ public class ShoseClickHandler : MonoBehaviour
_clicked = true;
Debug.Log("[ShoseClick] ★ 被点击 ★");
ActionHistory.Push(ActionHistory.ActionType.Shose, this);
var examMgr = FindObjectOfType<ExamFlowManager>(); if (examMgr != null) examMgr.OnObjectClicked(this);
StopAllCoroutines();
if (_outlineMesh != null) _outlineMesh.enabled = false;
if (targetPoint != null) { transform.position = targetPoint.position; transform.rotation = targetPoint.rotation; }
@@ -149,7 +151,11 @@ public class ShoseClickHandler : MonoBehaviour
{
_clicked = false;
if (_outlineMesh != null) _outlineMesh.enabled = startBlink;
if (_outlineMesh != null)
{
bool isExam = GameManager.Instance != null && GameManager.Instance.CurrentMode == ExperimentMode.Exam;
_outlineMesh.enabled = startBlink && !isExam;
}
if (_hasRecordedInitialTransform)
{
@@ -202,7 +208,8 @@ public class ShoseClickHandler : MonoBehaviour
}
StopAllCoroutines();
if (startBlink && isActiveAndEnabled)
bool examMode = GameManager.Instance != null && GameManager.Instance.CurrentMode == ExperimentMode.Exam;
if (startBlink && isActiveAndEnabled && !examMode)
{
StartCoroutine(BlinkOutline());
}
+55 -10
View File
@@ -57,40 +57,68 @@ public class PageController : MonoBehaviour
var cam2 = GameObject.Find("camera2"); if (cam2 != null) { var al = cam2.GetComponent<AudioListener>(); if (al != null) al.enabled = false; }
var cam3 = GameObject.Find("camera3"); if (cam3 != null) { var al = cam3.GetComponent<AudioListener>(); if (al != null) al.enabled = false; }
AutoAssignButtonsByName();
// 绑定首页按钮
if (_btnStart != null) { _btnStart.onClick.AddListener(OnStartClicked); Debug.Log("PageController: _btnStart 绑定成功"); }
if (_btnStart != null) { _btnStart.onClick.RemoveAllListeners(); _btnStart.onClick.AddListener(OnStartClicked); Debug.Log("PageController: _btnStart 绑定成功"); }
else Debug.LogError("PageController: _btnStart 未赋值!");
if (_btnExit != null) { _btnExit.onClick.AddListener(OnExitClicked); Debug.Log("PageController: _btnExit 绑定成功"); }
if (_btnExit != null) { _btnExit.onClick.RemoveAllListeners(); _btnExit.onClick.AddListener(OnExitClicked); Debug.Log("PageController: _btnExit 绑定成功"); }
else Debug.LogError("PageController: _btnExit 未赋值!");
if (_btnBack != null) { _btnBack.onClick.AddListener(OnModeBackClicked); Debug.Log("PageController: _btnBack 绑定成功"); }
if (_btnBack != null) { _btnBack.onClick.RemoveAllListeners(); _btnBack.onClick.AddListener(OnModeBackClicked); Debug.Log("PageController: _btnBack 绑定成功"); }
else Debug.LogError("PageController: _btnBack 未赋值!");
if (_btnPracticeMode != null) { _btnPracticeMode.onClick.AddListener(() => OnModeSelected(ExperimentMode.Learn)); Debug.Log("PageController: _btnPracticeMode 绑定成功"); }
if (_btnPracticeMode != null) { _btnPracticeMode.onClick.RemoveAllListeners(); _btnPracticeMode.onClick.AddListener(() => OnModeSelected(ExperimentMode.Learn)); Debug.Log("PageController: _btnPracticeMode 绑定成功"); }
else Debug.LogError("PageController: _btnPracticeMode 未赋值!");
if (_btnExamMode != null) { _btnExamMode.onClick.AddListener(() => OnModeSelected(ExperimentMode.Exam)); Debug.Log("PageController: _btnExamMode 绑定成功"); }
if (_btnExamMode != null) { _btnExamMode.onClick.RemoveAllListeners(); _btnExamMode.onClick.AddListener(() => OnModeSelected(ExperimentMode.Exam)); Debug.Log("PageController: _btnExamMode 绑定成功"); }
else Debug.LogError("PageController: _btnExamMode 未赋值!");
if (_btnFixationBack == null && _fixationPanel != null) { var fb = _fixationPanel.transform.Find("BtnBack"); if (fb != null) { _btnFixationBack = fb.GetComponent<Button>(); Debug.Log("PageController: 自动绑定 FixationPanel/BtnBack"); } }
if (_btnFixationBack != null) { _btnFixationBack.onClick.AddListener(OnFixationBackClicked); Debug.Log("PageController: _btnFixationBack 绑定成功"); }
if (_btnFixationBack != null) { _btnFixationBack.onClick.RemoveAllListeners(); _btnFixationBack.onClick.AddListener(OnFixationBackClicked); Debug.Log("PageController: _btnFixationBack 绑定成功"); }
else Debug.LogError("PageController: _btnFixationBack 未找到!");
if (_btnLimbFixation != null) { _btnLimbFixation.onClick.AddListener(() => OnFixationSelected(FixationMethod.LimbFixation)); Debug.Log("PageController: _btnLimbFixation 绑定成功"); }
if (_btnLimbFixation != null) { _btnLimbFixation.onClick.RemoveAllListeners(); _btnLimbFixation.onClick.AddListener(() => OnFixationSelected(FixationMethod.LimbFixation)); Debug.Log("PageController: _btnLimbFixation 绑定成功"); }
else Debug.LogError("PageController: _btnLimbFixation 未赋值!");
if (_btnSplintFixation != null) { _btnSplintFixation.onClick.AddListener(() => OnFixationSelected(FixationMethod.SplintFixation)); Debug.Log("PageController: _btnSplintFixation 绑定成功"); }
if (_btnSplintFixation != null) { _btnSplintFixation.onClick.RemoveAllListeners(); _btnSplintFixation.onClick.AddListener(() => OnFixationSelected(FixationMethod.SplintFixation)); Debug.Log("PageController: _btnSplintFixation 绑定成功"); }
else Debug.LogError("PageController: _btnSplintFixation 未赋值!");
if (_btnExperimentBack == null && _experimentPanel != null) { var eb = _experimentPanel.transform.Find("Navbar/BtnBack"); if (eb != null) { _btnExperimentBack = eb.GetComponent<Button>(); Debug.Log("PageController: 自动绑定 ExperimentPanel/Navbar/BtnBack"); } }
if (_btnExperimentBack != null) { _btnExperimentBack.onClick.AddListener(OnExperimentBackClicked); Debug.Log("PageController: _btnExperimentBack 绑定成功"); }
if (_btnExperimentBack != null) { _btnExperimentBack.onClick.RemoveAllListeners(); _btnExperimentBack.onClick.AddListener(OnExperimentBackClicked); Debug.Log("PageController: _btnExperimentBack 绑定成功"); }
else Debug.LogError("PageController: _btnExperimentBack 未找到!");
ShowPage(Page.Home);
Debug.Log("=== PageController 初始化完成 ===");
}
private void AutoAssignButtonsByName()
{
_btnStart = FindChildButton(_homePagePanel, "BtnStart", _btnStart);
_btnExit = FindChildButton(_homePagePanel, "BtnExit", _btnExit);
_btnBack = FindChildButton(_modeSelectPanel, "BtnBack", _btnBack);
_btnPracticeMode = FindChildButton(_modeSelectPanel, "BtnPracticeMode", _btnPracticeMode);
_btnExamMode = FindChildButton(_modeSelectPanel, "BtnExamMode", _btnExamMode);
_btnFixationBack = FindChildButton(_fixationPanel, "BtnBack", _btnFixationBack);
_btnLimbFixation = FindChildButton(_fixationPanel, "BtnLimbFixation", _btnLimbFixation);
_btnSplintFixation = FindChildButton(_fixationPanel, "BtnSplintFixation", _btnSplintFixation);
}
private Button FindChildButton(GameObject parent, string buttonName, Button fallback)
{
if (parent == null) return fallback;
var buttons = parent.GetComponentsInChildren<Button>(true);
foreach (var button in buttons)
{
if (button != null && button.name == buttonName)
return button;
}
return fallback;
}
private void Update()
{
#if UNITY_EDITOR
@@ -156,6 +184,12 @@ public class PageController : MonoBehaviour
GameManager.Instance.CurrentMode = mode;
GameManager.Instance.CurrentExperimentType = ExperimentType.FractureFixation;
if (mode == ExperimentMode.Learn)
{
var examFlow = FindObjectOfType<ExamFlowManager>();
if (examFlow != null) examFlow.StopExam();
}
ShowPage(Page.Fixation);
}
@@ -184,7 +218,6 @@ public class PageController : MonoBehaviour
{
stepManager.InitializeFromGameManager();
}
StartCoroutine(StartInitialVoiceAndCameraRoutine());
});
}
@@ -199,6 +232,16 @@ public class PageController : MonoBehaviour
}
}
private void StartExamFlowIfNeeded()
{
if (GameManager.Instance == null) return;
if (GameManager.Instance.CurrentMode != ExperimentMode.Exam) return;
if (GameManager.Instance.CurrentFixationMethod != FixationMethod.LimbFixation) return;
var examFlow = FindObjectOfType<ExamFlowManager>();
if (examFlow != null) examFlow.BeginExam();
}
private void OnExperimentBackClicked()
{
ReturnToHome();
@@ -439,6 +482,8 @@ public class PageController : MonoBehaviour
point2.SetActive(true);
Debug.Log("PageController: 镜头切换到camera3point2 已显示");
}
StartExamFlowIfNeeded();
}
private void HideShose()
+11 -1
View File
@@ -145,7 +145,17 @@ public class UIManager : MonoBehaviour
if (_dialogueBubble != null)
{
_dialogueBubble.SetActive(!string.IsNullOrEmpty(text));
bool hasText = !string.IsNullOrEmpty(text);
bool isExam = GameManager.Instance != null && GameManager.Instance.CurrentMode == ExperimentMode.Exam;
bool isExamBubble = _dialogueBubble.transform.Find("step1") != null;
if (!isExam && isExamBubble)
{
_dialogueBubble.SetActive(false);
return;
}
_dialogueBubble.SetActive(hasText);
var txt = _dialogueBubble.GetComponentInChildren<Text>();
if (txt != null) txt.text = text;
}