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


세부적인 기획 내용에 관해서는 들어봐야 할 것 같지만, 우선 조건을 아래와 같이 설정하여 미리 설계를 해 보았다.
먼저 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 이벤트를 붙인다.
위와 같은 방식은 canvas.renderMode 가 ScreenSpace.Overlay면 사용할 수 없는 방법이다. 또한 카메라까지 찾아서 진행해야 하는 지나치게 복잡한 방법이다. 따라서 다음과 같은 접근 방법을 추가했다.
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);
}
}
