Fix full flow reset cleanup for outlines and residual objects, adjust EndPanel buttons

This commit is contained in:
yangbear
2026-07-12 12:17:06 +08:00
parent a6bda2b296
commit e7eb9a9cbc
8 changed files with 312 additions and 66 deletions
+53 -1
View File
@@ -19,6 +19,10 @@ public class AudioManager : MonoBehaviour
private AudioSource _bgSource;
private AudioSource _sfxSource;
private AudioSource _voiceSource;
private Coroutine _cameraSwitchRoutine;
private Vector3 _camera3InitialPosition;
private Quaternion _camera3InitialRotation;
private bool _hasCamera3InitialTransform;
void Awake()
{
@@ -42,6 +46,7 @@ public class AudioManager : MonoBehaviour
if (_basketballSfx == null) _basketballSfx = UnityEditor.AssetDatabase.LoadAssetAtPath<AudioClip>("Assets/Audio/start/basketballSound.wav");
if (_manCallSfx == null) _manCallSfx = UnityEditor.AssetDatabase.LoadAssetAtPath<AudioClip>("Assets/Audio/start/mancall.wav");
#endif
CacheCamera3InitialTransform();
Debug.Log($"[AudioManager] 就绪 homeBg={_homeBg!=null} expBg={_experimentBg!=null} sfx={_basketballSfx!=null}/{_manCallSfx!=null}");
}
@@ -49,6 +54,7 @@ public class AudioManager : MonoBehaviour
public void PlayHomeBg()
{
if (_homeBg == null) return;
if (_bgSource.clip == _homeBg && _bgSource.isPlaying) return;
_bgSource.clip = _homeBg;
_bgSource.volume = BgVolume;
_bgSource.Play();
@@ -58,6 +64,7 @@ public class AudioManager : MonoBehaviour
public void PlayExperimentBg()
{
if (_experimentBg == null) return;
if (_bgSource.clip == _experimentBg && _bgSource.isPlaying) return;
_bgSource.clip = _experimentBg;
_bgSource.volume = BgVolume;
_bgSource.Play();
@@ -99,7 +106,51 @@ public class AudioManager : MonoBehaviour
public void SwitchToCamera3()
{
StartCoroutine(SmoothSwitchCameraCoroutine());
CacheCamera3InitialTransform();
if (_cameraSwitchRoutine != null) StopCoroutine(_cameraSwitchRoutine);
_cameraSwitchRoutine = StartCoroutine(SmoothSwitchCameraCoroutine());
}
public void ResetCamerasToHome()
{
if (_cameraSwitchRoutine != null)
{
StopCoroutine(_cameraSwitchRoutine);
_cameraSwitchRoutine = null;
}
var mainCam = GameObject.Find("Main Camera");
var cam2 = GameObject.Find("camera2");
var cam3 = GameObject.Find("camera3");
SetCameraEnabled(mainCam, true);
SetCameraEnabled(cam2, false);
SetCameraEnabled(cam3, false);
if (cam3 != null && _hasCamera3InitialTransform)
{
cam3.transform.position = _camera3InitialPosition;
cam3.transform.rotation = _camera3InitialRotation;
}
Debug.Log("[AudioManager] 已恢复首页相机状态");
}
private void CacheCamera3InitialTransform()
{
if (_hasCamera3InitialTransform) return;
var cam3 = GameObject.Find("camera3");
if (cam3 == null) return;
_camera3InitialPosition = cam3.transform.position;
_camera3InitialRotation = cam3.transform.rotation;
_hasCamera3InitialTransform = true;
}
private void SetCameraEnabled(GameObject cameraObject, bool enabled)
{
if (cameraObject == null) return;
var camera = cameraObject.GetComponent<Camera>();
if (camera != null) camera.enabled = enabled;
}
private System.Collections.IEnumerator SmoothSwitchCameraCoroutine()
@@ -148,6 +199,7 @@ float duration = 1.0f;
cam3.transform.position = targetPos;
cam3.transform.rotation = targetRot;
_cameraSwitchRoutine = null;
Debug.Log("[AudioManager] camera2→camera3 平滑过渡完成");
}
+7 -1
View File
@@ -22,6 +22,12 @@ public static class ActionHistory
public static Stack<HistoryItem> History = new Stack<HistoryItem>();
public static void Clear()
{
History.Clear();
Debug.Log("[ActionHistory] 已清空历史记录");
}
public static void Push(ActionType type, MonoBehaviour handler, int extra = 0)
{
History.Push(new HistoryItem { Type = type, Handler = handler, ExtraInt = extra });
@@ -102,4 +108,4 @@ public static class ActionHistory
return true;
}
}
}
@@ -120,14 +120,28 @@ public class ChanraoTuibuHandler : MonoBehaviour
}
public void ResetState()
{
ResetState(true);
}
public void ResetState(bool startBlink)
{
_isFinished = false;
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;
if (_outlineMesh != null) _outlineMesh.enabled = startBlink;
StopAllCoroutines();
StartCoroutine(BlinkOutline());
if (startBlink && isActiveAndEnabled)
{
StartCoroutine(BlinkOutline());
}
}
public void ForceHideOutline()
{
StopAllCoroutines();
if (_outlineMesh != null) _outlineMesh.enabled = false;
}
}
+21 -7
View File
@@ -188,9 +188,8 @@ public class PingClickHandler : MonoBehaviour
remaining++;
}
if (remaining <= 1)
if (remaining == 0)
{
// 彻底修复:使用静态标志位锁定播放动作,这一轮绝不会播两次
if (!_hasPlayedFinishVoice)
{
_hasPlayedFinishVoice = true;
@@ -208,7 +207,7 @@ public class PingClickHandler : MonoBehaviour
if (uiMgr != null)
{
uiMgr.HideVideo();
uiMgr.ShowEndPanel(); // 调用显示结束面板
uiMgr.ShowEndPanel();
}
}
}
@@ -250,7 +249,6 @@ public class PingClickHandler : MonoBehaviour
_isBlinking = true;
_enableFrame = Time.frameCount;
// 撤销的时候,把语音锁打开,以便能够重新播放
_hasPlayedFinishVoice = false;
if (_outlineMesh != null) _outlineMesh.enabled = true;
@@ -282,17 +280,33 @@ public class PingClickHandler : MonoBehaviour
}
public void ResetState()
{
ResetState(true);
}
public void ResetState(bool startBlink)
{
_modelShown = false;
_isBlinking = true;
_isBlinking = startBlink;
_enableFrame = Time.frameCount;
_hasPlayedFinishVoice = false;
if (_modelRenderer != null) _modelRenderer.enabled = false;
if (chanraoObject != null) chanraoObject.SetActive(false);
foreach (var r in GetComponentsInChildren<Renderer>(true)) { if (_outlineMesh != null && r == _outlineMesh) continue; r.enabled = false; }
if (_outlineMesh != null) _outlineMesh.enabled = true;
if (_outlineMesh != null) _outlineMesh.enabled = startBlink;
StopAllCoroutines();
StartCoroutine(BlinkOutline());
if (startBlink && isActiveAndEnabled)
{
StartCoroutine(BlinkOutline());
}
}
public void ForceHideOutline()
{
_isBlinking = false;
StopAllCoroutines();
if (_outlineMesh != null) _outlineMesh.enabled = false;
}
}
@@ -10,6 +10,12 @@ public class Point2ClickHandler : MonoBehaviour
private MeshRenderer _meshRenderer;
private void OnDisable()
{
StopAllCoroutines();
if (_meshRenderer != null) _meshRenderer.enabled = false;
}
void OnEnable()
{
if (GetComponent<Collider>() == null) gameObject.AddComponent<SphereCollider>().radius = 0.5f;
@@ -92,9 +98,14 @@ public class Point2ClickHandler : MonoBehaviour
}
public void ResetState()
{
ResetState(true);
}
public void ResetState(bool startBlink)
{
_clicked = false;
if (_meshRenderer != null) _meshRenderer.enabled = true;
if (_meshRenderer != null) _meshRenderer.enabled = startBlink;
if (nextObject != null)
{
@@ -113,8 +124,11 @@ public class Point2ClickHandler : MonoBehaviour
}
}
gameObject.SetActive(true);
StopAllCoroutines();
StartCoroutine(BlinkOutline());
gameObject.SetActive(startBlink);
if (startBlink && isActiveAndEnabled)
{
StartCoroutine(BlinkOutline());
}
}
}
+16 -13
View File
@@ -30,7 +30,6 @@ public class ShoseClickHandler : MonoBehaviour
var sc = GetComponent<SphereCollider>(); if(sc != null) sc.enabled = false;
var col = GetComponent<BoxCollider>();
if (col == null) col = gameObject.AddComponent<BoxCollider>();
// Unity 的 BoxCollider.size 处于本地坐标空间,会自动乘上 localScale,这里不能重复缩放
col.center = mb.center;
col.size = mb.size * 1.2f;
col.enabled = true;
@@ -104,7 +103,6 @@ public class ShoseClickHandler : MonoBehaviour
anim.CrossFade("womanCrouch", 0.1f);
}
// 移动 woman 到 point3
var point3 = GameObject.Find("womanPointArray/point3");
if (point3 != null)
{
@@ -121,13 +119,10 @@ public class ShoseClickHandler : MonoBehaviour
{
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)
{
@@ -138,7 +133,6 @@ public class ShoseClickHandler : MonoBehaviour
}
}
// 显示 VideoPanel 并播放视频
var uiMgr = FindObjectOfType<UIManager>();
if (uiMgr != null)
{
@@ -147,11 +141,15 @@ public class ShoseClickHandler : MonoBehaviour
}
public void ResetState()
{
ResetState(true);
}
public void ResetState(bool startBlink)
{
_clicked = false;
// 【关键修复】确保 OutlineMesh 在重新激活时处于开启状态并正常闪烁
if (_outlineMesh != null) _outlineMesh.enabled = true;
if (_outlineMesh != null) _outlineMesh.enabled = startBlink;
if (_hasRecordedInitialTransform)
{
@@ -159,7 +157,6 @@ public class ShoseClickHandler : MonoBehaviour
transform.rotation = _initialRotation;
}
// 返回时要隐藏 ping
if (nextObject != null) nextObject.SetActive(false);
var uiMgr = FindObjectOfType<UIManager>();
@@ -168,7 +165,6 @@ public class ShoseClickHandler : MonoBehaviour
uiMgr.HideVideo();
}
// 把 woman 重置(取消蹲下)
var woman = GameObject.Find("woman");
if (woman != null)
{
@@ -185,7 +181,6 @@ public class ShoseClickHandler : MonoBehaviour
}
}
// 取消男伤员的 CheckFoot 动作
var manInjury = GameObject.Find("manInjury");
if (manInjury != null)
{
@@ -196,7 +191,6 @@ public class ShoseClickHandler : MonoBehaviour
}
}
// 重新显示 touchArray/point3 的边框
var touchArray = GameObject.Find("touchArray");
if (touchArray != null)
{
@@ -208,7 +202,16 @@ public class ShoseClickHandler : MonoBehaviour
}
StopAllCoroutines();
StartCoroutine(BlinkOutline());
if (startBlink && isActiveAndEnabled)
{
StartCoroutine(BlinkOutline());
}
}
public void ForceHideOutline()
{
StopAllCoroutines();
if (_outlineMesh != null) _outlineMesh.enabled = false;
}
private System.Collections.IEnumerator ResetCheckFoot(Animator anim)
+143 -35
View File
@@ -44,6 +44,7 @@ public class PageController : MonoBehaviour
private Page _currentPage = Page.Home;
private ExperimentMode _selectedMode = ExperimentMode.Learn;
private int _lastReturnHomeFrame = -1;
private void Start()
{
@@ -114,6 +115,13 @@ public class PageController : MonoBehaviour
if (page == Page.ModeSelect && _modeDescText != null)
_modeDescText.text = "";
var audioManager = FindObjectOfType<AudioManager>();
if (audioManager != null)
{
if (page == Page.Experiment) audioManager.PlayExperimentBg();
else audioManager.PlayHomeBg();
}
Debug.Log($"PageController: 页面切换 -> [{page}]");
}
@@ -198,40 +206,64 @@ public class PageController : MonoBehaviour
public void ReturnToHome()
{
Debug.Log("PageController: 返回首页");
if (_lastReturnHomeFrame == Time.frameCount)
{
Debug.Log("PageController: 返回首页已在本帧执行,跳过重复调用");
return;
}
_lastReturnHomeFrame = Time.frameCount;
Debug.Log("PageController: 返回首页,开始完整复位流程");
StopAllCoroutines();
ActionHistory.Clear();
if (_uiManager != null)
{
_uiManager.HideVideo();
_uiManager.HideEndPanel();
}
HideCanvasPanel("VideoPanel");
HideCanvasPanel("EndPanel");
HideCanvasPanel("DialogueBubble");
HideCanvasPanel("GradeResult");
HideCanvasPanel("TipPanel");
// --- 核心修复:退出时彻底清理并隐藏所有交互物体 ---
var manInjury = GameObject.Find("manInjury");
if (manInjury != null)
foreach (var handler in GetSceneObjectsOfType<PingClickHandler>())
{
foreach (var handler in manInjury.GetComponentsInChildren<PingClickHandler>(true))
{
handler.ResetState();
handler.gameObject.SetActive(false); // 强制隐藏 ping
}
foreach (var handler in manInjury.GetComponentsInChildren<ChanraoTuibuHandler>(true))
{
handler.ResetState();
handler.gameObject.SetActive(false); // 强制隐藏 chanraotuibu
}
// 这里也可以顺带隐藏所有的 man_ba_zi_chan_rao 下的 chanrao
handler.ResetState(false);
handler.ForceHideOutline();
handler.gameObject.SetActive(false);
Debug.Log($"PageController: 已隐藏 Ping {handler.name}");
}
foreach (var handler in GetSceneObjectsOfType<ChanraoTuibuHandler>())
{
handler.ResetState(false);
handler.ForceHideOutline();
handler.gameObject.SetActive(false);
Debug.Log($"PageController: 已隐藏 ChanraoTuibu {handler.name}");
}
HideSceneObjectsByNamePrefix("chanrao");
// 找到 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; } }
}
var shose = FindSceneObject("Shose");
if (shose != null)
{
var h = shose.GetComponent<ShoseClickHandler>();
if (h != null) h.ResetState();
if (h != null)
{
h.ResetState(false);
h.ForceHideOutline();
}
// 关键:返回主页时,必须禁用点击组件,并隐藏物体
if (h != null) h.enabled = false;
shose.SetActive(false);
Debug.Log("PageController: 已隐藏 Shose");
}
// 把 touchArray 里的点也关掉
@@ -240,12 +272,84 @@ public class PageController : MonoBehaviour
{
foreach (Transform t in touchArray.transform)
{
var point2Handler = t.GetComponent<Point2ClickHandler>();
if (point2Handler != null) point2Handler.ResetState(false);
t.gameObject.SetActive(false);
}
}
var woman = GameObject.Find("woman");
if (woman != null)
{
var anim = woman.GetComponent<Animator>();
if (anim != null)
{
anim.SetBool("IsCheck", false);
anim.SetBool("isCrouch", false);
}
}
var manInjury = GameObject.Find("manInjury");
if (manInjury != null)
{
var anim = manInjury.GetComponent<Animator>();
if (anim != null) anim.SetBool("CheckFoot", false);
}
var audioManager = FindObjectOfType<AudioManager>();
if (audioManager != null)
{
audioManager.StopVoice();
audioManager.StopSfx();
audioManager.ResetCamerasToHome();
}
// ------------------------------------------------
ShowPage(Page.Home);
Debug.Log("PageController: 返回首页复位完成");
}
private void HideCanvasPanel(string panelName)
{
var panel = FindSceneObject(panelName);
if (panel != null) panel.SetActive(false);
}
private GameObject FindSceneObject(string objectName)
{
var found = GameObject.Find(objectName);
if (found != null) return found;
foreach (var t in Resources.FindObjectsOfTypeAll<Transform>())
{
if (t == null || !t.gameObject.scene.IsValid()) continue;
if (t.name == objectName) return t.gameObject;
}
return null;
}
private T[] GetSceneObjectsOfType<T>() where T : Component
{
var all = Resources.FindObjectsOfTypeAll<T>();
var list = new System.Collections.Generic.List<T>();
foreach (var item in all)
{
if (item == null || !item.gameObject.scene.IsValid()) continue;
list.Add(item);
}
return list.ToArray();
}
private void HideSceneObjectsByNamePrefix(string prefix)
{
foreach (var t in Resources.FindObjectsOfTypeAll<Transform>())
{
if (t == null || !t.gameObject.scene.IsValid()) continue;
if (!t.name.StartsWith(prefix, System.StringComparison.OrdinalIgnoreCase)) continue;
t.gameObject.SetActive(false);
Debug.Log($"PageController: 已隐藏残留对象 {t.name}");
}
}
private System.Collections.IEnumerator StartInitialVoiceAndCameraRoutine()
@@ -264,11 +368,7 @@ public class PageController : MonoBehaviour
}
// 找鞋子
var shose = GameObject.Find("Shose");
if (shose == null)
{
foreach(var t in allT) { if (t.parent == null && t.name == "Shose") { shose = t.gameObject; break; } }
}
var shose = FindSceneObject("Shose");
var point2 = GameObject.Find("touchArray/point2");
if (point2 == null)
@@ -287,14 +387,7 @@ public class PageController : MonoBehaviour
if (h == null) h = shose.AddComponent<ShoseClickHandler>();
h.targetPoint = GameObject.Find("ShosePoint")?.transform;
// 这里因为你换了绑带模型:我们需要确保拿到正确的 ping.004。
// 只要场景里激活/存在叫 ping.004 的就行
var ping004 = GameObject.Find("ping.004");
if (ping004 == null)
{
// 如果是隐藏状态,用全局查找
foreach(var t in allT) { if (t.name == "ping.004") { ping004 = t.gameObject; break; } }
}
var ping004 = FindFirstPingStepObject();
h.nextObject = ping004;
// 鞋子出现,但默认不开组件(不可点击)
@@ -350,13 +443,28 @@ public class PageController : MonoBehaviour
private void HideShose()
{
var shose = GameObject.Find("Shose");
var shose = FindSceneObject("Shose");
if (shose != null)
{
shose.SetActive(false);
}
}
private GameObject FindFirstPingStepObject()
{
foreach (var ping in GetSceneObjectsOfType<PingClickHandler>())
{
if (ping != null && ping.name == "ping.004") return ping.gameObject;
}
foreach (var ping in GetSceneObjectsOfType<PingClickHandler>())
{
if (ping != null && ping.pingIndex == 0) return ping.gameObject;
}
return null;
}
#endregion
}
+39 -4
View File
@@ -41,6 +41,7 @@ public class UIManager : MonoBehaviour
private void Start()
{
Debug.Log("=== UIManager 初始化开始 ===");
AutoFindPanels();
HideVideo();
if (_endPanel != null) _endPanel.SetActive(false);
@@ -56,6 +57,24 @@ public class UIManager : MonoBehaviour
Debug.Log("=== UIManager 初始化完成 ===");
}
private void AutoFindPanels()
{
var canvas = GameObject.Find("Canvas")?.transform;
if (canvas == null) return;
if (_videoPanel == null)
{
var t = canvas.Find("VideoPanel");
if (t != null) _videoPanel = t.gameObject;
}
if (_endPanel == null)
{
var t = canvas.Find("EndPanel");
if (t != null) _endPanel = t.gameObject;
}
}
private void BindNavbarButtons()
{
var experimentPanel = GameObject.Find("ExperimentPanel");
@@ -146,26 +165,43 @@ public class UIManager : MonoBehaviour
public void ShowVideo(string fileName)
{
AutoFindPanels();
if (_videoPanel != null)
{
_videoPanel.SetActive(true);
var vp = _videoPanel.GetComponentInChildren<UnityEngine.Video.VideoPlayer>(true);
if (vp != null) vp.Play();
Debug.Log("UIManager: 显示并播放 VideoPanel");
}
}
public void HideVideo()
{
AutoFindPanels();
if (_videoPanel != null)
{
var vp = _videoPanel.GetComponentInChildren<UnityEngine.Video.VideoPlayer>(true);
if (vp != null) vp.Stop();
_videoPanel.SetActive(false);
Debug.Log("UIManager: 隐藏 VideoPanel");
}
}
public void HideEndPanel()
{
AutoFindPanels();
if (_endPanel == null)
{
var t = GameObject.Find("Canvas")?.transform.Find("EndPanel");
if (t != null) _endPanel = t.gameObject;
}
if (_endPanel != null) _endPanel.SetActive(false);
}
public void ShowEndPanel()
{
AutoFindPanels();
if (_endPanel == null)
{
var t = GameObject.Find("Canvas")?.transform.Find("EndPanel");
@@ -176,12 +212,12 @@ public class UIManager : MonoBehaviour
{
_endPanel.SetActive(true);
// 绑定 BtnYes关闭 EndPanel
// 绑定 BtnYes结束后返回首页
var btnYes = _endPanel.transform.Find("BtnYes")?.GetComponent<Button>();
if (btnYes != null)
{
btnYes.onClick.RemoveAllListeners();
btnYes.onClick.AddListener(() => { _endPanel.SetActive(false); });
btnYes.onClick.AddListener(OnReturnClicked);
}
// 绑定 BtnBack:关闭 EndPanel(如果你希望点它也是单纯关面板而不是退回到首页的话)
@@ -270,8 +306,7 @@ public class UIManager : MonoBehaviour
private void OnReturnClicked()
{
// 先隐藏面板
if (_endPanel != null) _endPanel.SetActive(false);
HideEndPanel();
var pageCtrl = FindObjectOfType<PageController>();
if (pageCtrl != null) pageCtrl.ReturnToHome();