好多新内容
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user