hch
1 天以前 994cda07491be0e460c78bd9c8400780530ad07c
0312 增加椭圆形遮罩
2个文件已添加
318 ■■■■■ 已修改文件
Main/Component/UI/Effect/EllipseMask.cs 307 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Component/UI/Effect/EllipseMask.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Component/UI/Effect/EllipseMask.cs
New file
@@ -0,0 +1,307 @@
using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;
/// <summary>
/// 椭圆形遮罩组件
/// 基于模板测试实现椭圆形遮罩效果
/// </summary>
[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<Graphic> m_MaskedChildren = new List<Graphic>();
    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<RectTransform>();
        // 创建遮罩图像
        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);
        }
    }
    /// <summary>
    /// 创建遮罩图像
    /// </summary>
    private void CreateMaskImage()
    {
        if (m_MaskImage == null)
        {
            m_MaskImage = GetComponent<Image>();
            if (m_MaskImage == null)
            {
                m_MaskImage = gameObject.AddComponent<Image>();
                m_MaskImage.color = Color.white;
            }
        }
    }
    /// <summary>
    /// 创建遮罩材质
    /// </summary>
    private void CreateMaskMaterial()
    {
        if (m_MaskMaterial == null)
        {
            Shader ellipseShader = Shader.Find("GUI/EllipseMask");
            if (ellipseShader != null)
            {
                m_MaskMaterial = new Material(ellipseShader);
            }
            else
            {
                Debug.LogError("EllipseMask shader not found!");
            }
        }
    }
    /// <summary>
    /// 更新遮罩
    /// </summary>
    private void UpdateMask()
    {
        if (m_MaskMaterial != null && m_MaskImage != null)
        {
            UpdateMaterialProperties();
            // 设置材质到Image
            m_MaskImage.material = m_MaskMaterial;
            // 强制重绘
            m_MaskImage.SetMaterialDirty();
        }
    }
    /// <summary>
    /// 更新材质属性
    /// </summary>
    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);
        }
    }
    /// <summary>
    /// 更新遮罩可见性
    /// </summary>
    private void UpdateMaskVisibility()
    {
        if (m_MaskImage != null)
        {
            m_MaskImage.enabled = m_ShowMaskGraphic;
        }
    }
    /// <summary>
    /// 为子对象添加模板测试
    /// </summary>
    private void UpdateChildrenStencil()
    {
        // 清除之前的列表
        m_MaskedChildren.Clear();
        // 获取所有子对象的Graphic组件
        Graphic[] childrenGraphics = GetComponentsInChildren<Graphic>();
        foreach (var graphic in childrenGraphics)
        {
            // 跳过遮罩本身
            if (graphic.gameObject == this.gameObject)
                continue;
            // 为子对象创建遮罩材质
            CreateChildMaskMaterial(graphic);
            m_MaskedChildren.Add(graphic);
        }
    }
    /// <summary>
    /// 为子对象创建遮罩材质
    /// </summary>
    private void CreateChildMaskMaterial(Graphic graphic)
    {
        if (graphic.material == null || !graphic.material.shader.name.Contains("EllipseMaskedContent"))
        {
            Shader maskedShader = Shader.Find("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);
        }
    }
    /// <summary>
    /// 移除子对象的模板测试
    /// </summary>
    private void RemoveChildrenStencil()
    {
        foreach (var graphic in m_MaskedChildren)
        {
            if (graphic != null && graphic.material != null)
            {
                // 重置材质
                graphic.material = null;
            }
        }
        m_MaskedChildren.Clear();
    }
    /// <summary>
    /// 设置椭圆参数
    /// </summary>
    /// <param name="center">椭圆中心(归一化坐标)</param>
    /// <param name="radius">椭圆半径(归一化坐标)</param>
    /// <param name="softness">边缘柔化程度</param>
    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();
    }
    /// <summary>
    /// 设置圆形参数(特殊情况的椭圆)
    /// </summary>
    /// <param name="center">圆心(归一化坐标)</param>
    /// <param name="radius">半径(归一化坐标)</param>
    /// <param name="softness">边缘柔化程度</param>
    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
}
Main/Component/UI/Effect/EllipseMask.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d7b138cff435daf4e97efe03e10a756b
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant: