| | |
| | | using UnityEngine; |
| | | 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("椭圆参数")] |
| | | |
| | | |
| | | [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] [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结果,避免每次OnEnable分配数组 |
| | | // 缂撳瓨GetComponentsInChildren缁撴灉锛岄伩鍏嶆瘡娆nEnable鍒嗛厤鏁扮粍 |
| | | private static List<Graphic> _graphicCacheList = new List<Graphic>(); |
| | | private bool m_MaskShaderLoading = false; |
| | | |
| | | public Vector2 EllipseCenter |
| | | { |
| | |
| | | StencilID = order; |
| | | m_RectTransform = GetComponent<RectTransform>(); |
| | | |
| | | // 创建遮罩图像 |
| | | // 鍒涘缓閬僵鍥惧儚 |
| | | CreateMaskImage(); |
| | | |
| | | // 创建遮罩材质 |
| | | // 鍒涘缓閬僵鏉愯川 |
| | | CreateMaskMaterial(); |
| | | |
| | | UpdateMask(); |
| | |
| | | CreateMaskMaterial(); |
| | | UpdateMask(); |
| | | |
| | | // 为所有子对象添加模板测试 |
| | | // 涓烘墍鏈夊瓙瀵硅薄娣诲姞妯℃澘娴嬭瘯 |
| | | UpdateChildrenStencil(); |
| | | } |
| | | |
| | |
| | | DestroyImmediate(m_MaskMaterial); |
| | | m_MaskMaterial = null; |
| | | } |
| | | m_MaskShaderLoading = false; |
| | | |
| | | // 移除子对象的模板测试 |
| | | // 绉婚櫎瀛愬璞$殑妯℃澘娴嬭瘯 |
| | | RemoveChildrenStencil(); |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 创建遮罩图像 |
| | | /// 鍒涘缓閬僵鍥惧儚 |
| | | /// </summary> |
| | | private void CreateMaskImage() |
| | | { |
| | |
| | | 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) |
| | | 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); |
| | | } |
| | | else |
| | | { |
| | | Debug.LogError("EllipseMask shader not found!"); |
| | | // shader使用ColorMask 0,恢复color不会显示颜色,但保证Canvas提交DrawCall写入stencil |
| | | if (m_MaskImage != null) |
| | | m_MaskImage.color = Color.white; |
| | | UpdateMask(); |
| | | UpdateChildrenStencil(); |
| | | } |
| | | }).Forget(); |
| | | } |
| | |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 更新遮罩 |
| | | /// 鏇存柊閬僵 |
| | | /// </summary> |
| | | private void UpdateMask() |
| | | { |
| | |
| | | { |
| | | UpdateMaterialProperties(); |
| | | |
| | | // 设置材质到Image |
| | | // 璁剧疆鏉愯川鍒癐mage |
| | | m_MaskImage.material = m_MaskMaterial; |
| | | |
| | | // 强制重绘 |
| | | // 寮哄埗閲嶇粯 |
| | | m_MaskImage.SetMaterialDirty(); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 更新材质属性 |
| | | /// 鏇存柊鏉愯川灞炴€? |
| | | /// </summary> |
| | | private void UpdateMaterialProperties() |
| | | { |
| | |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 更新遮罩可见性 |
| | | /// 鏇存柊閬僵鍙鎬? |
| | | /// </summary> |
| | | private void UpdateMaskVisibility() |
| | | { |
| | |
| | | } |
| | | |
| | | /// <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); |
| | | } |
| | |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 为子对象创建遮罩材质 |
| | | /// 涓哄瓙瀵硅薄鍒涘缓閬僵鏉愯川 |
| | | /// </summary> |
| | | private void CreateChildMaskMaterial(Graphic graphic) |
| | | { |
| | |
| | | { |
| | | |
| | | #if !UNITY_EDITOR |
| | | // Shader maskedShader = Shader.Find("GUI/EllipseMaskedContent"); |
| | | 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 |
| | |
| | | } |
| | | else |
| | | { |
| | | // 更新现有材质的模板ID |
| | | // 鏇存柊鐜版湁鏉愯川鐨勬ā鏉縄D |
| | | graphic.material.SetInt("_Stencil", m_StencilID); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 移除子对象的模板测试 |
| | | /// 绉婚櫎瀛愬璞$殑妯℃澘娴嬭瘯 |
| | | /// </summary> |
| | | private void RemoveChildrenStencil() |
| | | { |
| | |
| | | { |
| | | if (graphic != null && graphic.material != null) |
| | | { |
| | | // 重置材质 |
| | | // 閲嶇疆鏉愯川 |
| | | graphic.material = null; |
| | | } |
| | | } |
| | |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 设置椭圆参数 |
| | | /// 璁剧疆妞渾鍙傛暟 |
| | | /// </summary> |
| | | /// <param name="center">椭圆中心(归一化坐标)</param> |
| | | /// <param name="radius">椭圆半径(归一化坐标)</param> |
| | | /// <param name="softness">边缘柔化程度</param> |
| | | /// <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; |
| | |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 设置圆形参数(特殊情况的椭圆) |
| | | /// 璁剧疆鍦嗗舰鍙傛暟锛堢壒娈婃儏鍐电殑妞渾锛? |
| | | /// </summary> |
| | | /// <param name="center">圆心(归一化坐标)</param> |
| | | /// <param name="radius">半径(归一化坐标)</param> |
| | | /// <param name="softness">边缘柔化程度</param> |
| | | /// <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); |
| | |
| | | } |
| | | } |
| | | #endif |
| | | } |
| | | } |