fix: resolve Next/Previous step and UI skipping bugs
This commit is contained in:
@@ -61,6 +61,10 @@ public class OpeningFlowController : MonoBehaviour
|
||||
if (_womanObject == null) _womanObject = GameObject.Find("woman");
|
||||
if (_manInjuryObject == null) _manInjuryObject = GameObject.Find("manInjury");
|
||||
|
||||
// 确保开场时隐藏 Shose
|
||||
var shose = GameObject.Find("Shose");
|
||||
if (shose != null) shose.SetActive(false);
|
||||
|
||||
if (_blackScreenPanel == null)
|
||||
_blackScreenPanel = GameObject.Find("BlackScreenPanel");
|
||||
if (_blackScreenPanel == null)
|
||||
@@ -90,6 +94,10 @@ public class OpeningFlowController : MonoBehaviour
|
||||
{
|
||||
Debug.Log($"[OpeningFlow] PlayOpeningFlow() 被调用, enable={_enableOpeningFlow}");
|
||||
|
||||
// 再次确保开场隐藏鞋子
|
||||
var shose = GameObject.Find("Shose");
|
||||
if (shose != null) shose.SetActive(false);
|
||||
|
||||
if (!_enableOpeningFlow)
|
||||
{
|
||||
Debug.Log("[OpeningFlow] 开场动画已禁用,直接跳过");
|
||||
@@ -136,9 +144,9 @@ public class OpeningFlowController : MonoBehaviour
|
||||
|
||||
// Step 3: 摔倒 + manCall 音效
|
||||
Debug.Log("[OpeningFlow] Step3: manfull 开始");
|
||||
if (AudioManager.I != null) AudioManager.I.PlayManCallSfx();
|
||||
if (_manAnimator != null) { _manAnimator.Play("manfull", 0, 0f); }
|
||||
yield return new WaitForSeconds(2.1f);
|
||||
if (AudioManager.I != null) AudioManager.I.PlayManCallSfx();
|
||||
Debug.Log("[OpeningFlow] manfull 播放完成, 停留 2s");
|
||||
yield return new WaitForSeconds(2f);
|
||||
|
||||
|
||||
@@ -124,12 +124,98 @@ public class StepManager : MonoBehaviour
|
||||
|
||||
public void NextStep()
|
||||
{
|
||||
// Woman 自由步进优先
|
||||
if (_pingObjects != null && _pingObjects.Length > 0 && _pingStepIndex < _pingMaxStep) { PingStepNext(); return; }
|
||||
if (_womanObject != null && _womanPositions != null) { WomanStepNext(); return; }
|
||||
if (_activeSteps == null) return;
|
||||
if (_currentStepIndex >= _activeSteps.Length - 1) { CompleteExperiment(); return; }
|
||||
GoToStep(_currentStepIndex + 1);
|
||||
Debug.LogError("====== StepManager: 接收到 NextStep 指令! ======");
|
||||
|
||||
// 首先尝试是否存在可以点击的 3D 物品,如果有,优先执行 3D 交互
|
||||
if (TryClickActiveHandler())
|
||||
{
|
||||
Debug.Log("====== StepManager: 成功模拟 3D 物品点击,不进行常规的 NextStep 流程 ======");
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.Log("====== StepManager: 未找到 3D 物品,执行常规 NextStep 流程 ======");
|
||||
|
||||
if (_activeSteps != null && _currentStepIndex < _activeSteps.Length - 1)
|
||||
{
|
||||
GoToStep(_currentStepIndex + 1);
|
||||
}
|
||||
else if (_activeSteps != null && _currentStepIndex == _activeSteps.Length - 1)
|
||||
{
|
||||
CompleteExperiment();
|
||||
}
|
||||
}
|
||||
|
||||
public void TryInteractCurrent3DStep()
|
||||
{
|
||||
Debug.Log("====== StepManager: 接收到 3D 自动交互指令 ======");
|
||||
if (TryClickActiveHandler()) return;
|
||||
Debug.Log("====== StepManager: 当前没有可自动交互的 3D 物品 ======");
|
||||
}
|
||||
|
||||
private bool TryClickActiveHandler()
|
||||
{
|
||||
var touches = Object.FindObjectsOfType<TouchPositionHandler>();
|
||||
System.Array.Sort(touches, (a, b) => string.Compare(a.name, b.name));
|
||||
foreach (var t in touches)
|
||||
{
|
||||
if (t.gameObject.activeInHierarchy && t.CanBeClicked())
|
||||
{
|
||||
Debug.LogError($"====== StepManager: 模拟点击了 {t.name}! ======");
|
||||
t.PerformClick();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
var shoses = Object.FindObjectsOfType<ShoseClickHandler>();
|
||||
foreach (var h in shoses)
|
||||
{
|
||||
if (h.gameObject.activeInHierarchy && h.CanBeClicked())
|
||||
{
|
||||
Debug.LogError($"====== StepManager: 模拟点击了 {h.name}! ======");
|
||||
h.PerformClick();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
var chanraos = Object.FindObjectsOfType<ChanraoTuibuHandler>();
|
||||
foreach (var h in chanraos)
|
||||
{
|
||||
if (h.gameObject.activeInHierarchy && h.CanBeClicked())
|
||||
{
|
||||
Debug.LogError($"====== StepManager: 模拟点击了 {h.name}! ======");
|
||||
h.PerformClick();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
var pings = Object.FindObjectsOfType<PingClickHandler>();
|
||||
System.Array.Sort(pings, (a, b) => string.Compare(b.name, a.name));
|
||||
foreach (var p in pings)
|
||||
{
|
||||
if (p.gameObject.activeInHierarchy && p.CanBeClicked())
|
||||
{
|
||||
Debug.LogError($"====== StepManager: 模拟点击了 {p.name}! ======");
|
||||
p.PerformClick();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryUndoActiveHandler()
|
||||
{
|
||||
if (_pingStepIndex > 0 && _pingStepIndex <= _pingMaxStep)
|
||||
{
|
||||
if (_pingObjects == null) InitPingSteps();
|
||||
PingStepPrevious();
|
||||
return true;
|
||||
}
|
||||
if (_womanStepIndex > 0 && _womanStepIndex <= _womanMaxStep && _pingStepIndex == 0)
|
||||
{
|
||||
WomanStepPrevious();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void PingStepPrevious()
|
||||
@@ -151,15 +237,33 @@ public class StepManager : MonoBehaviour
|
||||
}
|
||||
else
|
||||
{
|
||||
// Undo first click: hide current ping, show previous ping (if any)
|
||||
// Undo first click: reset current ping to blink state, and hide the next ping it spawned
|
||||
if (_pingObjects != null && idx < _pingObjects.Length && _pingObjects[idx] != null)
|
||||
{
|
||||
var h = _pingObjects[idx].GetComponent<PingClickHandler>();
|
||||
if (h != null) h.UndoShow();
|
||||
if (h != null)
|
||||
{
|
||||
// 撤销它对下一个目标的影响
|
||||
if (h.nextPing != null)
|
||||
{
|
||||
h.nextPing.SetActive(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果是最后一个,撤销它唤醒的所有其他边框
|
||||
var allPings = Object.FindObjectsOfType<PingClickHandler>();
|
||||
foreach (var p in allPings)
|
||||
{
|
||||
if (p.gameObject != h.gameObject)
|
||||
{
|
||||
p.gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 重置自己到初始状态
|
||||
h.ResetState();
|
||||
}
|
||||
}
|
||||
// If going back to start, show ping.004
|
||||
if (idx == 0 && _pingObjects != null && _pingObjects.Length > 0 && _pingObjects[0] != null)
|
||||
_pingObjects[0].SetActive(true);
|
||||
}
|
||||
_pingStepIndex--;
|
||||
Debug.Log($"StepManager: Ping撤销 -> {_pingStepIndex}");
|
||||
@@ -215,12 +319,23 @@ public class StepManager : MonoBehaviour
|
||||
|
||||
public void PreviousStep()
|
||||
{
|
||||
// Woman 自由步进优先
|
||||
if (_pingObjects != null && _pingObjects.Length > 0 && _pingStepIndex > 0) { PingStepPrevious(); return; }
|
||||
if (_womanObject != null && _womanPositions != null) { WomanStepPrevious(); return; }
|
||||
if (_activeSteps == null) return;
|
||||
if (_currentStepIndex <= 0) return;
|
||||
GoToStep(_currentStepIndex - 1);
|
||||
Debug.LogError("====== StepManager: 接收到 PreviousStep 指令! ======");
|
||||
|
||||
// 如果尝试撤销 3D 交互成功,就不再回退 UI
|
||||
if (TryUndoActiveHandler())
|
||||
{
|
||||
Debug.Log("====== StepManager: 成功撤销 3D 交互,不进行常规的 PreviousStep 流程 ======");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_currentStepIndex > 0)
|
||||
{
|
||||
GoToStep(_currentStepIndex - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("====== StepManager: 已经是第一步,无法后退 ======");
|
||||
}
|
||||
}
|
||||
|
||||
public StepData GetCurrentStep()
|
||||
@@ -290,15 +405,20 @@ public class StepManager : MonoBehaviour
|
||||
if (mi == null) return;
|
||||
_pingObjects = new GameObject[4];
|
||||
_chanraoObjects = new GameObject[4];
|
||||
for (int i = 1; i <= 4; i++)
|
||||
|
||||
var pings = Object.FindObjectsOfType<PingClickHandler>(true);
|
||||
foreach (var p in pings)
|
||||
{
|
||||
string pn = "ping." + i.ToString("D3");
|
||||
string cn = "chanrao." + i.ToString("D3");
|
||||
var pt = mi.transform.Find(pn);
|
||||
var ct = mi.transform.Find(cn);
|
||||
if (pt != null) _pingObjects[i-1] = pt.gameObject;
|
||||
if (ct != null) _chanraoObjects[i-1] = ct.gameObject;
|
||||
if (p.pingIndex >= 0 && p.pingIndex < 4)
|
||||
{
|
||||
_pingObjects[p.pingIndex] = p.gameObject;
|
||||
if (p.chanraoObject != null)
|
||||
{
|
||||
_chanraoObjects[p.pingIndex] = p.chanraoObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_pingStepIndex = 0;
|
||||
Debug.Log("StepManager: Ping步进已初始化");
|
||||
}
|
||||
|
||||
@@ -4,11 +4,15 @@ public class ChanraoTuibuHandler : MonoBehaviour
|
||||
{
|
||||
public GameObject nextObject;
|
||||
private MeshRenderer _outlineMesh;
|
||||
private bool _isBlinking;
|
||||
|
||||
public bool CanBeClicked() { return _isBlinking; }
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
if (GetComponent<Collider>() == null) gameObject.AddComponent<SphereCollider>().radius = 0.5f;
|
||||
foreach (var r in GetComponentsInChildren<Renderer>(true)) r.enabled = false; CreateOutlineMesh();
|
||||
_isBlinking = true;
|
||||
StopAllCoroutines();
|
||||
StartCoroutine(BlinkOutline());
|
||||
Debug.Log($"[ChanraoTuibu] OnEnable outline={_outlineMesh!=null}");
|
||||
@@ -19,17 +23,28 @@ public class ChanraoTuibuHandler : MonoBehaviour
|
||||
if (_outlineMesh != null) return;
|
||||
var mf = GetComponent<MeshFilter>();
|
||||
if (mf == null) mf = GetComponentInChildren<MeshFilter>();
|
||||
if (mf == null || mf.sharedMesh == null) { Debug.LogWarning("[ChanraoTuibu] 无MeshFilter"); return; }
|
||||
Mesh mesh = null;
|
||||
if (mf != null && mf.sharedMesh != null)
|
||||
{
|
||||
mesh = mf.sharedMesh;
|
||||
}
|
||||
else
|
||||
{
|
||||
var smr = GetComponent<SkinnedMeshRenderer>();
|
||||
if (smr == null) smr = GetComponentInChildren<SkinnedMeshRenderer>();
|
||||
if (smr != null && smr.sharedMesh != null)
|
||||
mesh = smr.sharedMesh;
|
||||
}
|
||||
if (mesh == null) { Debug.LogWarning("[ChanraoTuibu] 无Mesh数据"); return; }
|
||||
var shader = Shader.Find("Custom/OutlineUnlit");
|
||||
if (shader == null) { Debug.LogWarning("[ChanraoTuibu] Shader缺失"); return; }
|
||||
var mat = new Material(shader);
|
||||
mat.SetColor("_OutlineColor", new Color(0.2f, 0.5f, 1f, 1f));
|
||||
mat.SetColor("_MainColor", new Color(0, 0, 0, 0));
|
||||
float s = transform.lossyScale.magnitude / Mathf.Sqrt(3);
|
||||
mat.SetFloat("_OutlineWidth", s > 0.001f ? OutlineConfig.Width / s : OutlineConfig.Width);
|
||||
mat.SetFloat("_OutlineWidth", OutlineConfig.Width);
|
||||
var go = new GameObject("Outline");
|
||||
go.transform.SetParent(transform, false);
|
||||
go.AddComponent<MeshFilter>().sharedMesh = mf.sharedMesh;
|
||||
go.AddComponent<MeshFilter>().sharedMesh = mesh;
|
||||
_outlineMesh = go.AddComponent<MeshRenderer>();
|
||||
_outlineMesh.material = mat;
|
||||
}
|
||||
@@ -42,13 +57,42 @@ public class ChanraoTuibuHandler : MonoBehaviour
|
||||
}
|
||||
|
||||
void OnMouseDown()
|
||||
{
|
||||
PerformClick();
|
||||
}
|
||||
|
||||
public void PerformClick()
|
||||
{
|
||||
Debug.Log($"[ChanraoTuibu] {name} 被点击");
|
||||
_isBlinking = false;
|
||||
StopAllCoroutines();
|
||||
if (_outlineMesh != null) _outlineMesh.enabled = false;
|
||||
var mr = GetComponent<MeshRenderer>(); if (mr != null) mr.enabled = true;
|
||||
foreach (var r in GetComponentsInChildren<Renderer>(true)) r.enabled = true; // 显示模型
|
||||
foreach (var r in GetComponentsInChildren<Renderer>(true)) { if (_outlineMesh != null && r == _outlineMesh) continue; r.enabled = true; } // 显示模型
|
||||
|
||||
// 如果由于意外 nextObject 丢失了,手动把它指向 ping.004
|
||||
if (nextObject == null)
|
||||
{
|
||||
var mi = GameObject.Find("manInjury");
|
||||
if (mi != null)
|
||||
{
|
||||
var p004 = mi.transform.Find("ping.004");
|
||||
if (p004 != null) nextObject = p004.gameObject;
|
||||
}
|
||||
}
|
||||
|
||||
// 激活下一个(ping.004)
|
||||
if (nextObject != null) { nextObject.SetActive(true); Debug.Log($"[ChanraoTuibu] → {nextObject.name}"); }
|
||||
else Debug.LogError("[ChanraoTuibu] 致命错误:nextObject 依然是 null!找不到 ping.004");
|
||||
}
|
||||
|
||||
public void ResetState()
|
||||
{
|
||||
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 = true;
|
||||
StopAllCoroutines();
|
||||
StartCoroutine(BlinkOutline());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,5 +2,5 @@ using UnityEngine;
|
||||
|
||||
public static class OutlineConfig
|
||||
{
|
||||
public static float Width = 0.15f;
|
||||
public static float Width = 2.0f;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,9 @@ public class PingClickHandler : MonoBehaviour
|
||||
public int pingIndex;
|
||||
private MeshRenderer _modelRenderer, _outlineMesh;
|
||||
private bool _modelShown;
|
||||
private bool _isBlinking;
|
||||
|
||||
public bool CanBeClicked() { return _isBlinking; }
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
@@ -15,6 +18,7 @@ public class PingClickHandler : MonoBehaviour
|
||||
if (GetComponent<Collider>() == null) gameObject.AddComponent<SphereCollider>().radius = 0.5f;
|
||||
CreateOutlineMesh();
|
||||
_modelShown = false;
|
||||
_isBlinking = true;
|
||||
StopAllCoroutines();
|
||||
StartCoroutine(BlinkOutline());
|
||||
}
|
||||
@@ -30,10 +34,11 @@ public class PingClickHandler : MonoBehaviour
|
||||
var mat = new Material(shader);
|
||||
mat.SetColor("_OutlineColor", new Color(0.2f, 0.5f, 1f, 1f));
|
||||
mat.SetColor("_MainColor", new Color(0, 0, 0, 0));
|
||||
float s = transform.lossyScale.magnitude / Mathf.Sqrt(3);
|
||||
mat.SetFloat("_OutlineWidth", s > 0.001f ? OutlineConfig.Width / s : OutlineConfig.Width);
|
||||
mat.SetFloat("_OutlineWidth", OutlineConfig.Width);
|
||||
var go = new GameObject("Outline");
|
||||
go.transform.SetParent(transform, false);
|
||||
// 稍微往上偏移一点点,防止边框插进地里
|
||||
go.transform.position += Vector3.up * 0.05f;
|
||||
go.AddComponent<MeshFilter>().sharedMesh = mf.sharedMesh;
|
||||
_outlineMesh = go.AddComponent<MeshRenderer>();
|
||||
_outlineMesh.material = mat;
|
||||
@@ -47,6 +52,11 @@ public class PingClickHandler : MonoBehaviour
|
||||
}
|
||||
|
||||
void OnMouseDown()
|
||||
{
|
||||
PerformClick();
|
||||
}
|
||||
|
||||
public void PerformClick()
|
||||
{
|
||||
foreach (var c in Object.FindObjectsOfType<Camera>())
|
||||
{
|
||||
@@ -58,11 +68,23 @@ public class PingClickHandler : MonoBehaviour
|
||||
if (!_modelShown)
|
||||
{
|
||||
_modelShown = true;
|
||||
_isBlinking = false;
|
||||
StopAllCoroutines();
|
||||
if (_modelRenderer != null) _modelRenderer.enabled = true;
|
||||
foreach (var r in GetComponentsInChildren<Renderer>(true)) r.enabled = true;
|
||||
if (_outlineMesh != null) _outlineMesh.enabled = false;
|
||||
if (nextPing != null) nextPing.SetActive(true);
|
||||
else
|
||||
{
|
||||
// 如果 nextPing 是 null,说明我是最后一个 ping(也就是 ping.001)
|
||||
// 此时所有的 ping 都已经显示了模型,进入阶段二:重新点亮所有的 ping 边框
|
||||
var allPings = Object.FindObjectsOfType<PingClickHandler>();
|
||||
foreach (var p in allPings)
|
||||
{
|
||||
p.ReEnableBorder();
|
||||
}
|
||||
Debug.Log($"[PingClick] {name} 是最后一个 ping,所有边框已重新激活!");
|
||||
}
|
||||
Debug.Log($"[PingClick] {name} 模型显示, 激活 {nextPing?.name}");
|
||||
var sm = FindObjectOfType<StepManager>();
|
||||
if (sm != null) sm.RecordPingStep(pingIndex, false);
|
||||
@@ -73,10 +95,51 @@ public class PingClickHandler : MonoBehaviour
|
||||
gameObject.SetActive(false);
|
||||
var sm = FindObjectOfType<StepManager>();
|
||||
if (sm != null) sm.RecordPingStep(pingIndex, true);
|
||||
|
||||
// 判断是否是真正的最后一步:检查所有的 PingClickHandler,看是否还有 activeInHierarchy 的。
|
||||
// 注意当前 gameObject 刚刚被 SetActive(false),所以 FindObjectsOfType 获取不到当前的对象了。
|
||||
var remainingPings = Object.FindObjectsOfType<PingClickHandler>();
|
||||
int activePingCount = 0;
|
||||
foreach (var p in remainingPings)
|
||||
{
|
||||
if (p.gameObject.activeInHierarchy) activePingCount++;
|
||||
}
|
||||
|
||||
if (activePingCount == 0)
|
||||
{
|
||||
Debug.Log("[PingClick] 所有的 ping 都已经绑成 chanrao 了!播放完成语音。");
|
||||
var am = FindObjectOfType<AudioManager>();
|
||||
if (am != null)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
var finishClip = UnityEditor.AssetDatabase.LoadAssetAtPath<AudioClip>("Assets/Audio/people/伤员一切正常,包扎完毕.mp3");
|
||||
if (finishClip != null) am.PlayVoice(finishClip);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ReEnableBorder() { if (_outlineMesh != null) { _outlineMesh.enabled = true; StopAllCoroutines(); StartCoroutine(BlinkOutline()); } }
|
||||
public void UndoSecondClick() {}
|
||||
public void ReEnableBorder() { _isBlinking = true; if (_outlineMesh != null) { _outlineMesh.enabled = true; StopAllCoroutines(); StartCoroutine(BlinkOutline()); } }
|
||||
public void UndoSecondClick()
|
||||
{
|
||||
gameObject.SetActive(true);
|
||||
_modelShown = true;
|
||||
if (_modelRenderer != null) _modelRenderer.enabled = true;
|
||||
foreach (var r in GetComponentsInChildren<Renderer>(true)) r.enabled = true;
|
||||
if (_outlineMesh != null) _outlineMesh.enabled = false;
|
||||
}
|
||||
public void UndoShow() { gameObject.SetActive(false); }
|
||||
|
||||
public void ResetState()
|
||||
{
|
||||
_modelShown = false;
|
||||
_isBlinking = true;
|
||||
if (_modelRenderer != null) _modelRenderer.enabled = false;
|
||||
// 隐藏自身模型
|
||||
foreach (var r in GetComponentsInChildren<Renderer>(true)) { if (_outlineMesh != null && r == _outlineMesh) continue; r.enabled = false; }
|
||||
if (_outlineMesh != null) _outlineMesh.enabled = true;
|
||||
StopAllCoroutines();
|
||||
StartCoroutine(BlinkOutline());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,12 @@ public class ShoseClickHandler : MonoBehaviour
|
||||
public Transform targetPoint;
|
||||
private MeshRenderer _outlineMesh;
|
||||
private bool _clicked;
|
||||
|
||||
public bool CanBeClicked() { return !_clicked; }
|
||||
|
||||
private Vector3 _initialPosition;
|
||||
private Quaternion _initialRotation;
|
||||
private bool _hasRecordedInitialTransform = false;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
@@ -17,12 +23,20 @@ public class ShoseClickHandler : MonoBehaviour
|
||||
Destroy(GetComponent<SphereCollider>());
|
||||
var col = GetComponent<BoxCollider>();
|
||||
if (col == null) col = gameObject.AddComponent<BoxCollider>();
|
||||
// 按 localScale 缩放 collider 匹配模型实际大小
|
||||
col.center = Vector3.Scale(mb.center, transform.localScale);
|
||||
col.size = Vector3.Scale(mb.size, transform.localScale) * 1.2f;
|
||||
// Unity 的 BoxCollider.size 处于本地坐标空间,会自动乘上 localScale,这里不能重复缩放
|
||||
col.center = mb.center;
|
||||
col.size = mb.size * 1.2f;
|
||||
col.enabled = true;
|
||||
}
|
||||
else { if (GetComponent<Collider>() == null) gameObject.AddComponent<SphereCollider>().radius = 0.3f; }
|
||||
|
||||
if (!_hasRecordedInitialTransform)
|
||||
{
|
||||
_initialPosition = transform.position;
|
||||
_initialRotation = transform.rotation;
|
||||
_hasRecordedInitialTransform = true;
|
||||
}
|
||||
|
||||
CreateOutlineMesh();
|
||||
StopAllCoroutines();
|
||||
StartCoroutine(BlinkOutline());
|
||||
@@ -40,9 +54,7 @@ public class ShoseClickHandler : MonoBehaviour
|
||||
var mat = new Material(shader);
|
||||
mat.SetColor("_OutlineColor", new Color(0.2f, 0.5f, 1f, 1f));
|
||||
mat.SetColor("_MainColor", new Color(0, 0, 0, 0));
|
||||
// 描边宽度补偿 scale
|
||||
float s = transform.lossyScale.magnitude / Mathf.Sqrt(3);
|
||||
mat.SetFloat("_OutlineWidth", s > 0.001f ? OutlineConfig.Width / s : OutlineConfig.Width);
|
||||
mat.SetFloat("_OutlineWidth", OutlineConfig.Width);
|
||||
var go = new GameObject("Outline");
|
||||
go.transform.SetParent(transform, false);
|
||||
go.AddComponent<MeshFilter>().sharedMesh = mf.sharedMesh;
|
||||
@@ -58,6 +70,11 @@ public class ShoseClickHandler : MonoBehaviour
|
||||
}
|
||||
|
||||
void OnMouseDown()
|
||||
{
|
||||
PerformClick();
|
||||
}
|
||||
|
||||
public void PerformClick()
|
||||
{
|
||||
if (_clicked) return;
|
||||
_clicked = true;
|
||||
@@ -66,5 +83,92 @@ public class ShoseClickHandler : MonoBehaviour
|
||||
if (_outlineMesh != null) _outlineMesh.enabled = false;
|
||||
if (targetPoint != null) { transform.position = targetPoint.position; transform.rotation = targetPoint.rotation; }
|
||||
if (nextObject != null) nextObject.SetActive(true);
|
||||
|
||||
var woman = GameObject.Find("woman");
|
||||
if (woman != null)
|
||||
{
|
||||
var anim = woman.GetComponent<Animator>();
|
||||
if (anim != null)
|
||||
{
|
||||
anim.SetBool("isCrouch", true);
|
||||
anim.CrossFade("womanCrouch", 0.1f);
|
||||
}
|
||||
|
||||
// 移动 woman 到 point3
|
||||
var point3 = GameObject.Find("womanPointArray/point3");
|
||||
if (point3 != null)
|
||||
{
|
||||
woman.transform.position = point3.transform.position;
|
||||
woman.transform.rotation = point3.transform.rotation;
|
||||
}
|
||||
}
|
||||
|
||||
var manInjury = GameObject.Find("manInjury");
|
||||
if (manInjury != null)
|
||||
{
|
||||
var anim = manInjury.GetComponent<Animator>();
|
||||
if (anim != null)
|
||||
{
|
||||
anim.SetBool("CheckFoot", true);
|
||||
anim.CrossFade("manInjuryFoot", 0.1f);
|
||||
|
||||
// 启动协程,在动画播放完后将 CheckFoot 重置为 false
|
||||
StartCoroutine(ResetCheckFoot(anim));
|
||||
}
|
||||
}
|
||||
|
||||
// 隐藏 point3 的边框 (touchArray/point3)
|
||||
var touchArray = GameObject.Find("touchArray");
|
||||
if (touchArray != null)
|
||||
{
|
||||
var touchPoint3 = touchArray.transform.Find("point3");
|
||||
if (touchPoint3 != null)
|
||||
{
|
||||
touchPoint3.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
// 点击鞋子时播放原本挂在 point1 上的那段语音(伤员左大腿疑似骨折)
|
||||
var am = FindObjectOfType<AudioManager>();
|
||||
if (am != null)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
var fractureClip = UnityEditor.AssetDatabase.LoadAssetAtPath<AudioClip>("Assets/Audio/people/伤员左大腿疑似骨折,无伤口.mp3");
|
||||
if (fractureClip != null) am.PlayVoice(fractureClip);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// 切换到摄像机3
|
||||
var audioManager = FindObjectOfType<AudioManager>();
|
||||
if (audioManager != null)
|
||||
{
|
||||
audioManager.SwitchToCamera3();
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetState()
|
||||
{
|
||||
_clicked = false;
|
||||
if (_outlineMesh != null) _outlineMesh.enabled = true;
|
||||
|
||||
if (_hasRecordedInitialTransform)
|
||||
{
|
||||
transform.position = _initialPosition;
|
||||
transform.rotation = _initialRotation;
|
||||
}
|
||||
|
||||
StopAllCoroutines();
|
||||
StartCoroutine(BlinkOutline());
|
||||
}
|
||||
|
||||
private System.Collections.IEnumerator ResetCheckFoot(Animator anim)
|
||||
{
|
||||
// 等待约 3.5 秒(根据实际检查脚动画长度),将 CheckFoot 设回 false
|
||||
yield return new WaitForSeconds(3.5f);
|
||||
if (anim != null)
|
||||
{
|
||||
anim.SetBool("CheckFoot", false);
|
||||
Debug.Log("[ShoseClick] CheckFoot 已被重置为 false");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ public class TouchPositionHandler : MonoBehaviour
|
||||
private void Start()
|
||||
{
|
||||
_meshRenderer = GetComponent<MeshRenderer>();
|
||||
StartCoroutine(BlinkMesh());
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
@@ -32,19 +31,29 @@ public class TouchPositionHandler : MonoBehaviour
|
||||
var c = mat.GetColor("_MainColor");
|
||||
c.a = 0f;
|
||||
mat.SetColor("_MainColor", c);
|
||||
|
||||
// 同步赋值统一的描边宽度,否则可能会太细导致看不见闪动
|
||||
if (mat.HasProperty("_OutlineWidth"))
|
||||
{
|
||||
mat.SetFloat("_OutlineWidth", OutlineConfig.Width);
|
||||
}
|
||||
}
|
||||
}
|
||||
StopAllCoroutines();
|
||||
StartCoroutine(BlinkMesh());
|
||||
}
|
||||
|
||||
private System.Collections.IEnumerator BlinkMesh()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (_meshRenderer != null) _meshRenderer.enabled = !_meshRenderer.enabled;
|
||||
yield return new WaitForSeconds(0.4f);
|
||||
}
|
||||
yield break;
|
||||
}
|
||||
|
||||
public bool CanBeClicked()
|
||||
{
|
||||
return gameObject.activeInHierarchy && _meshRenderer != null && _meshRenderer.enabled;
|
||||
}
|
||||
|
||||
public void PerformClick()
|
||||
{
|
||||
OnMouseDown();
|
||||
}
|
||||
|
||||
private void OnMouseDown()
|
||||
@@ -92,7 +101,6 @@ public class TouchPositionHandler : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
StopAllCoroutines();
|
||||
if (_meshRenderer != null) _meshRenderer.enabled = false;
|
||||
|
||||
gameObject.SetActive(false);
|
||||
|
||||
@@ -49,6 +49,9 @@ public class PageController : MonoBehaviour
|
||||
{
|
||||
Debug.Log("=== PageController 初始化开始 ===");
|
||||
|
||||
// 一开始就隐藏鞋子,以免在主页或开场动画时穿帮
|
||||
HideShose();
|
||||
|
||||
// 关掉多余AudioListener(只留Main Camera的)
|
||||
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; }
|
||||
@@ -294,17 +297,27 @@ public class PageController : MonoBehaviour
|
||||
if (go.name == "medicalBox") { go.SetActive(true); Debug.Log("PageController: medicalBox 已显示"); break; }
|
||||
}
|
||||
|
||||
var touch = GameObject.Find("touchArray");
|
||||
if (touch != null && touch.transform.childCount > 0) { touch.transform.GetChild(0).gameObject.SetActive(true); Debug.Log("PageController: touchArray/point1 已显示");
|
||||
|
||||
// 自动播放第一段 woman 语音
|
||||
var am = FindObjectOfType<AudioManager>();
|
||||
if (am != null) {
|
||||
#if UNITY_EDITOR
|
||||
var autoClip = UnityEditor.AssetDatabase.LoadAssetAtPath<AudioClip>("Assets/Audio/people/周围环境安全,我是救护志愿者,我可以帮助您吗.mp3");
|
||||
if (autoClip != null) am.PlayVoice(autoClip);
|
||||
#endif
|
||||
} }
|
||||
// 隐藏所有的 point1, 2, 3,不要让它们显示出来
|
||||
var touchArray = GameObject.Find("touchArray");
|
||||
if (touchArray != null)
|
||||
{
|
||||
foreach (Transform child in touchArray.transform)
|
||||
{
|
||||
child.gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
// 把 woman 直接放到 point1 的位置
|
||||
var woman = GameObject.Find("woman");
|
||||
var point1 = GameObject.Find("womanPointArray/point1");
|
||||
if (woman != null && point1 != null)
|
||||
{
|
||||
woman.transform.position = point1.transform.position;
|
||||
woman.transform.rotation = point1.transform.rotation;
|
||||
}
|
||||
|
||||
// 启动语音、等待及切换摄像机并激活鞋子的流程
|
||||
StartCoroutine(StartInitialVoiceAndCameraRoutine());
|
||||
|
||||
var tipPanel = GameObject.Find("TipPanel");
|
||||
if (tipPanel != null) tipPanel.SetActive(true);
|
||||
@@ -316,6 +329,8 @@ public class PageController : MonoBehaviour
|
||||
{
|
||||
Debug.Log("PageController: [ReturnToHome] 返回首页并重置场景");
|
||||
|
||||
HideShose();
|
||||
|
||||
var man = GameObject.Find("man");
|
||||
if (man != null) man.SetActive(true);
|
||||
var man2 = GameObject.Find("man2");
|
||||
@@ -341,16 +356,117 @@ public class PageController : MonoBehaviour
|
||||
if (mainGo != null) { var c = mainGo.GetComponent<Camera>(); if (c != null) c.enabled = true; }
|
||||
var cam2Go = GameObject.Find("camera2");
|
||||
if (cam2Go != null) { var c = cam2Go.GetComponent<Camera>(); if (c != null) c.enabled = false; }
|
||||
var cam3Go = GameObject.Find("camera3");
|
||||
if (cam3Go != null) { var c = cam3Go.GetComponent<Camera>(); if (c != null) c.enabled = false; }
|
||||
|
||||
var manAnim = man != null ? man.GetComponent<Animator>() : null;
|
||||
if (manAnim != null) { manAnim.SetBool("IsStart", false); manAnim.SetBool("IsFull", false); manAnim.Play("manIdle", 0, 0f); }
|
||||
|
||||
// 重置 woman 动画
|
||||
if (woman != null)
|
||||
{
|
||||
var wAnim = woman.GetComponent<Animator>();
|
||||
if (wAnim != null)
|
||||
{
|
||||
wAnim.SetBool("isCrouch", false);
|
||||
wAnim.Play("Idle", 0, 0f); // 假设其默认闲置动画为 Idle 或类似名称
|
||||
}
|
||||
}
|
||||
|
||||
// 重置 manInjury 动画
|
||||
if (mi != null)
|
||||
{
|
||||
var mAnim = mi.GetComponent<Animator>();
|
||||
if (mAnim != null)
|
||||
{
|
||||
mAnim.SetBool("CheckFoot", false);
|
||||
mAnim.Play("manInjury", 0, 0f); // 默认动画
|
||||
}
|
||||
|
||||
// 重置所有的 Handler 状态
|
||||
foreach (var handler in mi.GetComponentsInChildren<PingClickHandler>(true)) handler.ResetState();
|
||||
foreach (var handler in mi.GetComponentsInChildren<ChanraoTuibuHandler>(true)) handler.ResetState();
|
||||
}
|
||||
|
||||
// 找到 Shose 重置点击状态
|
||||
var shose = GameObject.Find("Shose");
|
||||
if (shose == null)
|
||||
{
|
||||
var allTransforms = Resources.FindObjectsOfTypeAll<Transform>();
|
||||
foreach(var t in allTransforms) { if (t.parent == null && t.name == "Shose") { shose = t.gameObject; break; } }
|
||||
}
|
||||
if (shose != null)
|
||||
{
|
||||
var h = shose.GetComponent<ShoseClickHandler>();
|
||||
if (h != null) h.ResetState();
|
||||
}
|
||||
|
||||
ShowPage(Page.Home);
|
||||
}
|
||||
|
||||
private System.Collections.IEnumerator StartInitialVoiceAndCameraRoutine()
|
||||
{
|
||||
float waitTime = 1f; // 默认等1秒
|
||||
var am = FindObjectOfType<AudioManager>();
|
||||
|
||||
// 实验刚开始,立刻激活鞋子,不等语音播完
|
||||
var shose = GameObject.Find("Shose");
|
||||
if (shose == null)
|
||||
{
|
||||
// 如果被禁用了,找它的最顶层引用
|
||||
var allTransforms = Resources.FindObjectsOfTypeAll<Transform>();
|
||||
foreach(var t in allTransforms) { if (t.parent == null && t.name == "Shose") { shose = t.gameObject; break; } }
|
||||
}
|
||||
if (shose != null)
|
||||
{
|
||||
var h = shose.GetComponent<ShoseClickHandler>();
|
||||
if (h == null) h = shose.AddComponent<ShoseClickHandler>();
|
||||
h.targetPoint = GameObject.Find("ShosePoint")?.transform;
|
||||
if (h.nextObject == null)
|
||||
{
|
||||
var chanraotuibu = GameObject.Find("chanraotuibu");
|
||||
if (chanraotuibu == null)
|
||||
{
|
||||
var mi = GameObject.Find("manInjury");
|
||||
if (mi != null) chanraotuibu = mi.transform.Find("chanraotuibu")?.gameObject;
|
||||
}
|
||||
h.nextObject = chanraotuibu;
|
||||
}
|
||||
shose.SetActive(false);
|
||||
shose.SetActive(true);
|
||||
Debug.Log("PageController: 开场动画结束,Shose 已立刻激活");
|
||||
}
|
||||
|
||||
// 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} 秒以切换摄像机并激活鞋子");
|
||||
yield return new WaitForSeconds(waitTime);
|
||||
|
||||
// 2. 切换到 camera3
|
||||
if (am != null) am.SwitchToCamera3();
|
||||
}
|
||||
|
||||
private void HideShose()
|
||||
{
|
||||
var shose = GameObject.Find("Shose");
|
||||
if (shose != null)
|
||||
{
|
||||
shose.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ public class UIManager : MonoBehaviour
|
||||
var btn = btnFS.GetComponent<Button>();
|
||||
if (btn != null)
|
||||
{
|
||||
btn.onClick.AddListener(ToggleFullScreen);
|
||||
btn.onClick.RemoveListener(ToggleFullScreen); btn.onClick.AddListener(ToggleFullScreen);
|
||||
Debug.Log("UIManager: Navbar/FullScreen 绑定成功 -> ToggleFullScreen");
|
||||
}
|
||||
}
|
||||
@@ -118,7 +118,7 @@ public class UIManager : MonoBehaviour
|
||||
var btn = btnPrev.GetComponent<Button>();
|
||||
if (btn != null)
|
||||
{
|
||||
btn.onClick.AddListener(OnPreviousClicked);
|
||||
btn.onClick.RemoveListener(OnPreviousClicked); btn.onClick.AddListener(OnPreviousClicked);
|
||||
Debug.Log("UIManager: Navbar/Previous 绑定成功 -> OnPreviousClicked");
|
||||
}
|
||||
}
|
||||
@@ -131,7 +131,7 @@ public class UIManager : MonoBehaviour
|
||||
var btn = btnNext.GetComponent<Button>();
|
||||
if (btn != null)
|
||||
{
|
||||
btn.onClick.AddListener(OnNextClicked);
|
||||
btn.onClick.RemoveListener(OnNextClicked); btn.onClick.AddListener(OnNextClicked);
|
||||
Debug.Log("UIManager: Navbar/Next 绑定成功 -> OnNextClicked");
|
||||
}
|
||||
}
|
||||
@@ -144,7 +144,7 @@ public class UIManager : MonoBehaviour
|
||||
var btn = btnMode.GetComponent<Button>();
|
||||
if (btn != null)
|
||||
{
|
||||
btn.onClick.AddListener(OnSwitchModeClicked);
|
||||
btn.onClick.RemoveListener(OnSwitchModeClicked); btn.onClick.AddListener(OnSwitchModeClicked);
|
||||
Debug.Log("UIManager: Navbar/PracticeMode 绑定成功 -> OnSwitchModeClicked");
|
||||
}
|
||||
}
|
||||
@@ -157,7 +157,7 @@ public class UIManager : MonoBehaviour
|
||||
var btn = btnBack.GetComponent<Button>();
|
||||
if (btn != null)
|
||||
{
|
||||
btn.onClick.AddListener(OnReturnClicked);
|
||||
btn.onClick.RemoveListener(OnReturnClicked); btn.onClick.AddListener(OnReturnClicked);
|
||||
Debug.Log("UIManager: Navbar/BtnBack 绑定成功 -> OnReturnClicked");
|
||||
}
|
||||
}
|
||||
@@ -186,11 +186,11 @@ public class UIManager : MonoBehaviour
|
||||
|
||||
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 绑定成功"); }
|
||||
if (_btnFullScreen != null) { _btnFullScreen.onClick.RemoveListener(ToggleFullScreen); _btnFullScreen.onClick.AddListener(ToggleFullScreen); Debug.Log("UIManager: _btnFullScreen 绑定成功"); }
|
||||
if (_btnPrevious != null) { _btnPrevious.onClick.RemoveListener(OnPreviousClicked); _btnPrevious.onClick.AddListener(OnPreviousClicked); Debug.Log("UIManager: _btnPrevious 绑定成功"); }
|
||||
if (_btnNext != null) { _btnNext.onClick.RemoveListener(OnNextClicked); _btnNext.onClick.AddListener(OnNextClicked); Debug.Log("UIManager: _btnNext 绑定成功"); }
|
||||
if (_btnSwitchMode != null) { _btnSwitchMode.onClick.RemoveListener(OnSwitchModeClicked); _btnSwitchMode.onClick.AddListener(OnSwitchModeClicked); Debug.Log("UIManager: _btnSwitchMode 绑定成功"); }
|
||||
if (_btnReturn != null) { _btnReturn.onClick.RemoveListener(OnReturnClicked); _btnReturn.onClick.AddListener(OnReturnClicked); Debug.Log("UIManager: _btnReturn 绑定成功"); }
|
||||
|
||||
// 注意: _btnLimbFixation/_btnSplintFixation 不再在这里绑定 SwitchFixation,
|
||||
// 固定方式选择由 PageController 的 OnFixationSelected 统一处理,避免双重触发污染 StepManager 状态。
|
||||
@@ -247,12 +247,12 @@ public class UIManager : MonoBehaviour
|
||||
private void UpdateNavigationButtons()
|
||||
{
|
||||
if (_stepManager == null) return;
|
||||
if (_btnPrevious != null) _btnPrevious.interactable = _stepManager.CurrentStepIndex > 0;
|
||||
|
||||
if (_btnPrevious != null) _btnPrevious.interactable = true;
|
||||
if (_btnNext != null)
|
||||
{
|
||||
var label = _btnNext.GetComponentInChildren<Text>();
|
||||
bool isLast = _stepManager.CurrentStepIndex >= _stepManager.TotalSteps - 1;
|
||||
if (label != null) label.text = isLast ? "完成" : "下一步";
|
||||
if (label != null) label.text = "下一步";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,15 +299,15 @@ public class UIManager : MonoBehaviour
|
||||
Debug.Log($"UIManager: [ToggleFullScreen] 全屏 = {Screen.fullScreen}");
|
||||
}
|
||||
|
||||
private void OnPreviousClicked()
|
||||
{
|
||||
Debug.Log("UIManager: [OnPreviousClicked] -> 上一步");
|
||||
_stepManager?.PreviousStep();
|
||||
}
|
||||
|
||||
private void OnNextClicked()
|
||||
{
|
||||
Debug.Log("UIManager: [OnNextClicked] -> 下一步");
|
||||
Debug.LogError("====== UIManager: 点击了【下一步】按钮! ======");
|
||||
if (_stepManager == null)
|
||||
{
|
||||
_stepManager = FindObjectOfType<StepManager>();
|
||||
Debug.LogError($"====== UIManager: 重新寻找 StepManager 结果: {_stepManager != null} ======");
|
||||
}
|
||||
|
||||
if (_stepManager != null && _stepManager.Mode == ExperimentMode.Exam)
|
||||
{
|
||||
var step = _stepManager.GetCurrentStep();
|
||||
@@ -317,6 +317,13 @@ public class UIManager : MonoBehaviour
|
||||
_stepManager?.NextStep();
|
||||
}
|
||||
|
||||
private void OnPreviousClicked()
|
||||
{
|
||||
Debug.LogError("====== UIManager: 点击了【上一步】按钮! ======");
|
||||
if (_stepManager == null) _stepManager = FindObjectOfType<StepManager>();
|
||||
_stepManager?.PreviousStep();
|
||||
}
|
||||
|
||||
private void OnSwitchModeClicked()
|
||||
{
|
||||
if (_stepManager == null) return;
|
||||
|
||||
Reference in New Issue
Block a user