diff --git a/unity/Assets/2.5D Engine/Player.prefab b/unity/Assets/2.5D Engine/Player.prefab index c13ff92..af4f893 100644 --- a/unity/Assets/2.5D Engine/Player.prefab +++ b/unity/Assets/2.5D Engine/Player.prefab @@ -348,7 +348,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: f9e36f6d2e0ea0541bad29e784e0841b, type: 3} m_Name: m_EditorClassIdentifier: - maxHealth: 100 + maxHealth: 5 deathEffect: {fileID: 4806121257990350900, guid: f19c76183b5e22e44a73655dc18f1a92, type: 3} isPlayer: 1 --- !u!114 &4071397948156435190 diff --git a/unity/Assets/2.5D Engine/Scripts/HealthSystem.cs b/unity/Assets/2.5D Engine/Scripts/HealthSystem.cs index 52f97a5..52cba1f 100644 --- a/unity/Assets/2.5D Engine/Scripts/HealthSystem.cs +++ b/unity/Assets/2.5D Engine/Scripts/HealthSystem.cs @@ -16,6 +16,8 @@ namespace IndianOceanAssets.Engine2_5D [SerializeField] private bool isPlayer; // Is this the player? + private bool _isDead = false; // 防止重复触发死亡 + // Initializes health private void Start() { @@ -25,16 +27,25 @@ namespace IndianOceanAssets.Engine2_5D // Applies damage and checks for death public void Damage(int damageAmount) { + if (_isDead) return; // 已死亡,不再处理 + health -= damageAmount; - // If dead, trigger game over if player, then die + // If dead if (health <= 0) { + _isDead = true; + if (isPlayer) { + // 玩家死亡:触发过场动画,不立即销毁(由 GameManager 处理) GameManager.GameOver(); } - Die(); + else + { + // 非玩家:正常播放死亡特效并销毁 + Die(); + } } } diff --git a/unity/Assets/UI/DebugResultButtons.cs b/unity/Assets/UI/DebugResultButtons.cs index a402b8d..ad26989 100644 --- a/unity/Assets/UI/DebugResultButtons.cs +++ b/unity/Assets/UI/DebugResultButtons.cs @@ -1,6 +1,4 @@ using UnityEngine; -using UnityEngine.SceneManagement; -using UnityEngine.UI; namespace GameFramework { @@ -12,59 +10,6 @@ namespace GameFramework public class DebugResultButtons : MonoBehaviour { private string _debugLog = ""; - private GameResultScreen _resultScreen; - - // 转场效果 - private bool _isFading = false; - private float _fadeAlpha = 0f; - private float _fadeSpeed = 2f; // 淡出速度 - private string _pendingScene = ""; - private Texture2D _fadeTexture; - - private void Start() - { - // 查找场景中的 GameResultScreen - _resultScreen = FindObjectOfType(); - if (_resultScreen == null) - { - Debug.LogWarning("[DebugResultButtons] 场景中没有 GameResultScreen!"); - } - else - { - Debug.Log("[DebugResultButtons] 找到 GameResultScreen: " + _resultScreen.gameObject.name); - } - - // 检查 SceneLoader - if (SceneLoader.Instance == null) - { - Debug.LogWarning("[DebugResultButtons] SceneLoader.Instance 为 null,将使用 SceneManager 直接加载场景"); - } - - // 创建用于淡入淡出的纹理 - _fadeTexture = new Texture2D(1, 1); - _fadeTexture.SetPixel(0, 0, Color.black); - _fadeTexture.Apply(); - } - - private void Update() - { - // 处理淡出效果 - if (_isFading) - { - _fadeAlpha += Time.deltaTime * _fadeSpeed; - if (_fadeAlpha >= 1f) - { - _fadeAlpha = 1f; - // 淡出完成,加载场景 - if (!string.IsNullOrEmpty(_pendingScene)) - { - Debug.Log($"[DebugResultButtons] 淡出完成,加载场景: {_pendingScene}"); - SceneManager.LoadScene(_pendingScene); - } - _isFading = false; - } - } - } private void OnGUI() { @@ -75,98 +20,64 @@ namespace GameFramework debugStyle.fontStyle = FontStyle.Bold; // 调试信息背景 - GUI.Box(new Rect(5, 5, 500, 130), ""); + GUI.Box(new Rect(5, 5, 500, 85), ""); GUI.Label(new Rect(10, 10, 480, 25), $"GameManager.Instance: {(GameManager.Instance != null ? "【存在】" : "【null - 缺失!】")}", debugStyle); GUI.Label(new Rect(10, 35, 480, 25), $"GameState: {GameManager.GameState}", debugStyle); - string resultScreenStatus = _resultScreen != null ? "【已找到】" : "【未找到!】"; - GUI.Label(new Rect(10, 60, 480, 25), $"GameResultScreen: {resultScreenStatus}", debugStyle); - if (!string.IsNullOrEmpty(_debugLog)) { GUIStyle logStyle = new GUIStyle(GUI.skin.label); logStyle.fontSize = 16; - logStyle.normal.textColor = _debugLog.Contains("错误") ? Color.red : Color.green; + logStyle.normal.textColor = Color.green; logStyle.fontStyle = FontStyle.Bold; - GUI.Label(new Rect(10, 85, 580, 25), _debugLog, logStyle); + GUI.Label(new Rect(10, 60, 580, 25), _debugLog, logStyle); } - // 右上角显示两个按钮(淡出时隐藏) - if (!_isFading) + // 右上角显示两个按钮 + float x = Screen.width - 220; + float y = 10; + + GUIStyle style = new GUIStyle(GUI.skin.button); + style.fontSize = 20; + style.fontStyle = FontStyle.Bold; + + // 胜利按钮(绿色) + GUI.backgroundColor = new Color(0.2f, 0.8f, 0.2f, 0.9f); + if (GUI.Button(new Rect(x, y, 100, 50), "胜利", style)) { - float x = Screen.width - 220; - float y = 10; - - GUIStyle style = new GUIStyle(GUI.skin.button); - style.fontSize = 20; - style.fontStyle = FontStyle.Bold; - - // 胜利按钮(绿色) - GUI.backgroundColor = new Color(0.2f, 0.8f, 0.2f, 0.9f); - if (GUI.Button(new Rect(x, y, 100, 50), "胜利", style)) + if (GameManager.Instance == null) { - if (GameManager.Instance == null) - { - _debugLog = "[错误] GameManager.Instance 为 null!"; - Debug.LogError("[DebugResultButtons] GameManager.Instance 为 null!"); - } - else - { - _debugLog = $"[调用] Win() - 时间: {Time.time:F2}"; - Debug.Log("[DebugResultButtons] 调用 GameManager.Win()"); - GameManager.Win(); - - // 如果没有 ResultScreen,直接跳转结算场景 - if (_resultScreen == null) - { - StartFadeToScene("Scoring"); - } - } + _debugLog = "[错误] GameManager.Instance 为 null!"; + Debug.LogError("[DebugResultButtons] GameManager.Instance 为 null!"); } - - // 失败按钮(红色) - GUI.backgroundColor = new Color(0.8f, 0.2f, 0.2f, 0.9f); - if (GUI.Button(new Rect(x + 110, y, 100, 50), "失败", style)) + else { - if (GameManager.Instance == null) - { - _debugLog = "[错误] GameManager.Instance 为 null!"; - Debug.LogError("[DebugResultButtons] GameManager.Instance 为 null!"); - } - else - { - _debugLog = $"[调用] GameOver() - 时间: {Time.time:F2}"; - Debug.Log("[DebugResultButtons] 调用 GameManager.GameOver()"); - GameManager.GameOver(); - - // 如果没有 ResultScreen,直接跳转结算场景 - if (_resultScreen == null) - { - StartFadeToScene("Scoring"); - } - } + _debugLog = $"[调用] Win() - 时间: {Time.time:F2}"; + Debug.Log("[DebugResultButtons] 调用 GameManager.Win()"); + GameManager.Win(); } - - // 重置颜色 - GUI.backgroundColor = Color.white; } - - // 绘制淡出遮罩 - if (_isFading || _fadeAlpha > 0) + + // 失败按钮(红色) + GUI.backgroundColor = new Color(0.8f, 0.2f, 0.2f, 0.9f); + if (GUI.Button(new Rect(x + 110, y, 100, 50), "失败", style)) { - GUI.color = new Color(0, 0, 0, _fadeAlpha); - GUI.DrawTexture(new Rect(0, 0, Screen.width, Screen.height), _fadeTexture); - GUI.color = Color.white; + if (GameManager.Instance == null) + { + _debugLog = "[错误] GameManager.Instance 为 null!"; + Debug.LogError("[DebugResultButtons] GameManager.Instance 为 null!"); + } + else + { + _debugLog = $"[调用] GameOver() - 时间: {Time.time:F2}"; + Debug.Log("[DebugResultButtons] 调用 GameManager.GameOver()"); + GameManager.GameOver(); + } } - } - - private void StartFadeToScene(string sceneName) - { - _debugLog = $"[转场] 淡出到 {sceneName}..."; - _pendingScene = sceneName; - _isFading = true; - _fadeAlpha = 0f; + + // 重置颜色 + GUI.backgroundColor = Color.white; } } } diff --git a/unity/Assets/UI/GameManager.cs b/unity/Assets/UI/GameManager.cs index abf11fc..100ec36 100644 --- a/unity/Assets/UI/GameManager.cs +++ b/unity/Assets/UI/GameManager.cs @@ -1,5 +1,9 @@ using System; +using System.Collections; using UnityEngine; +using UnityEngine.SceneManagement; +using UnityEngine.UI; +using IndianOceanAssets.Engine2_5D; namespace GameFramework { @@ -23,20 +27,130 @@ namespace GameFramework [SerializeField] private GameState gameState = GameState.Playing; - /// 触发游戏失败:设状态 + 触发事件。 + [Header("过场动画")] + [Tooltip("光源扩大的持续时间(秒)")] + [SerializeField] private float lightExpandDuration = 1.5f; + + [Tooltip("光源扩大目标半径")] + [SerializeField] private float lightExpandTargetRadius = 20f; + + [Tooltip("淡出持续时间(秒)")] + [SerializeField] private float fadeOutDuration = 1f; + + /// 触发游戏失败:设状态 + 触发事件 + 播放过场动画。 public static void GameOver() { if (Instance == null) return; GameState = GameState.GameOver; onGameOver?.Invoke(); + Instance.StartCoroutine(Instance.DeathTransition()); } - /// 触发游戏胜利:设状态 + 触发事件。 + /// 触发游戏胜利:设状态 + 触发事件 + 播放过场动画。 public static void Win() { if (Instance == null) return; GameState = GameState.Victory; onGameWin?.Invoke(); + Instance.StartCoroutine(Instance.VictoryTransition()); + } + + /// + /// 死亡过场协程:禁用控制 → 光源放大 → 淡出 → 跳转 Scoring。 + /// 使用 unscaledDeltaTime 确保不受 timeScale 影响。 + /// + private IEnumerator DeathTransition() + { + // 1. 查找玩家并禁用控制 + var player = GameObject.FindGameObjectWithTag("Player"); + if (player != null) + { + // 禁用玩家移动 + var playerController = player.GetComponent(); + if (playerController != null) playerController.enabled = false; + + // 禁用相机跟随 + var camFollow = Camera.main != null ? Camera.main.GetComponent() : null; + if (camFollow != null) camFollow.enabled = false; + + // 2. 光源扩大 + var lightSource = player.GetComponent(); + if (lightSource != null) + { + float startRadius = lightSource.Radius; + float elapsed = 0f; + + while (elapsed < lightExpandDuration) + { + elapsed += Time.unscaledDeltaTime; + float t = Mathf.Clamp01(elapsed / lightExpandDuration); + float smoothT = t * t * (3f - 2f * t); // SmoothStep + lightSource.SetRadius(Mathf.Lerp(startRadius, lightExpandTargetRadius, smoothT)); + yield return null; + } + + lightSource.SetRadius(lightExpandTargetRadius); + } + else + { + // 没有光源,等待同样时间 + yield return new WaitForSecondsRealtime(lightExpandDuration); + } + } + else + { + // 没有玩家,等待同样时间 + yield return new WaitForSecondsRealtime(lightExpandDuration); + } + + // 3. 淡出到黑色 + yield return StartCoroutine(FadeOutAndLoad()); + } + + /// + /// 胜利过场协程(与死亡相同,可后续扩展不同效果)。 + /// + private IEnumerator VictoryTransition() + { + // 胜利也使用相同的光源扩散 + 淡出效果 + yield return DeathTransition(); + } + + /// + /// 创建全屏黑色遮罩并淡出,然后加载 Scoring 场景。 + /// + private IEnumerator FadeOutAndLoad() + { + // 创建全屏黑色遮罩 + var canvasObj = new GameObject("FadeOverlay"); + var canvas = canvasObj.AddComponent(); + canvas.renderMode = RenderMode.ScreenSpaceOverlay; + canvas.sortingOrder = 99999; + canvasObj.AddComponent(); + + var overlay = canvasObj.AddComponent(); + overlay.color = new Color(0, 0, 0, 0); + + // 淡出到黑色 + float elapsed = 0f; + while (elapsed < fadeOutDuration) + { + elapsed += Time.unscaledDeltaTime; + float t = Mathf.Clamp01(elapsed / fadeOutDuration); + overlay.color = new Color(0, 0, 0, t); + yield return null; + } + overlay.color = new Color(0, 0, 0, 1); + + // 加载 Scoring 场景 + if (SceneLoader.Instance != null) + { + SceneLoader.Instance.LoadScoringScene(); + } + else + { + SceneManager.LoadScene("Scoring"); + } } } diff --git a/unity/Assets/UI/GameResultScreen.cs b/unity/Assets/UI/GameResultScreen.cs index bdec1b1..f361f93 100644 --- a/unity/Assets/UI/GameResultScreen.cs +++ b/unity/Assets/UI/GameResultScreen.cs @@ -1,5 +1,6 @@ using UnityEngine; using UnityEngine.UI; +using UnityEngine.SceneManagement; namespace GameFramework { @@ -39,6 +40,23 @@ namespace GameFramework private bool _isVisible; + /// + /// 运行时初始化所有引用(由 GameManager 自动创建时调用)。 + /// + public void Initialize(Canvas canvas, GameObject win, Button winBtn, Text winTitle, Text winScore, + GameObject lose, Button loseBtn, Text loseTitle, Text loseScore) + { + resultCanvas = canvas; + winPanel = win; + winConfirmButton = winBtn; + winTitleText = winTitle; + winScoreText = winScore; + losePanel = lose; + loseConfirmButton = loseBtn; + loseTitleText = loseTitle; + loseScoreText = loseScore; + } + void OnEnable() { GameManager.onGameOver += OnGameOver; @@ -108,7 +126,14 @@ namespace GameFramework // 暂停游戏 if (TimeController.Instance != null) + { TimeController.Instance.Pause(); + } + else + { + // 没有 TimeController 时直接设置 timeScale + Time.timeScale = 0f; + } // 显示鼠标 Cursor.visible = true; @@ -132,11 +157,24 @@ namespace GameFramework // 解除暂停 if (TimeController.Instance != null) + { TimeController.Instance.Unpause(); + } + else + { + Time.timeScale = 1f; + } // 跳转到排行榜 if (SceneLoader.Instance != null) + { SceneLoader.Instance.LoadScoringScene(); + } + else + { + // 没有 SceneLoader 时直接加载场景 + SceneManager.LoadScene("Scoring"); + } } } }