using UnityEngine; using UnityEngine.UI; using UnityEngine.EventSystems; using System.Collections; /// /// MinimapExpandController — Tap the minimap to expand it to a full-screen map view. /// Tap again or press the close button to return to mini view. /// Attach to the minimap RawImage UI element. /// /// In expanded mode: /// - Camera zooms out to show entire play area /// - Map stops rotating (north-up for readability) /// - Semi-transparent overlay dims the game behind /// - Close button appears in the corner /// [RequireComponent(typeof(RawImage))] public class MinimapExpandController : MonoBehaviour, IPointerClickHandler { [Header("Expand Settings")] [SerializeField] private float expandedZoom = 200f; // Camera ortho size when expanded [SerializeField] private float expandDuration = 0.25f; // Animation duration [SerializeField] private float overlayAlpha = 0.6f; // Background dim overlay [Header("Expanded Size (screen fraction)")] [SerializeField] private float expandedScreenFraction = 0.85f; // 85% of screen [Header("Close Button")] [SerializeField] private Sprite closeButtonSprite; // Optional: custom close icon [SerializeField] private float closeButtonSize = 50f; // ─── Runtime ───────────────────────────────────────────── private RectTransform _rectTransform; private MinimapManager _minimapManager; private Camera _minimapCam; // Saved mini-view state private Vector2 _miniAnchorMin; private Vector2 _miniAnchorMax; private Vector2 _miniOffsetMin; private Vector2 _miniOffsetMax; private Vector2 _miniPivot; private Vector2 _miniSizeDelta; private Vector3 _miniAnchoredPos; private float _miniZoom; private bool _miniRotateWithPlayer; private Vector3 _miniCameraOffset; // Expanded state private bool _isExpanded = false; private bool _isAnimating = false; private GameObject _overlay; private GameObject _closeButton; private Canvas _parentCanvas; // ─── Lifecycle ─────────────────────────────────────────── private void Start() { _rectTransform = GetComponent(); _parentCanvas = GetComponentInParent(); // Find MinimapManager _minimapManager = FindObjectOfType(); if (_minimapManager != null) _minimapCam = _minimapManager.GetComponent(); // Save initial mini-view layout SaveMiniState(); // Create overlay (hidden by default) CreateOverlay(); CreateCloseButton(); } // ─── Click Handler ─────────────────────────────────────── public void OnPointerClick(PointerEventData eventData) { if (_isAnimating) return; if (_isExpanded) Collapse(); else Expand(); } // ─── Expand / Collapse ─────────────────────────────────── public void Expand() { if (_isExpanded || _isAnimating) return; _isExpanded = true; SaveMiniState(); // Show overlay if (_overlay != null) _overlay.SetActive(true); if (_closeButton != null) _closeButton.SetActive(true); // Bring minimap on top _rectTransform.SetAsLastSibling(); if (_closeButton != null) _closeButton.transform.SetAsLastSibling(); // Switch camera to full-map mode if (_minimapManager != null) { _minimapManager.rotateWithPlayer = false; _minimapManager.SetZoom(expandedZoom); } // Animate the RectTransform to fill screen StartCoroutine(AnimateExpand()); } public void Collapse() { if (!_isExpanded || _isAnimating) return; _isExpanded = false; // Hide overlay if (_overlay != null) _overlay.SetActive(false); if (_closeButton != null) _closeButton.SetActive(false); // Restore camera if (_minimapManager != null) { _minimapManager.rotateWithPlayer = _miniRotateWithPlayer; _minimapManager.SetZoom(_miniZoom); } // Animate back to mini StartCoroutine(AnimateCollapse()); } // ─── State Save/Restore ────────────────────────────────── private void SaveMiniState() { _miniAnchorMin = _rectTransform.anchorMin; _miniAnchorMax = _rectTransform.anchorMax; _miniOffsetMin = _rectTransform.offsetMin; _miniOffsetMax = _rectTransform.offsetMax; _miniPivot = _rectTransform.pivot; _miniSizeDelta = _rectTransform.sizeDelta; _miniAnchoredPos = _rectTransform.anchoredPosition; if (_minimapManager != null) { _miniRotateWithPlayer = _minimapManager.rotateWithPlayer; _miniCameraOffset = _minimapManager.offset; } if (_minimapCam != null) _miniZoom = _minimapCam.orthographicSize; } private void RestoreMiniState() { _rectTransform.anchorMin = _miniAnchorMin; _rectTransform.anchorMax = _miniAnchorMax; _rectTransform.offsetMin = _miniOffsetMin; _rectTransform.offsetMax = _miniOffsetMax; _rectTransform.pivot = _miniPivot; _rectTransform.sizeDelta = _miniSizeDelta; _rectTransform.anchoredPosition = _miniAnchoredPos; } // ─── Animations ────────────────────────────────────────── private IEnumerator AnimateExpand() { _isAnimating = true; // Capture start state Vector2 startAnchorMin = _rectTransform.anchorMin; Vector2 startAnchorMax = _rectTransform.anchorMax; Vector2 startOffsetMin = _rectTransform.offsetMin; Vector2 startOffsetMax = _rectTransform.offsetMax; Vector2 startPivot = _rectTransform.pivot; // Target: centered, filling expandedScreenFraction of screen float margin = (1f - expandedScreenFraction) / 2f; Vector2 targetAnchorMin = new Vector2(margin, margin); Vector2 targetAnchorMax = new Vector2(1f - margin, 1f - margin); Vector2 targetOffsetMin = Vector2.zero; Vector2 targetOffsetMax = Vector2.zero; Vector2 targetPivot = new Vector2(0.5f, 0.5f); float elapsed = 0f; while (elapsed < expandDuration) { elapsed += Time.unscaledDeltaTime; float t = Mathf.SmoothStep(0f, 1f, elapsed / expandDuration); _rectTransform.anchorMin = Vector2.Lerp(startAnchorMin, targetAnchorMin, t); _rectTransform.anchorMax = Vector2.Lerp(startAnchorMax, targetAnchorMax, t); _rectTransform.offsetMin = Vector2.Lerp(startOffsetMin, targetOffsetMin, t); _rectTransform.offsetMax = Vector2.Lerp(startOffsetMax, targetOffsetMax, t); _rectTransform.pivot = Vector2.Lerp(startPivot, targetPivot, t); yield return null; } // Ensure final values are exact _rectTransform.anchorMin = targetAnchorMin; _rectTransform.anchorMax = targetAnchorMax; _rectTransform.offsetMin = targetOffsetMin; _rectTransform.offsetMax = targetOffsetMax; _rectTransform.pivot = targetPivot; _isAnimating = false; } private IEnumerator AnimateCollapse() { _isAnimating = true; // Capture current (expanded) state Vector2 startAnchorMin = _rectTransform.anchorMin; Vector2 startAnchorMax = _rectTransform.anchorMax; Vector2 startOffsetMin = _rectTransform.offsetMin; Vector2 startOffsetMax = _rectTransform.offsetMax; Vector2 startPivot = _rectTransform.pivot; float elapsed = 0f; while (elapsed < expandDuration) { elapsed += Time.unscaledDeltaTime; float t = Mathf.SmoothStep(0f, 1f, elapsed / expandDuration); _rectTransform.anchorMin = Vector2.Lerp(startAnchorMin, _miniAnchorMin, t); _rectTransform.anchorMax = Vector2.Lerp(startAnchorMax, _miniAnchorMax, t); _rectTransform.offsetMin = Vector2.Lerp(startOffsetMin, _miniOffsetMin, t); _rectTransform.offsetMax = Vector2.Lerp(startOffsetMax, _miniOffsetMax, t); _rectTransform.pivot = Vector2.Lerp(startPivot, _miniPivot, t); yield return null; } // Ensure exact mini state restored RestoreMiniState(); _isAnimating = false; } // ─── UI Creation ───────────────────────────────────────── private void CreateOverlay() { if (_parentCanvas == null) return; _overlay = new GameObject("MinimapExpandOverlay"); _overlay.transform.SetParent(_parentCanvas.transform, false); RectTransform overlayRect = _overlay.AddComponent(); overlayRect.anchorMin = Vector2.zero; overlayRect.anchorMax = Vector2.one; overlayRect.offsetMin = Vector2.zero; overlayRect.offsetMax = Vector2.zero; Image overlayImage = _overlay.AddComponent(); overlayImage.color = new Color(0f, 0f, 0f, overlayAlpha); overlayImage.raycastTarget = true; // Clicking the overlay also closes the expanded map Button overlayButton = _overlay.AddComponent