using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;
///
/// 椭圆形遮罩组件
/// 基于模板测试实现椭圆形遮罩效果
///
[RequireComponent(typeof(RectTransform))]
[AddComponentMenu("UI/Effects/Ellipse Mask")]
[ExecuteInEditMode]
public class EllipseMask : MonoBehaviour
{
[Header("椭圆参数")]
[SerializeField] private Vector2 m_EllipseCenter = new Vector2(0.5f, 0.5f);
[SerializeField] private Vector2 m_EllipseRadius = new Vector2(0.5f, 0.5f);
[SerializeField] [Range(0, 0.5f)] private float m_Softness = 0f; //暂无效果
[SerializeField] private int m_StencilID = 1; //多个遮罩时
[SerializeField] private bool m_ShowMaskGraphic = false;
private Material m_MaskMaterial;
private Image m_MaskImage;
private RectTransform m_RectTransform;
private List m_MaskedChildren = new List();
public Vector2 EllipseCenter
{
get => m_EllipseCenter;
set
{
if (m_EllipseCenter != value)
{
m_EllipseCenter = value;
UpdateMask();
}
}
}
public Vector2 EllipseRadius
{
get => m_EllipseRadius;
set
{
if (m_EllipseRadius != value)
{
m_EllipseRadius = value;
UpdateMask();
}
}
}
public float Softness
{
get => m_Softness;
set
{
if (m_Softness != value)
{
m_Softness = Mathf.Clamp(value, 0, 0.5f);
UpdateMask();
}
}
}
public int StencilID
{
get => m_StencilID;
set
{
if (m_StencilID != value)
{
m_StencilID = value;
UpdateMask();
UpdateChildrenStencil();
}
}
}
public bool ShowMaskGraphic
{
get => m_ShowMaskGraphic;
set
{
if (m_ShowMaskGraphic != value)
{
m_ShowMaskGraphic = value;
UpdateMaskVisibility();
}
}
}
protected virtual void Awake()
{
m_RectTransform = GetComponent();
// 创建遮罩图像
CreateMaskImage();
// 创建遮罩材质
CreateMaskMaterial();
UpdateMask();
}
protected virtual void OnEnable()
{
CreateMaskMaterial();
UpdateMask();
// 为所有子对象添加模板测试
UpdateChildrenStencil();
}
protected virtual void OnDisable()
{
if (m_MaskMaterial != null)
{
DestroyImmediate(m_MaskMaterial);
m_MaskMaterial = null;
}
// 移除子对象的模板测试
RemoveChildrenStencil();
}
protected virtual void OnDestroy()
{
if (m_MaskMaterial != null)
{
DestroyImmediate(m_MaskMaterial);
}
}
///
/// 创建遮罩图像
///
private void CreateMaskImage()
{
if (m_MaskImage == null)
{
m_MaskImage = GetComponent();
if (m_MaskImage == null)
{
m_MaskImage = gameObject.AddComponent();
m_MaskImage.color = Color.white;
}
}
}
///
/// 创建遮罩材质
///
private void CreateMaskMaterial()
{
if (m_MaskMaterial == null)
{
Shader ellipseShader = ResManager.Instance.LoadAsset("Shader", "GUI_EllipseMask");
if (ellipseShader != null)
{
m_MaskMaterial = new Material(ellipseShader);
}
else
{
Debug.LogError("EllipseMask shader not found!");
}
}
}
///
/// 更新遮罩
///
private void UpdateMask()
{
if (m_MaskMaterial != null && m_MaskImage != null)
{
UpdateMaterialProperties();
// 设置材质到Image
m_MaskImage.material = m_MaskMaterial;
// 强制重绘
m_MaskImage.SetMaterialDirty();
}
}
///
/// 更新材质属性
///
private void UpdateMaterialProperties()
{
if (m_MaskMaterial != null)
{
m_MaskMaterial.SetVector("_EllipseCenter", new Vector4(m_EllipseCenter.x, m_EllipseCenter.y, 0, 0));
m_MaskMaterial.SetVector("_EllipseRadius", new Vector4(m_EllipseRadius.x, m_EllipseRadius.y, 0, 0));
m_MaskMaterial.SetFloat("_Softness", m_Softness);
m_MaskMaterial.SetInt("_Stencil", m_StencilID);
}
}
///
/// 更新遮罩可见性
///
private void UpdateMaskVisibility()
{
if (m_MaskImage != null)
{
m_MaskImage.enabled = m_ShowMaskGraphic;
}
}
///
/// 为子对象添加模板测试
///
private void UpdateChildrenStencil()
{
// 清除之前的列表
m_MaskedChildren.Clear();
// 获取所有子对象的Graphic组件
Graphic[] childrenGraphics = GetComponentsInChildren();
foreach (var graphic in childrenGraphics)
{
// 跳过遮罩本身
if (graphic.gameObject == this.gameObject)
continue;
// 为子对象创建遮罩材质
CreateChildMaskMaterial(graphic);
m_MaskedChildren.Add(graphic);
}
}
///
/// 为子对象创建遮罩材质
///
private void CreateChildMaskMaterial(Graphic graphic)
{
if (graphic.material == null || !graphic.material.shader.name.Contains("EllipseMaskedContent"))
{
// Shader maskedShader = Shader.Find("GUI/EllipseMaskedContent");
Shader maskedShader = ResManager.Instance.LoadAsset("Shader", "GUI_EllipseMaskedContent");
if (maskedShader != null)
{
Material maskedMaterial = new Material(maskedShader);
maskedMaterial.SetInt("_Stencil", m_StencilID);
maskedMaterial.SetInt("_StencilComp", 3); // Equal
graphic.material = maskedMaterial;
}
}
else
{
// 更新现有材质的模板ID
graphic.material.SetInt("_Stencil", m_StencilID);
}
}
///
/// 移除子对象的模板测试
///
private void RemoveChildrenStencil()
{
foreach (var graphic in m_MaskedChildren)
{
if (graphic != null && graphic.material != null)
{
// 重置材质
graphic.material = null;
}
}
m_MaskedChildren.Clear();
}
///
/// 设置椭圆参数
///
/// 椭圆中心(归一化坐标)
/// 椭圆半径(归一化坐标)
/// 边缘柔化程度
public void SetEllipseParameters(Vector2 center, Vector2 radius, float softness = 0.1f)
{
m_EllipseCenter = center;
m_EllipseRadius = radius;
m_Softness = Mathf.Clamp(softness, 0, 0.5f);
UpdateMask();
}
///
/// 设置圆形参数(特殊情况的椭圆)
///
/// 圆心(归一化坐标)
/// 半径(归一化坐标)
/// 边缘柔化程度
public void SetCircleParameters(Vector2 center, float radius, float softness = 0.1f)
{
SetEllipseParameters(center, new Vector2(radius, radius), softness);
}
#if UNITY_EDITOR
protected virtual void OnValidate()
{
if (m_MaskMaterial != null)
{
UpdateMaterialProperties();
UpdateChildrenStencil();
}
}
#endif
}