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
|
}
|