Files
bansonic_beta_main/Assets/scripts/UI/UIBackgroundCover.cs
T
2026-07-22 05:35:53 +08:00

101 lines
3.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using UnityEngine;
/// <summary>
/// 全屏背景/遮罩自适应(Cover):保持原始宽高比,等比放大到覆盖整个根 Canvas,超出部分裁切。
/// 过覆盖 => 任意宽高比都无缝铺满、且 sizeDelta 永不为 0;对纯色遮罩与艺术背景都正确、不失真。
/// 仅调整 sizeDelta(表现层),不改锚点/位置/pivot,不影响居中布局与位移动画。
/// 16:9 屏下结果等于设计尺寸,与原始逐像素一致。由 UIResolutionAutoFitter 自动附加。
/// </summary>
[RequireComponent(typeof(RectTransform))]
[DisallowMultipleComponent]
public class UIBackgroundCover : MonoBehaviour
{
public float sourceAspect = 16f / 9f;
private RectTransform rt;
private RectTransform coverTarget;
private float lastW = -1f, lastH = -1f;
private bool aspectResolved;
// 重入锁:设置 sizeDelta 会同步触发 OnRectTransformDimensionsChange
// 若不拦截会无限递归导致 StackOverflow 崩溃。
private bool _isFitting;
private void OnEnable()
{
rt = GetComponent<RectTransform>();
ResolveAspectFromInitialSize();
ResolveTarget();
Fit(true);
}
private void LateUpdate()
{
// 每帧检测 coverTarget 实际尺寸变化(而非只看 Screen):Unity 布局延迟更新,
// Screen 变化后 CanvasScaler 可能过几帧才把新尺寸写入 rootCanvas.rect。
// Fit(false) 内部按 lastW/lastH 判重,尺寸未变时零开销,无需额外节流。
if (coverTarget == null) ResolveTarget();
if (coverTarget != null)
{
float pw = coverTarget.rect.width;
float ph = coverTarget.rect.height;
if (!Mathf.Approximately(pw, lastW) || !Mathf.Approximately(ph, lastH))
{
Fit(false);
}
}
}
/// <summary>用元素初始 sizeDelta 推断原始宽高比,避免写死。</summary>
private void ResolveAspectFromInitialSize()
{
if (aspectResolved) return;
if (rt == null) rt = GetComponent<RectTransform>();
Vector2 s = rt.sizeDelta;
if (s.x > 1f && s.y > 1f) sourceAspect = s.x / s.y;
aspectResolved = true;
}
private void OnRectTransformDimensionsChange()
{
if (_isFitting) return; // 拦截自身写入触发的递归回调
Fit(false);
}
private void ResolveTarget()
{
Canvas canvas = GetComponentInParent<Canvas>();
if (canvas != null)
{
Canvas root = canvas.rootCanvas != null ? canvas.rootCanvas : canvas;
coverTarget = root.GetComponent<RectTransform>();
}
}
private void Fit(bool force)
{
if (_isFitting) return;
if (rt == null) rt = GetComponent<RectTransform>();
if (coverTarget == null) ResolveTarget();
if (coverTarget == null || sourceAspect <= 0f) return;
float pw = coverTarget.rect.width;
float ph = coverTarget.rect.height;
if (pw <= 0f || ph <= 0f) return;
if (!force && Mathf.Approximately(pw, lastW) && Mathf.Approximately(ph, lastH)) return;
lastW = pw; lastH = ph;
float targetAspect = pw / ph;
float w, h;
if (targetAspect > sourceAspect) { w = pw; h = pw / sourceAspect; }
else { h = ph; w = ph * sourceAspect; }
Vector2 newSize = new Vector2(w, h);
if (rt.sizeDelta != newSize)
{
_isFitting = true;
rt.sizeDelta = newSize;
_isFitting = false;
}
}
}