using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UIElements; using System.Linq; namespace UI { public class UIManager : MonoBehaviour { public static UIManager Instance { get; private set; } [System.Serializable] public class ScreenData { public string screenName; public UIDocument document; public bool isOverlay; public bool isActive; } public List screens = new List(); public string initialScreen = "MainMenu"; [Header("Cursor Settings")] private VisualElement _customCursor; private List _trailPool = new List(); private int _trailIndex = 0; public int trailCount = 15; public float focusRadius = 500f; [Header("Editor Preview")] [Range(0f, 1f)] public float globalOpacity = 1f; private Stack _navigationStack = new Stack(); private string _currentScreenName; private VisualElement _lastHoveredElement; // Flags for MainMenu state public bool isMainMenuActive = false; private bool _isSettingsOpen = false; private void Awake() { if (Instance == null) Instance = this; else { Destroy(gameObject); return; } SetupCursor(); foreach (var s in screens) { if (s.document != null) s.document.rootVisualElement.style.display = DisplayStyle.None; } ShowScreen(initialScreen); } private void SetupCursor() { UIDocument doc = GetComponent(); if (doc == null && screens.Count > 0) doc = screens[0].document; if (doc == null) return; var root = doc.rootVisualElement; _customCursor = new VisualElement(); _customCursor.style.width = 25; _customCursor.style.height = 25; _customCursor.style.backgroundColor = Color.white; _customCursor.style.borderTopLeftRadius = 13; _customCursor.style.borderTopRightRadius = 13; _customCursor.style.borderBottomLeftRadius = 13; _customCursor.style.borderBottomRightRadius = 13; _customCursor.style.position = Position.Absolute; _customCursor.pickingMode = PickingMode.Ignore; root.Add(_customCursor); for (int i = 0; i < trailCount; i++) { var trail = new VisualElement(); trail.style.width = 18; trail.style.height = 18; trail.style.backgroundColor = new Color(1, 1, 1, 0.4f); trail.style.borderTopLeftRadius = 9; trail.style.borderTopRightRadius = 9; trail.style.borderBottomLeftRadius = 9; trail.style.borderBottomRightRadius = 9; trail.style.position = Position.Absolute; trail.pickingMode = PickingMode.Ignore; root.Add(trail); _trailPool.Add(trail); } _customCursor.BringToFront(); } private void Update() { Vector2 mousePos = Input.mousePosition; bool isMainMenu = (_currentScreenName == "MainMenu"); bool restrictY = (isMainMenu && isMainMenuActive && !_isSettingsOpen); float targetY = restrictY ? Screen.height / 2f : mousePos.y; Vector2 uiPos = new Vector2(mousePos.x, Screen.height - targetY); bool showCursor = !isMainMenu || !isMainMenuActive || _isSettingsOpen; DisplayStyle cursorDisplay = showCursor ? DisplayStyle.Flex : DisplayStyle.None; if (_customCursor != null) { _customCursor.style.display = cursorDisplay; _customCursor.style.left = uiPos.x - 12.5f; _customCursor.style.top = uiPos.y - 12.5f; } if (_trailPool.Count > 0) { var currentTrail = _trailPool[_trailIndex]; currentTrail.style.left = uiPos.x - 9; currentTrail.style.top = uiPos.y - 9; currentTrail.style.opacity = 0.5f; foreach(var t in _trailPool) { t.style.display = cursorDisplay; t.style.opacity = Mathf.Max(0, t.style.opacity.value - Time.deltaTime * 4f); } _trailIndex = (_trailIndex + 1) % _trailPool.Count; } HandleVirtualInput(uiPos); if ((Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl)) && Input.GetKeyDown(KeyCode.O)) ToggleSettings(); } private void HandleVirtualInput(Vector2 uiPos) { UIDocument activeDoc = null; var settings = screens.Find(s => s.screenName == "Settings"); if (_isSettingsOpen) activeDoc = settings.document; else activeDoc = screens.Find(s => s.screenName == _currentScreenName)?.document; if (activeDoc == null) return; VisualElement bestElement = null; float minDistance = float.MaxValue; var interactables = activeDoc.rootVisualElement.Query() .Where(e => e.focusable && e.pickingMode != PickingMode.Ignore).ToList(); foreach (var element in interactables) { Rect worldBounds = element.worldBound; float dist = Vector2.Distance(uiPos, worldBounds.center); if (dist < minDistance && dist < focusRadius) { minDistance = dist; bestElement = element; } } if (bestElement != _lastHoveredElement) { _lastHoveredElement?.RemoveFromClassList("hover"); bestElement?.AddToClassList("hover"); _lastHoveredElement = bestElement; } if (Input.GetMouseButtonDown(0) && _lastHoveredElement != null) { using (var clickEvent = ClickEvent.GetPooled()) { clickEvent.target = _lastHoveredElement; _lastHoveredElement.SendEvent(clickEvent); } } } // --- Editor Support Methods (Restored) --- public void SyncScreens() { foreach (var screen in screens) { if (screen.document != null && screen.document.rootVisualElement != null) { screen.document.rootVisualElement.style.display = screen.isActive ? DisplayStyle.Flex : DisplayStyle.None; screen.document.rootVisualElement.style.opacity = globalOpacity; } } } public void ShowOnly(string name) { foreach (var screen in screens) { screen.isActive = (screen.screenName == name); } SyncScreens(); } // --- Runtime Logic --- public void ShowScreen(string name) { var screen = screens.Find(s => s.screenName == name); if (screen == null) return; if (!screen.isOverlay) { foreach(var s in screens) if(!s.isOverlay && s.document != null) s.document.rootVisualElement.style.display = DisplayStyle.None; _navigationStack.Push(name); _currentScreenName = name; } screen.document.rootVisualElement.style.display = DisplayStyle.Flex; screen.isActive = true; UnityEngine.Cursor.visible = false; } public void GoBack() { if (_navigationStack.Count <= 1) return; string current = _navigationStack.Pop(); var currentData = screens.Find(s => s.screenName == current); if (currentData != null) currentData.document.rootVisualElement.style.display = DisplayStyle.None; _currentScreenName = _navigationStack.Peek(); var prevData = screens.Find(s => s.screenName == _currentScreenName); if (prevData != null) { prevData.document.rootVisualElement.style.display = DisplayStyle.Flex; prevData.isActive = true; } } public void ToggleSettings() { var settings = screens.Find(s => s.screenName == "Settings"); if (settings == null) return; _isSettingsOpen = settings.document.rootVisualElement.style.display == DisplayStyle.None; settings.document.rootVisualElement.style.display = _isSettingsOpen ? DisplayStyle.Flex : DisplayStyle.None; settings.isActive = _isSettingsOpen; if (_isSettingsOpen) settings.document.sortingOrder = 999; } } }