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
@@ -0,0 +1,404 @@
using System.Collections.Generic;
using UnityEngine;
public class SplintPracticeFlowManager : MonoBehaviour
{
private const string ManMubanName = "manMuban";
private const string ManInjuryName = "manInjury";
private readonly HashSet<PingClickHandler> _placedPings = new HashSet<PingClickHandler>();
private readonly HashSet<PingClickHandler> _finishedPings = new HashSet<PingClickHandler>();
private readonly HashSet<SplintObjectClickHandler> _clickedPlanks = new HashSet<SplintObjectClickHandler>();
private GameObject _manMuban;
private GameObject _manInjury;
private PingClickHandler[] _pings;
private SplintObjectClickHandler _bodyPad;
private SplintObjectClickHandler[] _planks;
private bool _bodyClicked;
private bool _active;
private Coroutine _finishRoutine;
public bool IsActive => _active && SplintPracticeSelected;
private bool SplintPracticeSelected
{
get
{
return GameManager.Instance != null &&
GameManager.Instance.CurrentMode == ExperimentMode.Learn &&
GameManager.Instance.CurrentFixationMethod == FixationMethod.SplintFixation;
}
}
private void Awake()
{
AutoBind();
}
private void Start()
{
AutoBind();
StopPractice();
}
public void BeginPractice()
{
if (!SplintPracticeSelected) return;
AutoBind();
_active = true;
_placedPings.Clear();
_finishedPings.Clear();
_clickedPlanks.Clear();
_bodyClicked = false;
if (_manInjury != null) _manInjury.SetActive(false);
if (_manMuban != null) _manMuban.SetActive(true);
HideSplintObjects();
ConfigurePings();
ConfigureBodyPad();
ConfigurePlanks();
ShowTip("请先完成伤情评估和脱鞋;脱鞋后按由脚踝到大腿的顺序预置 4 条固定带。");
Debug.Log("[SplintPractice] 夹板固定练习流程已初始化");
}
public void StopPractice()
{
_active = false;
_placedPings.Clear();
_finishedPings.Clear();
_clickedPlanks.Clear();
_bodyClicked = false;
AutoBind();
HideSplintObjects();
if (_manMuban != null) _manMuban.SetActive(false);
}
public bool ConfigureShose(ShoseClickHandler shose)
{
if (shose == null || !IsActive) return false;
AutoBind();
ConfigurePings();
PingClickHandler firstPing = GetPingByIndex(0);
if (firstPing == null) return false;
shose.nextObject = firstPing.gameObject;
return true;
}
public bool OnPingFirstClick(PingClickHandler ping)
{
if (!IsActive || ping == null || !IsSplintPing(ping)) return false;
_placedPings.Add(ping);
Debug.Log($"[SplintPractice] 已放置夹板绑带 {_placedPings.Count}/4: {ping.name}");
if (_placedPings.Count >= 4)
{
ShowBodyPadTarget();
ShowTip("四条固定带已预置。下一步:点击 Body.001 蓝色边框,放置夹板衬垫。");
}
else
{
ShowTip($"已预置固定带 {_placedPings.Count}/4。继续由远端向近端点击下一个蓝色绑带点。");
}
return true;
}
public void OnPlankClicked(SplintObjectClickHandler plank)
{
if (!IsActive || plank == null) return;
if (plank == _bodyPad)
{
_bodyClicked = true;
Debug.Log("[SplintPractice] 已放置夹板衬垫 Body.001");
ShowPlankTargets();
ShowTip("衬垫已放置。下一步:依次点击两块木板蓝色边框,将夹板置于伤肢两侧。");
return;
}
if (!_bodyClicked) return;
_clickedPlanks.Add(plank);
Debug.Log($"[SplintPractice] 已放置夹板 {_clickedPlanks.Count}/2: {plank.name}");
if (_clickedPlanks.Count >= 2)
{
EnableSecondPingStage();
ShowTip("两侧夹板已放置。下一步:再次点击 4 个蓝色固定点,完成夹板系带固定。");
}
else
{
ShowTip("已放置一侧夹板。继续点击另一块木板蓝色边框。");
}
}
public void UndoPlank(SplintObjectClickHandler plank)
{
if (plank == null) return;
_clickedPlanks.Remove(plank);
_finishedPings.Clear();
plank.gameObject.SetActive(true);
plank.ResetState(true);
if (plank == _bodyPad)
{
_bodyClicked = false;
HidePlankTargets();
return;
}
DisableSecondPingStage();
}
public bool OnPingSecondClick(PingClickHandler ping)
{
if (!IsActive || ping == null || !IsSplintPing(ping)) return false;
_finishedPings.Add(ping);
Debug.Log($"[SplintPractice] 已完成夹板固定点 {_finishedPings.Count}/4: {ping.name}");
if (_finishedPings.Count >= 4)
{
ShowTip("夹板固定已完成,请观察固定效果并点击结束确认。");
if (_finishRoutine != null) StopCoroutine(_finishRoutine);
_finishRoutine = StartCoroutine(ShowEndPanelDelayed());
}
else
{
ShowTip($"已完成固定点 {_finishedPings.Count}/4。继续由远端向近端点击下一个蓝色固定点。");
}
return true;
}
public void ResetToFirstPingStage()
{
if (!IsActive) return;
_placedPings.Clear();
_finishedPings.Clear();
_clickedPlanks.Clear();
_bodyClicked = false;
HideSplintObjects();
ConfigurePings();
ConfigureBodyPad();
ConfigurePlanks();
PingClickHandler firstPing = GetPingByIndex(0);
if (firstPing != null)
{
firstPing.gameObject.SetActive(true);
firstPing.ResetState(true);
}
}
private void AutoBind()
{
_manMuban = FindSceneObject(ManMubanName);
_manInjury = FindSceneObject(ManInjuryName);
}
private void ConfigurePings()
{
if (_manMuban == null) return;
_pings = new PingClickHandler[4];
string[] guideNames = { "ping.001", "ping.002", "ping.003", "ping.004" };
string[] targetNames = { "Armature/Bottoms.001", "ping.005", "ping.006", "ping.007" };
for (int i = 0; i < guideNames.Length; i++)
{
Transform guide = _manMuban.transform.Find(guideNames[i]);
Transform target = _manMuban.transform.Find(targetNames[i]);
if (guide == null || target == null) continue;
PingClickHandler handler = guide.GetComponent<PingClickHandler>();
if (handler == null) handler = guide.gameObject.AddComponent<PingClickHandler>();
handler.pingIndex = i;
handler.chanraoObject = target.gameObject;
handler.nextPing = null;
_pings[i] = handler;
target.gameObject.SetActive(false);
guide.gameObject.SetActive(false);
}
for (int i = 0; i < _pings.Length - 1; i++)
{
if (_pings[i] != null && _pings[i + 1] != null)
_pings[i].nextPing = _pings[i + 1].gameObject;
}
}
private void ConfigureBodyPad()
{
if (_manMuban == null) return;
Transform body = _manMuban.transform.Find("Body.001");
if (body == null) return;
_bodyPad = body.GetComponent<SplintObjectClickHandler>();
if (_bodyPad == null) _bodyPad = body.gameObject.AddComponent<SplintObjectClickHandler>();
body.gameObject.SetActive(false);
}
private void ConfigurePlanks()
{
if (_manMuban == null) return;
string[] plankNames = { "Wood plank - warm oak board", "Wood plank - warm oak board.001" };
List<SplintObjectClickHandler> planks = new List<SplintObjectClickHandler>();
for (int i = 0; i < plankNames.Length; i++)
{
Transform plank = _manMuban.transform.Find(plankNames[i]);
if (plank == null) continue;
SplintObjectClickHandler handler = plank.GetComponent<SplintObjectClickHandler>();
if (handler == null) handler = plank.gameObject.AddComponent<SplintObjectClickHandler>();
planks.Add(handler);
plank.gameObject.SetActive(false);
}
_planks = planks.ToArray();
}
private void HideSplintObjects()
{
if (_manMuban == null) return;
string[] names =
{
"ping.001", "ping.002", "ping.003", "ping.004",
"ping.005", "ping.006", "ping.007",
"Body.001",
"Wood plank - warm oak board", "Wood plank - warm oak board.001"
};
for (int i = 0; i < names.Length; i++)
{
Transform target = _manMuban.transform.Find(names[i]);
if (target != null) target.gameObject.SetActive(false);
}
Transform bottoms = _manMuban.transform.Find("Armature/Bottoms.001");
if (bottoms != null) bottoms.gameObject.SetActive(false);
}
private void ShowBodyPadTarget()
{
if (_bodyPad == null) ConfigureBodyPad();
if (_bodyPad == null) return;
_bodyPad.gameObject.SetActive(true);
_bodyPad.ResetState(true);
}
private void ShowPlankTargets()
{
if (_planks == null || _planks.Length == 0) ConfigurePlanks();
for (int i = 0; i < _planks.Length; i++)
{
if (_planks[i] == null) continue;
_planks[i].gameObject.SetActive(true);
_planks[i].ResetState(true);
}
}
private void HidePlankTargets()
{
if (_planks == null) return;
for (int i = 0; i < _planks.Length; i++)
{
if (_planks[i] == null) continue;
_planks[i].ResetState(false);
_planks[i].gameObject.SetActive(false);
}
}
private void EnableSecondPingStage()
{
if (_pings == null || _pings.Length == 0) ConfigurePings();
for (int i = 0; i < _pings.Length; i++)
{
if (_pings[i] == null) continue;
_pings[i].gameObject.SetActive(true);
_pings[i].ReEnableBorder();
}
}
private void DisableSecondPingStage()
{
if (_pings == null) return;
for (int i = 0; i < _pings.Length; i++)
{
if (_pings[i] == null) continue;
if (_pings[i].chanraoObject != null) _pings[i].chanraoObject.SetActive(false);
_pings[i].ResetState(false);
_pings[i].gameObject.SetActive(_placedPings.Contains(_pings[i]));
}
}
private System.Collections.IEnumerator ShowEndPanelDelayed()
{
yield return new WaitForSeconds(1f);
UIManager uiManager = FindObjectOfType<UIManager>();
if (uiManager != null) uiManager.ShowEndPanel();
_finishRoutine = null;
}
private PingClickHandler GetPingByIndex(int index)
{
if (_pings == null || _pings.Length == 0) ConfigurePings();
if (_pings == null || index < 0 || index >= _pings.Length) return null;
return _pings[index];
}
private bool IsSplintPing(PingClickHandler ping)
{
if (ping == null || _pings == null) return false;
for (int i = 0; i < _pings.Length; i++)
{
if (_pings[i] == ping) return true;
}
return false;
}
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;
}
private void ShowTip(string text)
{
UIManager uiManager = FindObjectOfType<UIManager>();
if (uiManager != null) uiManager.ShowTip(text);
}
}