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 }