using UnityEngine; using TMPro; using System; using UnityEngine.UI; public class InputManager : MonoBehaviour { public static InputManager Instance { get; private set; } public static event Action OnKeyPressed; public static event Action OnKeyReleased; private static readonly string[] TrackColors = { "red", "green", "yellow", "purple", "blue" }; [Header("Inspector")] public TextMeshProUGUI[] trackJudgeTexts = new TextMeshProUGUI[5]; [Header("Inspector")] public Text[] trackKeyTexts = new Text[5]; [Header("Lane Background Settings")] [Tooltip("The sprites for the 5 lanes (Red, Green, Yellow, Purple, Blue)")] public SpriteRenderer[] laneSprites = new SpriteRenderer[5]; [Tooltip("Alpha value when pressed (0-255)")] public float pressedAlpha = 30f; [Header("Inspector")] public bool showJudgeText = true; // Documentation text normalized. [Header("Inspector")] [Tooltip("Documentation text normalized.")] public Color keyActiveColor = Color.yellow; [Tooltip("Documentation text normalized.")] public Color keyInactiveColor = Color.black; // Documentation text normalized. public Color perfectColor = Color.yellow; public Color greatColor = Color.green; public Color goodColor = Color.cyan; public Color missColor = Color.red; private KeyCode[] cachedKeys = new KeyCode[5]; private bool pauseBlockedLastFrame = false; private void Awake() { if (Instance == null) Instance = this; else Destroy(gameObject); // Documentation text normalized. RefreshKeyCache(); // Documentation text normalized. if (trackKeyTexts != null) { foreach (var t in trackKeyTexts) { if (t != null) t.color = keyInactiveColor; } } } private void RefreshKeyCache() { for (int i = 0; i < TrackColors.Length; i++) { cachedKeys[i] = KeyBindingManager.GetKeyForColor(TrackColors[i]); } } private void Start() { // Ensure runtime input and UI labels both use current bindings. RefreshKeyCache(); RefreshKeyLabels(); // Initialize lane sprites to 0 alpha if (laneSprites != null) { foreach (var sprite in laneSprites) { if (sprite != null) SetSpriteAlpha(sprite, 0f); } } } private void SetSpriteAlpha(SpriteRenderer sprite, float alpha255) { if (sprite == null) return; Color c = sprite.color; c.a = Mathf.Clamp01(alpha255 / 255f); sprite.color = c; } private void Update() { bool pauseBlocked = IsBlockedByPause(); if (pauseBlocked) { if (!pauseBlockedLastFrame) ForceReleaseAllKeys(); pauseBlockedLastFrame = true; return; } pauseBlockedLastFrame = false; for (int i = 0; i < TrackColors.Length; i++) { KeyCode key = cachedKeys[i]; if (key == KeyCode.None) continue; int index = i; // TrackColors array index matches track index logic here if (Input.GetKeyDown(key)) { OnKeyPressed?.Invoke(key); // Documentation text normalized. // Update lane sprite alpha if (laneSprites != null && index >= 0 && index < laneSprites.Length) { SetSpriteAlpha(laneSprites[index], pressedAlpha); } // Documentation text normalized. if (trackKeyTexts != null && index >= 0 && index < trackKeyTexts.Length) { var txt = trackKeyTexts[index]; if (txt != null) txt.color = keyActiveColor; } } if (Input.GetKeyUp(key)) { OnKeyReleased?.Invoke(key); // Update lane sprite alpha if (laneSprites != null && index >= 0 && index < laneSprites.Length) { SetSpriteAlpha(laneSprites[index], 0f); } // Documentation text normalized. if (trackKeyTexts != null && index >= 0 && index < trackKeyTexts.Length) { var txt = trackKeyTexts[index]; if (txt != null) txt.color = keyInactiveColor; } } } } private bool IsBlockedByPause() { var pm = PauseManager.Instance; return pm != null && pm.IsPaused; } /// /// Force a release event for all bound keys and reset UI indicators. /// Useful for external "clear input" operations where the system should treat /// all keys as released regardless of physical state. /// public void ForceReleaseAllKeys() { for (int i = 0; i < TrackColors.Length; i++) { string color = TrackColors[i]; KeyCode key = KeyBindingManager.GetKeyForColor(color); if (key == KeyCode.None) continue; try { OnKeyReleased?.Invoke(key); } catch { } int index = GetIndexForColor(color); if (laneSprites != null && index >= 0 && index < laneSprites.Length) { SetSpriteAlpha(laneSprites[index], 0f); } if (trackKeyTexts != null && index >= 0 && index < trackKeyTexts.Length) { var txt = trackKeyTexts[index]; if (txt != null) txt.color = keyInactiveColor; } } } // Refresh displayed labels for key bindings (called after rebind) public void RefreshKeyLabels() { if (trackKeyTexts != null) { for (int i = 0; i < TrackColors.Length && i < trackKeyTexts.Length; i++) { var txt = trackKeyTexts[i]; if (txt == null) continue; KeyCode k = KeyBindingManager.GetKeyForColor(TrackColors[i]); txt.text = KeyBindingManager.GetDisplayName(k); } } // Keep runtime input cache in sync with latest bindings even when label UI is missing. RefreshKeyCache(); } private int GetIndexForColor(string color) { // Documentation text normalized. switch (color) { case "red": return 0; case "green": return 1; case "yellow": return 2; case "purple": return 3; case "blue": return 4; default: return -1; } } /// /// Documentation text normalized. public void ShowJudgeResult(int trackIndex, string result) { if (!showJudgeText) return; if (trackJudgeTexts == null || trackIndex < 0 || trackIndex >= trackJudgeTexts.Length) return; var textObj = trackJudgeTexts[trackIndex]; if (textObj == null) return; textObj.text = result; switch (result) { case "Perfect": textObj.color = perfectColor; break; case "Great": textObj.color = greatColor; break; case "Good": textObj.color = goodColor; break; case "Miss": textObj.color = missColor; break; default: textObj.color = Color.white; break; } } }