using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
///
/// 妞渾褰㈤伄缃╃粍浠?
/// 鍩轰簬妯℃澘娴嬭瘯瀹炵幇妞渾褰㈤伄缃╂晥鏋?
///
[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();
// 缂撳瓨GetComponentsInChildren缁撴灉锛岄伩鍏嶆瘡娆nEnable鍒嗛厤鏁扮粍
private static List _graphicCacheList = new List();
private bool m_MaskShaderLoading = false;
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();
}
}
}
static int order=1;
protected virtual void Awake()
{
order++;
StencilID = order;
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;
}
m_MaskShaderLoading = false;
// 绉婚櫎瀛愬璞$殑妯℃澘娴嬭瘯
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;
}
}
#if !UNITY_EDITOR
// 鍦╯hader寮傛鍔犺浇瀹屾垚鍓嶈涓哄叏閫忔槑锛岄槻姝㈤粯璁hader娓叉煋鍑虹櫧鑹茬煩褰?
if (m_MaskMaterial == null)
{
m_MaskImage.color = Color.clear;
}
#endif
}
///
/// 鍒涘缓閬僵鏉愯川
///
private void CreateMaskMaterial()
{
#if !UNITY_EDITOR
if (m_MaskMaterial == null && !m_MaskShaderLoading)
{
m_MaskShaderLoading = true;
ResManager.Instance.LoadAssetAsync("Shader", "GUI_EllipseMask").ContinueWith(ellipseShader =>
{
m_MaskShaderLoading = false;
if (this == null) { return; }
if (ellipseShader != null)
{
m_MaskMaterial = new Material(ellipseShader);
// shader使用ColorMask 0,恢复color不会显示颜色,但保证Canvas提交DrawCall写入stencil
if (m_MaskImage != null)
m_MaskImage.color = Color.white;
UpdateMask();
UpdateChildrenStencil();
}
}).Forget();
}
#else
if (m_MaskMaterial == null)
{
m_MaskMaterial = new Material(Shader.Find("GUI/EllipseMask"));
}
#endif
}
///
/// 鏇存柊閬僵
///
private void UpdateMask()
{
if (m_MaskMaterial != null && m_MaskImage != null)
{
UpdateMaterialProperties();
// 璁剧疆鏉愯川鍒癐mage
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;
}
}
///
/// 涓哄瓙瀵硅薄娣诲姞妯℃澘娴嬭瘯
///
public void UpdateChildrenStencil()
{
// 娓呴櫎涔嬪墠鐨勫垪琛?
m_MaskedChildren.Clear();
// 浣跨敤闈欐€佺紦瀛樺垪琛ㄩ伩鍏嶆瘡娆″垎閰嶆暟缁?
_graphicCacheList.Clear();
GetComponentsInChildren(false, _graphicCacheList);
for (int i = 0; i < _graphicCacheList.Count; i++)
{
var graphic = _graphicCacheList[i];
// 璺宠繃閬僵鏈韩
if (graphic.gameObject == this.gameObject)
continue;
// 涓哄瓙瀵硅薄鍒涘缓閬僵鏉愯川
CreateChildMaskMaterial(graphic);
m_MaskedChildren.Add(graphic);
}
_graphicCacheList.Clear();
}
///
/// 涓哄瓙瀵硅薄鍒涘缓閬僵鏉愯川
///
private void CreateChildMaskMaterial(Graphic graphic)
{
if (graphic.material == null || !graphic.material.shader.name.Contains("EllipseMaskedContent"))
{
#if !UNITY_EDITOR
ResManager.Instance.LoadAssetAsync("Shader", "GUI_EllipseMaskedContent").ContinueWith(maskedShader =>
{
if (this == null || graphic == null) { return; }
if (maskedShader != null)
{
Material maskedMaterial = new Material(maskedShader);
maskedMaterial.SetInt("_Stencil", m_StencilID);
maskedMaterial.SetInt("_StencilComp", 3); // Equal
graphic.material = maskedMaterial;
graphic.SetMaterialDirty();
}
}).Forget();
#else
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;
}
#endif
}
else
{
// 鏇存柊鐜版湁鏉愯川鐨勬ā鏉縄D
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
}