[Unity/UI] 특정 위치로 팝업 UI 띄우기

이형원·2026년 3월 22일

주간 공부

목록 보기
2/7

1. 구현 목표

기획된 UI 디자인으로 받은 내용에 따르면, 이와 같은 디자인 요청이 있었다.

  • 언어 버튼이 아래쪽에 있을 때 누르면 버튼의 위쪽에 팝업이 뜨게 함

  • 언어 버튼이 위쪽에 있을 때 누르면 버튼의 아래쪽에 팝업이 뜨게 함

세부적인 기획 내용에 관해서는 들어봐야 할 것 같지만, 우선 조건을 아래와 같이 설정하여 미리 설계를 해 보았다.

  • 언어 버튼의 위치에 따라 팝업의 위치를 위/아래로 결정하여 띄움
  • 팝업이 뜬 순간에 스크롤은 일시적으로 막히며, 팝업의 바깥쪽을 누르면 팝업이 꺼지는 구조

2. 설계 방법

먼저 UI 구조는 다음과 같이 설정하였다. 버튼은 4번 이미지에 있다.

그리고 코드 구조는 다음과 같이 설계했다.

using UnityEngine;

public class ScrollviewPopupUI : MonoBehaviour
{
    [Header("References")]
    [SerializeField] private RectTransform popup;
    [SerializeField] private GameObject dimmedBackground;
    [SerializeField] private Canvas canvas;

    [Header("Settings")]
    [SerializeField] private float yoffset = 20f;
    [SerializeField] private float xoffset = -150f;

    private RectTransform canvasRect;

    private void Awake()
    {
        canvasRect = canvas.transform as RectTransform;

        popup.gameObject.SetActive(false);
        dimmedBackground.SetActive(false);
    }

    public void ShowPopup(RectTransform target)
    {   
        Vector2 screenPos = RectTransformUtility.WorldToScreenPoint(
            canvas.renderMode == RenderMode.ScreenSpaceOverlay ? null : canvas.worldCamera,
            target.position
        );

        RectTransformUtility.ScreenPointToLocalPointInRectangle(
            canvasRect,
            screenPos,
            canvas.renderMode == RenderMode.ScreenSpaceOverlay ? null : canvas.worldCamera,
            out Vector2 localPos
        );

        bool isUpper = localPos.y > 0;

        if (isUpper)
        {
            popup.pivot = new Vector2(0.5f, 1f);
            popup.anchoredPosition = localPos + new Vector2(xoffset, -yoffset);
        }
        else
        {

            popup.pivot = new Vector2(0.5f, 0f);
            popup.anchoredPosition = localPos + new Vector2(xoffset, yoffset);
        }

        popup.gameObject.SetActive(true);
        dimmedBackground.SetActive(true);
    }

    public void HidePopup()
    {
        popup.gameObject.SetActive(false);
        dimmedBackground.SetActive(false);
    }
}

스크롤뷰에 해당 스크립트를 부착하고, Background 패널에 버튼 추가 후 HidePopup 이벤트를 붙인다. 그리고 타겟 버튼에 ShowPopup 이벤트를 붙인다.

2.1 개선 코드 추가

위와 같은 방식은 canvas.renderMode 가 ScreenSpace.Overlay면 사용할 수 없는 방법이다. 또한 카메라까지 찾아서 진행해야 하는 지나치게 복잡한 방법이다. 따라서 다음과 같은 접근 방법을 추가했다.

  • ViewPoint의 중앙점을 기준으로 버튼의 위치가 중앙의 위쪽인지, 아래쪽인지 계산
  • 해당 결과를 바탕으로 피벗을 설정하고, 아래에 붙일지 위에 붙일지 계산 진행
using UnityEngine;

public class ScrollviewPopupUI : MonoBehaviour
{
    [Header("References")]
    [SerializeField] private RectTransform popup;
    [SerializeField] private GameObject dimmedBackground;

    [SerializeField] private RectTransform viewport;

    [Header("Settings")]
    [SerializeField] private float yoffset = 20f;
    [SerializeField] private float xoffset = -150f;

    private RectTransform popupParent;

    private void Awake()
    {
        popup.gameObject.SetActive(false);
        dimmedBackground.SetActive(false);

        popupParent = popup.parent as RectTransform;
    }

    public void ShowPopup(RectTransform target)
    {
        if (target == null) return;

        Vector3 viewLocalPos = viewport.InverseTransformPoint(target.position);

        float centerY = viewport.rect.center.y;

        bool isUpper = viewLocalPos.y > centerY;

        Vector3 worldPos = target.position;
        Vector3 localPos = popupParent.InverseTransformPoint(worldPos);

        if (isUpper)
        {
            popup.pivot = new Vector2(0.5f, 1f);
            localPos.y -= yoffset;
        }
        else
        {
            popup.pivot = new Vector2(0.5f, 0f);
            localPos.y += yoffset;
        }

        localPos.x += xoffset;

        popup.anchoredPosition = localPos;

        popup.gameObject.SetActive(true);
        dimmedBackground.SetActive(true);
    }

    public void HidePopup()
    {
        popup.gameObject.SetActive(false);
        dimmedBackground.SetActive(false);
    }
}

3. 결과

profile
게임 개발을 위한 화력 태우기

0개의 댓글