미니맵 시스템
전체 플레이 영상
구현 내용
전체 코드
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();
}
}