using UnityEngine;
|
using UnityEngine.UI;
|
using System.Collections.Generic;
|
using Cysharp.Threading.Tasks;
|
|
/// <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>();
|
// 缂撳瓨GetComponentsInChildren缁撴灉锛岄伩鍏嶆瘡娆nEnable鍒嗛厤鏁扮粍
|
private static List<Graphic> _graphicCacheList = new List<Graphic>();
|
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<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;
|
}
|
m_MaskShaderLoading = false;
|
|
// 绉婚櫎瀛愬璞$殑妯℃澘娴嬭瘯
|
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;
|
}
|
}
|
#if !UNITY_EDITOR
|
// 鍦╯hader寮傛鍔犺浇瀹屾垚鍓嶈涓哄叏閫忔槑锛岄槻姝㈤粯璁hader娓叉煋鍑虹櫧鑹茬煩褰?
|
if (m_MaskMaterial == null)
|
{
|
m_MaskImage.color = Color.clear;
|
}
|
#endif
|
}
|
|
/// <summary>
|
/// 鍒涘缓閬僵鏉愯川
|
/// </summary>
|
private void CreateMaskMaterial()
|
{
|
#if !UNITY_EDITOR
|
if (m_MaskMaterial == null && !m_MaskShaderLoading)
|
{
|
m_MaskShaderLoading = true;
|
ResManager.Instance.LoadAssetAsync<Shader>("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
|
}
|
|
/// <summary>
|
/// 鏇存柊閬僵
|
/// </summary>
|
private void UpdateMask()
|
{
|
if (m_MaskMaterial != null && m_MaskImage != null)
|
{
|
UpdateMaterialProperties();
|
|
// 璁剧疆鏉愯川鍒癐mage
|
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>
|
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();
|
}
|
|
/// <summary>
|
/// 涓哄瓙瀵硅薄鍒涘缓閬僵鏉愯川
|
/// </summary>
|
private void CreateChildMaskMaterial(Graphic graphic)
|
{
|
if (graphic.material == null || !graphic.material.shader.name.Contains("EllipseMaskedContent"))
|
{
|
|
#if !UNITY_EDITOR
|
ResManager.Instance.LoadAssetAsync<Shader>("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);
|
}
|
}
|
|
/// <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
|
}
|