102 lines
4.0 KiB
C#
102 lines
4.0 KiB
C#
// UICustomBoundsFade.cs
|
||
// 挂在使用 UI/Default-Gama shader 的 Graphic 对象上。
|
||
// 指定一个目标 RectTransform,每帧将其世界坐标四角范围同步到 _CustomFadeRect。
|
||
// 当前 Graphic 会在该范围内渐变,边缘外完全透明。
|
||
|
||
using UnityEngine;
|
||
using UnityEngine.UI;
|
||
#if UNITY_EDITOR
|
||
using UnityEditor;
|
||
#endif
|
||
|
||
namespace NLD.UI
|
||
{
|
||
[RequireComponent(typeof(Graphic))]
|
||
[DisallowMultipleComponent]
|
||
public class UICustomBoundsFade : MonoBehaviour
|
||
{
|
||
[Tooltip("使用 UI-Default-Gama shader 的基础材质(必须指定,否则脚本不生效)")]
|
||
[SerializeField] private Material _baseMaterial;
|
||
|
||
[Tooltip("目标 GameObject 的 RectTransform,渐变范围以其世界坐标四角为准")]
|
||
public RectTransform targetRect;
|
||
|
||
[Tooltip("渐变过渡宽度(UV 分数):0 = 硬切边界,0.5 = 渐变延伸到 bounds 中心。与 bounds 物理尺寸无关")]
|
||
[Range(0f, 0.5f)]
|
||
public float softness = 0.1f;
|
||
|
||
[Tooltip("渐变曲线指数,1 = 线性,>1 慢起快收,<1 快起慢收")]
|
||
[Range(0.1f, 5f)]
|
||
public float curve = 1f;
|
||
|
||
// ── 内部状态 ──────────────────────────────────────────────────
|
||
private Graphic _graphic;
|
||
private Material _materialInstance;
|
||
|
||
private static readonly int _propRect = Shader.PropertyToID("_CustomFadeRect");
|
||
private static readonly int _propSoftness = Shader.PropertyToID("_CustomFadeSoftness");
|
||
private static readonly int _propCurve = Shader.PropertyToID("_CustomFadeCurve");
|
||
private const string _keyword = "USE_CUSTOM_BOUNDS_FADE";
|
||
|
||
// ── 生命周期 ──────────────────────────────────────────────────
|
||
private void OnEnable()
|
||
{
|
||
_graphic = GetComponent<Graphic>();
|
||
if (_graphic == null) return;
|
||
|
||
// 优先使用指定的 Gama 基础材质,否则回退到 Graphic 当前材质
|
||
Material original = _baseMaterial != null ? _baseMaterial : _graphic.material;
|
||
_materialInstance = new Material(original)
|
||
{
|
||
name = original.name + "_CustomBoundsFade"
|
||
};
|
||
_materialInstance.EnableKeyword(_keyword);
|
||
_graphic.material = _materialInstance;
|
||
|
||
SyncToMaterial();
|
||
}
|
||
|
||
private void OnDisable()
|
||
{
|
||
if (_graphic != null)
|
||
_graphic.material = null; // 置 null 即回退到 defaultMaterial
|
||
|
||
if (_materialInstance != null)
|
||
{
|
||
DestroyImmediate(_materialInstance);
|
||
_materialInstance = null;
|
||
}
|
||
}
|
||
|
||
private void LateUpdate() => SyncToMaterial();
|
||
|
||
// ── 核心同步 ──────────────────────────────────────────────────
|
||
private void SyncToMaterial()
|
||
{
|
||
if (_materialInstance == null || targetRect == null) return;
|
||
|
||
// GetWorldCorners: [0]=BL [1]=TL [2]=TR [3]=BR
|
||
var corners = new Vector3[4];
|
||
targetRect.GetWorldCorners(corners);
|
||
|
||
float xMin = corners[0].x;
|
||
float yMin = corners[0].y;
|
||
float xMax = corners[2].x;
|
||
float yMax = corners[2].y;
|
||
|
||
_materialInstance.SetVector(_propRect, new Vector4(xMin, yMin, xMax, yMax));
|
||
_materialInstance.SetFloat (_propSoftness, softness);
|
||
_materialInstance.SetFloat (_propCurve, curve);
|
||
}
|
||
|
||
#if UNITY_EDITOR
|
||
// Editor 下实时预览(OnValidate 在 inspector 改值时调用)
|
||
private void OnValidate()
|
||
{
|
||
if (!Application.isPlaying && _materialInstance != null)
|
||
SyncToMaterial();
|
||
}
|
||
#endif
|
||
}
|
||
}
|