카테고리 없음

미니맵 Clamp & 디버깅 - Unity 본캠프 58일차

julesnow 2025. 7. 3. 17:44

▪️MiniMap 카메라 Clamp/플레이어 추적 & 버그 디버깅 TIL

📑 목차

⓵ 트러블슈팅
 𝟙 문제 상황 & 요약
 𝟚 원인 분석 
    - Clamp/Tilemap/Camera Tracking
 𝟛 해결 과정
    - Clamp Setting 위치에 따른 결과 차이
    - 후속 테스트
 𝟜 결과
 𝟝 다음 계획

🗂️ Minimap Animaion 트러블슈팅

① 문제 상황 & 증상 요약

  • 미니맵 카메라가 플레이어를 따라가지 않거나, 이동이 부자연스러움

  • 방 입장/이동 시만 Clamp(카메라 이동 제한)가 적용되고,
    방 내부에서는 플레이어를 따라오지 않음

  • 플레이어 아이콘이 방 내부에서 제대로 보이지 않기도 함
      -> 사이즈 조정해도 해결 안됨

  • Clamp(min, max) 값이 너무 빡빡하거나 0,0~0,0 등 비정상일 때,
    카메라가 고정되어 움직이지 않음

 


② 원인 분석 - Clamp / Tilemap / Camera Tracking

▪️현재 구조에서는

  • Clamp(min/max)는  방 입장 시 에만 갱신
  • target(플레이어)는 한번 세팅하면 계속 따라가야 함
  • LateUpdate에서 "매 프레임" 플레이어의 위치를 읽어서 부드럽게 따라가게 되어 있음

▪️만약

  • 방 이동 시에만 카메라가 움직이고, 방 안에서 머무를 때는 플레이어를 따라오지 않는다면?

▪️원인

  • 아마도 LateUpdate에서 target이 null이거나

  • SetTarget이 방 입장 시에만 한번 호출되고,
    LateUpdate에서 실제로 따라가는 코드를 생략했거나

  • 혹은 Clamp min/max가 너무 빡빡하게 잡혀있어서 카메라가 아예 움직이지 않을 수 있음

 

▪️그렇다면   Clamp 값이 정해지는 규칙은 ? 

  • Clamp 값(min, max)은 타일맵(방)의 경계를 기준으로 설정됨

▫️예시

방의 타일맵 bounds가 min(10, 10), max(20, 20)이라면

_clampMin = (10, 10)
_clampMax = (20, 20)


-> 단, 방의 크기가 작으면
카메라가 움직일 수 있는 공간이 거의 없음!

  • 방 타일맵 bounds가 실제로 너무 작은 경우 (예: 10,10 ~ 12,12)
    -> 카메라 이동 제한이 너무 심하게 적용됨 (min==max에 가깝거나, 카메라 오프셋 등)
  • 플레이어가 아무리 움직여도, 카메라의 Clamp 범위(=min~max)가
    플레이어 움직임 범위를 다 감싸지 못하면 카메라가 거의 안 움직이거나, 한 위치에 고정된 것처럼 보임
  • Clamp 값을 늘린다고 카메라가 잘 따라오는 것은 아님

-> 따라서, Clamp 범위가 실제로 플레이어 이동 범위보다 충분히 넓어야 한다!

▪️체크리스트

  • 타일맵 bounds(min/max) 값이 실제 플레이어 이동 범위와 맞는가?
  • target(플레이어)의 position이 충분히 변하고 있는가?
  • MiniMapCamera의 LateUpdate가 정상적으로 실행되고 있는가?
  • Clamp/Lerp가 정상 적용 중인지 Debug로 값 확인 필요

 


③ 해결 과정

▪️우선, 카메라가 움직이게끔 수정하자!

기존 코드

  • 임시 플레이어와 적을 prefab화 해서 사용함
public void DrawRoom(GameObject tempPlayer, Tilemap roomTilemap, GameObject[] tempEnemies)
{
    SetPlayer(tempPlayer.transform);

    if (tempEnemies != null)
    {
        var enemyList = new List<Transform>();
        foreach (var e in tempEnemies)
            if (e != null) enemyList.Add(e.transform);
        SetEnemies(enemyList);
    }

    if (roomTilemap != null)
    {
        CalculateMapBounds(roomTilemap);
    }
}



  • 입력값:
    • GameObject 타입의 플레이어, 적 오브젝트
    • Tilemap 타입의 맵 정보
  • 동작:
    • SetPlayer/SetEnemies로 Transform만 따로 빼서 관리
    • roomTilemap이 있으면 CalculateMapBounds(roomTilemap) 호출(맵의 경계 계산?)
  • 전체적으로:
    • “플레이어, 적, 맵” 정보를 받아서 미니맵에 필요한 정보로 변환(Transform, Bounds 등)
    • 적/플레이어는 GameObject로 입력받고 내부에서 Transform으로 바꿔줌
    • 맵 사이즈/위치 계산은 RoomManager에서 담당


개선 코드

using System;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Tilemaps;
using System.Collections.Generic;

public class MiniMapUI : BaseUI
{
    [SerializeField] private RawImage _minimapRawImage;
    [SerializeField] private GameObject player;
    [SerializeField] private Tilemap currentRoomTilemap;
    public override int CanvasLayer => 0;

    private Transform _player;
    private RectTransform _miniMapRectTransform;
    private Vector2 _worldMin;
    private Vector2 _worldMax;
    private MiniMapCamera _minimapCamera;
    
    private void Start()
    {
        _minimapCamera = FindObjectOfType<MiniMapCamera>();
    }

    public void SetPlayer(Transform player)
    {
        _player = player;
        if (_minimapCamera != null)
            _minimapCamera.SetTarget(player);
    }
    
    public void DrawRoom(Tilemap roomTilemap, Transform player)
    {
        if (roomTilemap == null || player == null) return;

        BoundsInt bounds = roomTilemap.cellBounds;
        Vector3Int minInt = bounds.min;
        Vector3Int maxInt = bounds.max;
        Vector2 min = new Vector2(minInt.x, minInt.y);
        Vector2 max = new Vector2(maxInt.x, maxInt.y);
        
        SetPlayer(player);
        
        if (_minimapCamera != null)
            _minimapCamera.SetClampBounds(min, max);
        
        if (_minimapRawImage != null)
        {
            _miniMapRectTransform = _minimapRawImage.rectTransform;
            AdjustMiniMapSize(bounds.size);
        }
        
        _worldMin = min;
        _worldMax = max;
    }

    public void LocateMiniMapCamera(Vector3 position)
    {
        if (_minimapCamera != null)
            _minimapCamera.MoveTo(position);
    }
    private void AdjustMiniMapSize(Vector3Int tilemapSize)
    {
        float scaleFactor = 10f;
        _miniMapRectTransform.sizeDelta = new Vector2(tilemapSize.x, tilemapSize.y) * scaleFactor;
    }
    

    private void Update()
    {
        if (_miniMapRectTransform == null) return;
    }
    private Vector2 WorldToMiniMap(Vector3 worldPos)
    {
        float x = (worldPos.x - _worldMin.x) / (_worldMax.x - _worldMin.x) * _miniMapRectTransform.sizeDelta.x;
        float y = (worldPos.y - _worldMin.y) / (_worldMax.y - _worldMin.y) * _miniMapRectTransform.sizeDelta.y;
        return new Vector2(x, y);
    }

    public override void Enable() => gameObject.SetActive(true);
    public override void Disable() => gameObject.SetActive(false);
}
  • 입력값:
    • Tilemap 타입의 맵 정보
    • Transform 타입의 플레이어 (GameObject → Transform 아님, 바로 Transform)
  • 동작:
    • 맵의 cellBounds로 미니맵 영역 계산 (BoundsInt → min, max로 벡터화)
    • MiniMapCamera의 클램프 영역을 직접 세팅
    • RawImage 크기를 맵 사이즈에 맞게 동적으로 조정
    • 내부적으로 _worldMin, _worldMax 저장
  • 전체적으로:
    • 미니맵 카메라와 RawImage(미니맵 UI)의 크기, 영역, 플레이어 위치 동기화까지 포함
    • 더 직관적이고 직접적으로 미니맵 UI 영역을 조정하는 기능 추가

차이점

입력 데이터 GameObject(플레이어, 적) Transform(플레이어), Tilemap(맵)
플레이어/적 처리 GameObject로 받고 Transform 추출 Transform으로 바로 받음(더 단순)
맵 처리 CalculateMapBounds(함수 분리) bounds/cellBounds로 직접 계산
미니맵 UI 크기 별도 없음(기본값/고정?) AdjustMiniMapSize로 동적 조정
카메라 영역 없음(함수에 미포함) _minimapCamera.SetClampBounds로 동기화
RawImage/UI와 연결 직접적 UI 제어 없음 RawImage/RectTransform을 직접 조정
내부 State worldMin/worldMax 등 분리 관리 DrawRoom에서 한 번에 처리(직접 연결)
기타 적 관리 포함(SetEnemies) 적 미포함(플레이어, 맵 중심)


즉, 개선된 코드가

  • 미니맵 UI/카메라/경계 조정까지 DrawRoom에서 한 번에 처리
    -> 더 직관적이고 UI 자동화에 가까움
  • 입력값이 Transform/Tilemap 중심으로 단순화돼서,
    -> 더 간단하게 함수 호출, 유지보수 쉬움


결과적으로,

방 이동시에는 카메라가 움직이니 플레이어 부분을 수정해보자.

문제 코드

public void DrawRoom(Tilemap roomTilemap, Transform player) 
{
    // ...
    SetPlayer(player);
    if (_minimapCamera != null)
        _minimapCamera.SetClampBounds(min, max);
}

 

  • DrawRoom(방 입장 시)에서만 Clamp가 갱신됨
    -> 방 이동(씬 이동) 때만 카메라가 움직이고, 방 안에서는 플레이어를 따라가지 않음

 

 

개선 코드

private void Start() {
    _minimapCamera = FindObjectOfType<MiniMapCamera>();
    // 전체 Clamp를 Start에서 먼저 세팅
    float clampPadding = 1.0f;
    Vector2 paddedMin = new Vector2(mapMin.x + clampPadding, mapMin.y + clampPadding);
    Vector2 paddedMax = new Vector2(mapMax.x - clampPadding, mapMax.y - clampPadding);

    if (_minimapCamera != null)
        _minimapCamera.SetClampBounds(paddedMin, paddedMax);

    if (player != null && _minimapCamera != null)
        _minimapCamera.SetTarget(player.transform);
}

 

  • Start()에서 전체 맵 기준으로 Clamp를 여유있게 지정해 플레이어를 항상 부드럽게 따라가도록 개선
  • DrawRoom에서는 필요할 때만 방 경계로 Clamp 갱신

 

▪️Clamp 세팅 위치에 따른 결과 차이

DrawRoom() 방 입장 기준 / 이동 시에만 갱신 방 내부에서 카메라 거의 안 움직임 (범위 너무 좁으면 아예 고정)
Start() 맵 전체 기준 / Clamp값 여유 있게 지정 항상 플레이어를 따라감, 버벅임/고정 현상 해결

-> 즉, Start에서 Clamp를 먼저 넓게 잡으면, 타일맵 없이도(혹은 DrawRoom 호출 없이도)

      항상 플레이어 이동에 맞춰 카메라가 정상적으로 따라옴

 

     반대로, DrawRoom에서만 Clamp 갱신하면 방 범위가 너무 작으면 카메라 이동 공간 자체가 없음

 

첫번째 코드

미니맵 관련 테스트용
public void GenerateMiniMapTexture(Tilemap tilemap)
{
    var bounds = tilemap.cellBounds;
    Texture2D minimapTexture = new Texture2D(bounds.size.x, bounds.size.y);

    for (int x = 0; x < bounds.size.x; x++)
    {
        for (int y = 0; y < bounds.size.y; y++)
        {
            Vector3Int cellPosition = new Vector3Int(bounds.x + x, bounds.y + y, 0);
            TileBase tile = tilemap.GetTile(cellPosition);
            if (tile != null)
            {
                minimapTexture.SetPixel(x, y, Color.white); // 타일마다 색을 다르게 하고 싶다면 조건 추가
            }
            else
            {
                minimapTexture.SetPixel(x, y, Color.clear);
            }
        }
    }
    minimapTexture.Apply();
    _minimapRawImage.texture = minimapTexture;
}
  • Clamp 범위(카메라 이동 제한 경계)를
    DrawRoom(타일맵을 기반으로 하는 함수)에서만 세팅하고 있음.
  • Start()에서 Clamp 경계를 지정하는 코드가 없음.
  • 즉, 방 진입/타일맵 기준이 아닌 경우(씬 시작, 전체 맵 등)에는
    카메라 Clamp 값이 0,0~0,0 상태로 남아있을 수 있음

    → 카메라가 움직이지 않음.

 

두 번째 코드

using System;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Tilemaps;
using System.Collections.Generic;

public class MiniMapUI : BaseUI
{
    [SerializeField] private RawImage _minimapRawImage;
    [SerializeField] private GameObject player;
    [SerializeField] private Tilemap currentRoomTilemap;
    [SerializeField] private Vector2 mapMin = new Vector2(-30, -30); 
    [SerializeField] private Vector2 mapMax = new Vector2(30, 30); 
    [SerializeField] private float clampPadding = 1.0f;
    public override int CanvasLayer => 0;

    private Transform _player;
    private RectTransform _miniMapRectTransform;
    private Vector2 _worldMin;
    private Vector2 _worldMax;
    private MiniMapCamera _minimapCamera;
    
    private void Start()
    {
        _minimapCamera = FindObjectOfType<MiniMapCamera>();
        
        if (player != null && _minimapCamera != null)
            _minimapCamera.SetTarget(player.transform);
        
        float clampPadding = 1.0f;
        Vector2 paddedMin = new Vector2(mapMin.x + clampPadding, mapMin.y + clampPadding);
        Vector2 paddedMax = new Vector2(mapMax.x - clampPadding, mapMax.y - clampPadding);
        
        if (_minimapCamera != null)
            _minimapCamera.SetClampBounds(paddedMin, paddedMax);
    }
    
    public void SetPlayer(Transform player)
    {
        _player = player;
        if (_minimapCamera != null)
            _minimapCamera.SetTarget(player);
    }
    
    public void DrawRoom(Tilemap roomTilemap, Transform player)
    {
        if (roomTilemap == null || player == null) return;

        BoundsInt bounds = roomTilemap.cellBounds;
        Vector3Int minInt = bounds.min;
        Vector3Int maxInt = bounds.max;
        Vector2 min = new Vector2(minInt.x, minInt.y);
        Vector2 max = new Vector2(maxInt.x, maxInt.y);
        
        SetPlayer(player);
        
        if (_minimapCamera != null)
            _minimapCamera.SetClampBounds(min, max);
        
        if (_minimapRawImage != null)
        {
            _miniMapRectTransform = _minimapRawImage.rectTransform;
            AdjustMiniMapSize(bounds.size);
        }
        
        _worldMin = min;
        _worldMax = max;
    }

    public void LocateMiniMapCamera(Vector3 position)
    {
        if (_minimapCamera != null)
            _minimapCamera.MoveTo(position);
    }
    private void AdjustMiniMapSize(Vector3Int tilemapSize)
    {
        float scaleFactor = 10f;
        _miniMapRectTransform.sizeDelta = new Vector2(tilemapSize.x, tilemapSize.y) * scaleFactor;
    }
    

    private void Update()
    {
        if (_miniMapRectTransform == null) return;
    }
    private Vector2 WorldToMiniMap(Vector3 worldPos)
    {
        float x = (worldPos.x - _worldMin.x) / (_worldMax.x - _worldMin.x) * _miniMapRectTransform.sizeDelta.x;
        float y = (worldPos.y - _worldMin.y) / (_worldMax.y - _worldMin.y) * _miniMapRectTransform.sizeDelta.y;
        return new Vector2(x, y);
    }

    public override void Enable() => gameObject.SetActive(true);
    public override void Disable() => gameObject.SetActive(false);
}

 

 

  • Start()에서
    • mapMin, mapMax를 인스펙터에서 미리 넓게 지정
    • 여기에 clampPadding을 더해서 넉넉한 Clamp 범위를 만들어
      바로 미니맵 카메라에 전달!
  • 즉, 방 입장과 상관없이 항상 넓은 Clamp가 적용
    → 플레이어가 움직일 때마다 카메라가 정상적으로 따라감.

해결!


해결방법

  • 두 번째 코드에서는 Start()에서 바로 Clamp 세팅
    (SetClampBounds(paddedMin, paddedMax);)
  • 즉, 타일맵이 없어도(혹은 DrawRoom 호출 없이도)
    초기값이 무조건 넉넉하게 잡혀서 카메라가 따라다님!
  • (DrawRoom을 사용하면 방마다 Clamp를 바꿀 수도 있고,
    Start에서 한 번만 세팅하면 맵 전체 Clamp로 항상 플레이어를 따라다님)

 

요약

  • 첫 번째: Clamp가 초기화 안 됨 → 카메라 안 움직임
  • 두 번째: Clamp가 Start에서 항상 넓게 초기화 → 플레이어 이동에 따라 카메라 정상 추적


미니맵 통로에서 플레이어가 지나갈 때 플레이어 보이지 않게

 

방법

입구에 알 수 있게끔 (콜라이더 /  -> 미니맵 종료 | 재시작

혹은 카메라 위치 범위를 주어  | 플레이어 위치가 어디~ 범위라면

 

                               수정 전                              ->                             수정 후

 

 

플레이어 위치에 따라 미니맵에도 clamp animaion이 적용되는 모습을 확인할 수 있다.