미니맵 시스템

전체 플레이 영상

전체 코드

using UnityEngine;

public class MiniMapPointInfo : MonoBehaviour
{
    [SerializeField] private string placeName;
    [SerializeField] private string description;

    public void ShowInfo()
    {
        MiniMapInfoUI.Instance.Show(placeName, description);
    }

    public void HideInfo()
    {
        MiniMapInfoUI.Instance.Hide();
    }
}
using UnityEngine;
using UnityEngine.UI;

public class MiniMapManager : MonoBehaviour
{
    private Camera miniMapCam;

    [Header("Follow Target")]
    public Transform target;

    [Header("Zoom Settings")]
    public Slider zoomSlider;              // 오른쪽 슬라이더
    public RectTransform miniMapArea;      // 초록색 미니맵 영역 (RawImage가 있는 RectTransform)
    public float minZoom = 15f;            // 최소 줌 (작게 볼 때)
    public float maxZoom = 90f;            // 최대 줌 (넓게 볼 때)
    public float defaultZoom = 40f;        // 원래대로 값
    public float scrollSensitivity = 5f;         // 마우스 스크롤 민감도

    void Start()
    {
        if (miniMapCam == null)
            miniMapCam = GetComponentInChildren<Camera>();

        // 슬라이더 초기화
        if (zoomSlider != null)
        {
            zoomSlider.minValue = 0f;
            zoomSlider.maxValue = 1f;

            float t = Mathf.InverseLerp(minZoom, maxZoom, defaultZoom);
            zoomSlider.value = t;
            zoomSlider.onValueChanged.AddListener(OnZoomSliderChanged);
        }

        SetZoom(defaultZoom);
    }

    void Update()
    {
        // 1) 플레이어 따라가기
        if (target != null)
        {
            transform.position = new Vector3(
                target.position.x,
                transform.position.y,
                target.position.z
            );

            Vector3 forward = target.forward;
            forward.y = 0;
            Quaternion targetRotation = Quaternion.LookRotation(forward);
            transform.rotation = Quaternion.Euler(
                transform.rotation.eulerAngles.x,
                targetRotation.eulerAngles.y,
                transform.rotation.eulerAngles.z
            );
        }
        else
        {
            GameObject player = GameObject.FindGameObjectWithTag("Player");
            if (player != null) target = player.transform;
        }

        // 2) 마우스 스크롤로 줌 조절 (미니맵 영역 위에서만)
        HandleScrollZoom();
    }

    void OnZoomSliderChanged(float t)
    {
        float value = Mathf.Lerp(minZoom, maxZoom, t);
        SetZoom(value);
    }

    void HandleScrollZoom()
    {
        if (zoomSlider == null || miniMapArea == null)
            return;

        // 마우스가 미니맵 RawImage 위에 있는지 체크
        bool overMiniMap = RectTransformUtility.RectangleContainsScreenPoint(
            miniMapArea,
            Input.mousePosition,
            null // Canvas가 Screen Space - Overlay면 null
        );

        if (!overMiniMap) return;

        float scroll = Input.GetAxis("Mouse ScrollWheel");
        if (Mathf.Abs(scroll) < 0.0001f) return;

        // 현재 높이/사이즈 가져오기
        float current = GetCurrentZoomValue();

        // 위로 스크롤: 확대 -> 값 줄어듦
        current -= scroll * scrollSensitivity;
        current = Mathf.Clamp(current, minZoom, maxZoom);

        // 실제 줌 적용 + 슬라이더 값 동기화
        SetZoom(current);

        float t = Mathf.InverseLerp(minZoom, maxZoom, current);
        zoomSlider.SetValueWithoutNotify(t);
    }

    float GetCurrentZoomValue()
    {
        if (miniMapCam.orthographic)
            return miniMapCam.orthographicSize;
        else
            return transform.position.y;
    }

    void SetZoom(float value)
    {
        value = Mathf.Clamp(value, minZoom, maxZoom);

        if (miniMapCam.orthographic)
        {
            miniMapCam.orthographicSize = value;
        }
        else
        {
            Vector3 p = transform.position;
            p.y = value;
            transform.position = p;
        }
    }

    public void ResetZoom()
    {
        SetZoom(defaultZoom);

        if (zoomSlider != null)
        {
            float t = Mathf.InverseLerp(minZoom, maxZoom, defaultZoom);
            zoomSlider.SetValueWithoutNotify(t);
        }
    }

}
using UnityEngine;
using TMPro;

public class MiniMapInfoUI : MonoBehaviour
{
    public static MiniMapInfoUI Instance;
    
    [SerializeField] private GameObject panel;
    [SerializeField] private TextMeshProUGUI nameText;
    [SerializeField] private TextMeshProUGUI infoText;

    void Awake()
    {
        Instance = this;
    }

    public void Show(string _name, string _info)
    {
        nameText.text = _name;
        infoText.text = _info;
        panel.SetActive(true);
    }

    public void Hide()
    {
        if(panel.activeSelf) panel.SetActive(false);
    }
}
using UnityEngine;
using UnityEngine.EventSystems;

public class MiniMapHoverHandler : MonoBehaviour,
    IPointerEnterHandler, IPointerExitHandler
{
    [Header("참조")]
    [SerializeField] private Camera miniMapCam;      // MinimapCamera
    [SerializeField] private RectTransform miniMapRect; // MinimapImage(RawImage)의 RectTransform
    [SerializeField] private LayerMask npcLayerMask; // MapNPC 레이어

    private bool pointerOver = false;
    private MiniMapPointInfo currentInfo = null;

    private void Reset()
    {
        miniMapRect = transform as RectTransform;
    }

    private void Update()
    {
        if (!pointerOver || miniMapCam == null || miniMapRect == null)
        {
            ClearHit();
            return;
        }

        // 1. 마우스 위치를 RawImage 로컬좌표로 변환
        if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(
                miniMapRect,
                Input.mousePosition,
                null,                // Screen Space - Overlay라면 null
                out Vector2 localPoint))
        {
            ClearHit();
            return;
        }

        // 2. 로컬(-w/2~w/2)을 0~1 뷰포트로 변환
        Vector2 size = miniMapRect.rect.size;
        Vector2 normalized = (localPoint / size) + new Vector2(0.5f, 0.5f);

        // 3. 미니맵 카메라로 Ray 쏘기
        Ray ray = miniMapCam.ViewportPointToRay(
            new Vector3(normalized.x, normalized.y, 0f));

        if (Physics.Raycast(ray, out RaycastHit hit, 1000f, npcLayerMask))
        {
            var info = hit.collider.GetComponent<MiniMapPointInfo>();
            if (info != null)
            {
                // 새 NPC로 바뀌면 이전 건 숨기고 새거 띄우기
                if (info != currentInfo)
                {
                    if (currentInfo != null)
                        currentInfo.HideInfo();

                    currentInfo = info;
                    currentInfo.ShowInfo();
                }

                return;
            }
        }

        // 아무것도 안 맞았으면 숨기기
        ClearHit();
    }

    private void ClearHit()
    {
        if (currentInfo != null)
        {
            currentInfo.HideInfo();
            currentInfo = null;
        }
    }

    public void OnPointerEnter(PointerEventData eventData)
    {
        pointerOver = true;
    }

    public void OnPointerExit(PointerEventData eventData)
    {
        pointerOver = false;
        ClearHit();
    }
}

전체 흐름도