using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; /* Canvas (기존 InGameUI Canvas) └── MinimapRoot (빈 오브젝트, MinimapManager 컴포넌트 부착) ├── MapMask (원형/사각형 마스크, Mask 컴포넌트) │ ├── MapImage (RawImage) ← _mapImage, _mapRect │ ├── PlayerIcon (Image) ← _playerIcon │ └── MarkerContainer (빈 오브젝트) ← _markerContainer └── MinimapFrame (Image) ← 테두리 장식 (선택) */ public class MinimapManager : MonoBehaviour { [Header("미니맵 UI")] [SerializeField] private GameObject _minimapRoot; [SerializeField] private RawImage _mapImage; // 맵 배경 (RenderTexture 또는 프리렌더 이미지) [SerializeField] private RectTransform _mapRect; // 맵 이미지의 RectTransform [SerializeField] private RectTransform _playerIcon; // 플레이어 아이콘 [SerializeField] private RectTransform _markerContainer;// 마커 부모 [Header("프리렌더 이미지")] [SerializeField] private Texture2D _bakedMapImage; [Header("월드 범위")] [SerializeField] private Vector2 _worldMin = new Vector2(-50, -50); [SerializeField] private Vector2 _worldMax = new Vector2(50, 50); [Header("모드")] [SerializeField] private bool _useRenderTexture = true; // true: 실시간(개발용), false: 프리렌더(출시용) //개발용 설정 //---------------------------------------------------------- [Header("미니맵 카메라")] [SerializeField] private Camera _minimapCamera; [SerializeField] private RenderTexture _renderTexture; [Header("카메라 설정")] [SerializeField] private float _cameraHeight = 50f; [SerializeField] private float _zoomLevel = 30f; [SerializeField] private float _minZoom = 15f; [SerializeField] private float _maxZoom = 80f; //---------------------------------------------------------- private List _markers = new List(); private void Start() { ApplyMode(); } private void LateUpdate() { UpdateCamera(); UpdatePlayerIcon(); UpdateMarkers(); } public void ApplyMode() { if (_useRenderTexture) { _mapImage.texture = _renderTexture; if (_minimapCamera != null) _minimapCamera.gameObject.SetActive(true); } else { _mapImage.texture = _bakedMapImage; if (_minimapCamera != null) _minimapCamera.gameObject.SetActive(false); } } private void UpdateCamera() { if (!_useRenderTexture || _minimapCamera == null) return; Vector3 pos = GameManager.Instance.Level.CurrentCharacter.transform.position; _minimapCamera.transform.position = new Vector3(pos.x, pos.y + _cameraHeight, pos.z); _minimapCamera.orthographicSize = _zoomLevel; } private void UpdatePlayerIcon() { if (_playerIcon == null) return; _playerIcon.anchoredPosition = WorldToMapPos(GameManager.Instance.Level.CurrentCharacter.transform.position); _playerIcon.localRotation = Quaternion.Euler(0, 0, -GameManager.Instance.Level.CurrentCharacter.transform.eulerAngles.y); } private void UpdateMarkers() { for (int i = _markers.Count - 1; i >= 0; i--) { if (_markers[i] == null) { _markers.RemoveAt(i); continue; } _markers[i].UpdatePosition(this); } } public Vector2 WorldToMapPos(Vector3 worldPos) { float ratioX = (worldPos.x - _worldMin.x) / (_worldMax.x - _worldMin.x); float ratioY = (worldPos.z - _worldMin.y) / (_worldMax.y - _worldMin.y); //sizeDelta는 앵커 영역과 실제 UI 크기의 차이값 return new Vector2( ratioX * _mapRect.sizeDelta.x - _mapRect.sizeDelta.x * 0.5f, ratioY * _mapRect.sizeDelta.y - _mapRect.sizeDelta.y * 0.5f ); } public RectTransform MarkerContainer => _markerContainer; public void RegisterMarker(MinimapMarker marker) { if (!_markers.Contains(marker)) _markers.Add(marker); } public void UnregisterMarker(MinimapMarker marker) { _markers.Remove(marker); } public void SetZoom(float zoom) { _zoomLevel = Mathf.Clamp(zoom, _minZoom, _maxZoom); } public void ZoomIn(float amount) => SetZoom(_zoomLevel - amount); public void ZoomOut(float amount) => SetZoom(_zoomLevel + amount); }