using UnityEngine;
using UnityEngine.UI;
using GoogleMobileAds.Api;
[RequireComponent(typeof(CanvasScaler))]
public class CanvasResolutionController : MonoBehaviour
{
private GoogleAdMobManager AdManager => GoogleAdMobManager.Instance;
private CanvasScaler _CanvasScaler;
private RectTransform safeAreaWrapper;
private RectTransform uiContainer;
private RectTransform adBannerSpace;
private RectTransform contentArea;
private const float TARGET_ASPECT = 1440f / 2560f; // 9:16
// 캐싱
private Rect lastSafeArea;
private Vector2 lastScreenSize;
private bool lastAdStatus;
// ------------------
void Awake()
{
_CanvasScaler = GetComponent<CanvasScaler>();
_CanvasScaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
_CanvasScaler.referenceResolution = new Vector2(1440, 2560);
_CanvasScaler.screenMatchMode = CanvasScaler.ScreenMatchMode.MatchWidthOrHeight;
CreateUIHierarchy();
if (AdManager != null)
AdManager.OnBannerAdLoaded += ApplySafeLayout;
}
void Start() => ApplySafeLayout();
void LateUpdate()
{
if (CheckForChanges())
{
ApplySafeLayout();
}
}
/// <summary>
/// 레이아웃 업데이트가 필요한 변화가 있는지 확인
/// </summary>
private bool CheckForChanges()
{
bool isChanged = false;
// 1. 화면 해상도 체크
if (Screen.width != lastScreenSize.x || Screen.height != lastScreenSize.y)
{
lastScreenSize = new Vector2(Screen.width, Screen.height);
isChanged = true;
}
// 2. Safe Area 체크
if (Screen.safeArea != lastSafeArea)
{
lastSafeArea = Screen.safeArea;
isChanged = true;
}
// 3. 광고 로드 상태 체크
bool currentAdStatus = (AdManager != null && AdManager.IsBannerLoaded);
if (currentAdStatus != lastAdStatus)
{
lastAdStatus = currentAdStatus;
isChanged = true;
}
return isChanged;
}
void CreateUIHierarchy()
{
safeAreaWrapper = CreateRectElement("Safe_Area_Wrapper", transform);
uiContainer = CreateRectElement("UI_Container", safeAreaWrapper);
adBannerSpace = CreateRectElement("AdBannerSpace", uiContainer);
contentArea = CreateRectElement("ContentArea", uiContainer);
var fitter = contentArea.gameObject.AddComponent<AspectRatioFitter>();
fitter.aspectRatio = TARGET_ASPECT;
fitter.aspectMode = AspectRatioFitter.AspectMode.FitInParent;
fitter.enabled = true;
}
public void ApplySafeLayout()
{
if (safeAreaWrapper == null || contentArea == null) return;
Rect safe = Screen.safeArea;
float sw = Screen.width;
float sh = Screen.height;
// 1. Safe Area 적용
safeAreaWrapper.anchorMin = new Vector2(safe.x / sw, safe.y / sh);
safeAreaWrapper.anchorMax = new Vector2((safe.x + safe.width) / sw, (safe.y + safe.height) / sh);
ResetOffsets(safeAreaWrapper);
// 2. 광고 높이 계산
float adHeightPx = lastAdStatus ? GetAdBannerHeightInPixels() : 0f;
float adHeightNormalized = adHeightPx / safe.height;
// 3. 광고 및 콘텐츠 영역 분할
adBannerSpace.anchorMin = new Vector2(0, 1 - adHeightNormalized);
adBannerSpace.anchorMax = new Vector2(1, 1);
ResetOffsets(adBannerSpace);
contentArea.anchorMin = Vector2.zero;
contentArea.anchorMax = new Vector2(1, 1 - adHeightNormalized);
ResetOffsets(contentArea);
// 4. 동적 Match 수정
UpdateCanvasMatch(safe.width, safe.height - adHeightPx);
Debug.Log($"[LayoutUpdate] Match: {_CanvasScaler.matchWidthOrHeight}, AdHeight: {adHeightPx}");
}
private void UpdateCanvasMatch(float availableW, float availableH)
{
if (availableW <= 0 || availableH <= 0) return;
float availableAspect = availableW / availableH;
// 가로가 남는 환경(패드) -> 세로에 맞춤
if (availableAspect > TARGET_ASPECT)
{
_CanvasScaler.matchWidthOrHeight = 1f;
}
else // 세로가 남는 환경(플립) -> 가로에 맞춤
{
_CanvasScaler.matchWidthOrHeight = 0f;
}
}
private RectTransform CreateRectElement(string name, Transform parent)
{
GameObject obj = new GameObject(name, typeof(RectTransform));
obj.transform.SetParent(parent, false);
RectTransform rt = obj.GetComponent<RectTransform>();
rt.anchorMin = Vector2.zero;
rt.anchorMax = Vector2.one;
ResetOffsets(rt);
return rt;
}
private void ResetOffsets(RectTransform rt) => rt.offsetMin = rt.offsetMax = Vector2.zero;
private float GetAdBannerHeightInPixels()
{
int widthInDp = MobileAds.Utils.GetDeviceSafeWidth();
AdSize adaptiveSize = AdSize.GetCurrentOrientationAnchoredAdaptiveBannerAdSizeWithWidth(widthInDp);
return (float)adaptiveSize.Height * Screen.dpi / 160f;
}
void OnDestroy()
{
if (AdManager != null)
AdManager.OnBannerAdLoaded -= ApplySafeLayout;
}
}