diff --git a/Assets/Scripts/Core/StepManager.cs b/Assets/Scripts/Core/StepManager.cs index ec5f0f6..bcf7d9d 100644 --- a/Assets/Scripts/Core/StepManager.cs +++ b/Assets/Scripts/Core/StepManager.cs @@ -125,15 +125,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); } - public void PreviousStep() + private void PingStepPrevious() + { + if (_pingStepIndex <= 0) return; + int idx = (_pingStepIndex - 1) / 2; // ping index (0-3, mapping to 004-001) + bool wasSecond = (_pingStepIndex - 1) % 2 == 1; // true if was second click + + if (wasSecond) + { + // Undo second click: hide chanrao, restore ping to first-click state + if (_chanraoObjects != null && idx < _chanraoObjects.Length && _chanraoObjects[idx] != null) + _chanraoObjects[idx].SetActive(false); + if (_pingObjects != null && idx < _pingObjects.Length && _pingObjects[idx] != null) + { + var h = _pingObjects[idx].GetComponent(); + if (h != null) h.UndoSecondClick(); + } + } + else + { + // Undo first click: hide current ping, show previous ping (if any) + if (_pingObjects != null && idx < _pingObjects.Length && _pingObjects[idx] != null) + { + var h = _pingObjects[idx].GetComponent(); + if (h != null) h.UndoShow(); + } + // 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}"); + OnStepChanged?.Invoke(_pingStepIndex); + } + + private void PingStepNext() + { + if (_pingStepIndex >= _pingMaxStep) return; + int idx = _pingStepIndex / 2; + bool isSecond = _pingStepIndex % 2 == 1; + + if (!isSecond) + { + // Advance to first click: ensure ping is visible and in first-click state + if (_pingObjects != null && idx < _pingObjects.Length && _pingObjects[idx] != null) + { + var ping = _pingObjects[idx]; + ping.SetActive(true); + var h = ping.GetComponent(); + if (h != null) + { + h.UndoSecondClick(); // Reset to first-click state + // Trigger first click via reflection/method + } + } + } + else + { + // Advance to second click: show chanrao + if (_chanraoObjects != null && idx < _chanraoObjects.Length && _chanraoObjects[idx] != null) + _chanraoObjects[idx].SetActive(true); + if (_pingObjects != null && idx < _pingObjects.Length && _pingObjects[idx] != null) + { + var h = _pingObjects[idx].GetComponent(); + if (h != null) h.UndoShow(); + } + // Show next ping for first click + if (idx + 1 < 4 && _pingObjects != null && _pingObjects[idx+1] != null) + _pingObjects[idx+1].SetActive(true); + } + _pingStepIndex++; + Debug.Log($"StepManager: Ping前进 -> {_pingStepIndex}"); + OnStepChanged?.Invoke(_pingStepIndex); + } + + public void RecordPingStep(int pingIndex, bool secondClick) + { + _pingStepIndex = pingIndex * 2 + (secondClick ? 2 : 1); + Debug.Log($"StepManager: Ping步进 -> {_pingStepIndex} (ping{pingIndex} second={secondClick})"); + OnStepChanged?.Invoke(_pingStepIndex); + } + + public 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; @@ -201,6 +284,25 @@ public class StepManager : MonoBehaviour Debug.Log($"StepManager: Woman步进已初始化, 共{_womanMaxStep}个位置"); } + public void InitPingSteps() + { + var mi = GameObject.Find("manInjury"); + if (mi == null) return; + _pingObjects = new GameObject[4]; + _chanraoObjects = new GameObject[4]; + for (int i = 1; i <= 4; i++) + { + 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; + } + _pingStepIndex = 0; + Debug.Log("StepManager: Ping步进已初始化"); + } + public void RecordWomanStep(int newStepIndex) { _womanStepIndex = Mathf.Clamp(newStepIndex, 0, _womanMaxStep); Debug.Log($"StepManager: Woman步进 -> {_womanStepIndex}"); OnStepChanged?.Invoke(_womanStepIndex); } private void WomanStepPrevious() @@ -267,6 +369,10 @@ public class StepManager : MonoBehaviour private int _womanMaxStep = 3; private Transform[] _womanPositions; private GameObject[] _touchPoints; + private GameObject[] _pingObjects; + private GameObject[] _chanraoObjects; + private int _pingStepIndex = 0; + private int _pingMaxStep = 8; // 4 pings × 2 clicks each private Vector3 _womanInitPos; diff --git a/Assets/Scripts/Interaction/PingClickHandler.cs b/Assets/Scripts/Interaction/PingClickHandler.cs index f0ec818..431afd1 100644 --- a/Assets/Scripts/Interaction/PingClickHandler.cs +++ b/Assets/Scripts/Interaction/PingClickHandler.cs @@ -1,20 +1,26 @@ using UnityEngine; /// -/// 挂在每个 ping 对象上。带蓝色闪烁描边,点击后显示对应 chanrao 并隐藏自己。 -/// 从 ping.004 开始作为第一个(reverse order)。 +/// 挂在每个 ping 对象上。蓝色圆环闪烁描边,两步点击: +/// 1. 第一次点击 → 停止闪烁,显示 ping 模型 +/// 2. 第二次点击 → 显示对应 chanrao,隐藏自己,激活下个 ping +/// 点击通知 StepManager 记录步骤。 /// public class PingClickHandler : MonoBehaviour { public GameObject chanraoObject; public GameObject nextPing; + public int pingIndex; // ping.004=0, ping.003=1, ping.002=2, ping.001=3 private MeshRenderer _meshRenderer; + private LineRenderer _outlineRenderer; + private bool _clickedOnce; void Start() { _meshRenderer = GetComponent(); if (_meshRenderer == null) _meshRenderer = GetComponentInChildren(); + CreateBlueOutline(); StartCoroutine(BlinkOutline()); } @@ -22,39 +28,102 @@ public class PingClickHandler : MonoBehaviour { _meshRenderer = GetComponent(); if (_meshRenderer == null) _meshRenderer = GetComponentInChildren(); - if (_meshRenderer != null) _meshRenderer.enabled = true; + if (_meshRenderer != null) _meshRenderer.enabled = false; // 初始隐藏模型,只留描边 + CreateBlueOutline(); + _clickedOnce = false; StopAllCoroutines(); StartCoroutine(BlinkOutline()); } + void CreateBlueOutline() + { + if (_outlineRenderer != null) return; + var ringGo = new GameObject("BlueRing"); + ringGo.transform.SetParent(transform, false); + ringGo.transform.localPosition = Vector3.zero; + ringGo.transform.localRotation = Quaternion.Euler(90f, 0, 0); + + _outlineRenderer = ringGo.AddComponent(); + _outlineRenderer.useWorldSpace = false; + _outlineRenderer.loop = true; + _outlineRenderer.startWidth = 0.04f; + _outlineRenderer.endWidth = 0.04f; + _outlineRenderer.material = new Material(Shader.Find("Unlit/Color")); + _outlineRenderer.material.color = new Color(0.2f, 0.5f, 1f); + _outlineRenderer.startColor = new Color(0.2f, 0.5f, 1f); + _outlineRenderer.endColor = new Color(0.2f, 0.5f, 1f); + + float r = 0.25f; + int segs = 32; + _outlineRenderer.positionCount = segs; + for (int i = 0; i < segs; i++) + { + float a = i * 2f * Mathf.PI / segs; + _outlineRenderer.SetPosition(i, new Vector3(Mathf.Cos(a) * r, Mathf.Sin(a) * r, 0)); + } + } + System.Collections.IEnumerator BlinkOutline() { while (true) { - if (_meshRenderer != null) - _meshRenderer.enabled = !_meshRenderer.enabled; + if (_outlineRenderer != null) + _outlineRenderer.enabled = !_outlineRenderer.enabled; yield return new WaitForSeconds(0.4f); } } void OnMouseDown() { - Debug.Log($"[PingClick] {name} 被点击"); - - if (chanraoObject != null) + if (!_clickedOnce) { - chanraoObject.SetActive(true); - Debug.Log($"[PingClick] {chanraoObject.name} 已显示"); + // 第一次点击:停止闪烁,显示模型 + _clickedOnce = true; + StopAllCoroutines(); + if (_meshRenderer != null) _meshRenderer.enabled = true; + if (_outlineRenderer != null) _outlineRenderer.enabled = true; + Debug.Log($"[PingClick] {name} 第一次点击,模型显示"); + } + else + { + // 第二次点击:显示 chanrao,隐藏自己,激活下个 ping + if (chanraoObject != null) + { + chanraoObject.SetActive(true); + Debug.Log($"[PingClick] {chanraoObject.name} 已显示"); + } + + StopAllCoroutines(); + if (_meshRenderer != null) _meshRenderer.enabled = false; + if (_outlineRenderer != null) _outlineRenderer.enabled = false; + gameObject.SetActive(false); + + if (nextPing != null) + { + nextPing.SetActive(true); + Debug.Log($"[PingClick] {name} → {nextPing.name} 已激活"); + } } - StopAllCoroutines(); + // 通知 StepManager + var sm = FindObjectOfType(); + if (sm != null) sm.RecordPingStep(pingIndex, _clickedOnce); + } + + /// 上一步撤销:恢复到first-click状态 + public void UndoSecondClick() + { + if (!_clickedOnce) return; + _clickedOnce = false; + gameObject.SetActive(true); if (_meshRenderer != null) _meshRenderer.enabled = false; - gameObject.SetActive(false); + StopAllCoroutines(); + StartCoroutine(BlinkOutline()); + } - if (nextPing != null) - { - nextPing.SetActive(true); - Debug.Log($"[PingClick] {name} → {nextPing.name} 已激活"); - } + /// 上一步撤销:隐藏自己,恢复前一个ping + public void UndoShow() + { + gameObject.SetActive(false); } }