hch
2025-09-02 c5adf3e22a5cdfb185a4befc22ef0cd079a7eb33
Main/Component/UI/Core/OutlineEx.cs
@@ -40,37 +40,34 @@
    public int OutlineWidth = 10;    
    private static List<UIVertex> m_VetexList = new List<UIVertex>();
    // 材质池:Key为"颜色_宽度"格式的字符串,Value为对应的材质; 减少合批问题,又能保持不同的描边;共用材质会同时改变参数
    private static Dictionary<string, Material> m_MaterialPool = new Dictionary<string, Material>();
    static Shader m_tmpShader;
    private static Shader m_Shader
    protected override void Awake()
    {
        get
        base.Awake();
        if (base.graphic)
        {
            if (m_tmpShader == null)
            if (base.graphic.material == null || base.graphic.material.shader.name != "TSF Shaders/UI/OutlineEx")
            {
                m_tmpShader = Shader.Find("TSF Shaders/UI/OutlineEx");
// #if UNITY_EDITOR
                var texMaterial = ResManager.Instance.LoadAsset<Material>("Materials", "OutlineMat");
                if (texMaterial != null)
                {
                    base.graphic.material = texMaterial;
            }
            return m_tmpShader;
                else
                {
                    Debug.LogError("没有找到材质OutlineMat.mat");
        }
// #else
//                 var shader = Shader.Find("TSF Shaders/UI/OutlineEx");
//                 base.graphic.material = new Material(shader);
// #endif
    }
    protected override void Start()
            if (base.graphic.canvas)
    {
        base.Start();
        // 使用材质池获取或创建材质
        string key = GetMaterialKey(OutlineColor, OutlineWidth);
        if (!m_MaterialPool.TryGetValue(key, out Material material))
        {
            material = new Material(m_Shader);
            material.SetColor("_OutlineColor", this.OutlineColor);
            material.SetInt("_OutlineWidth", this.OutlineWidth);
            m_MaterialPool[key] = material;
        }
        base.graphic.material = material;
        var v1 = base.graphic.canvas.additionalShaderChannels;
        var v2 = AdditionalCanvasShaderChannels.TexCoord1;
        if ((v1 & v2) != v2)
@@ -82,24 +79,47 @@
        {
            base.graphic.canvas.additionalShaderChannels |= v2;
        }
                v2 = AdditionalCanvasShaderChannels.TexCoord3;
                if ((v1 & v2) != v2)
                {
                    base.graphic.canvas.additionalShaderChannels |= v2;
                }
                v2 = AdditionalCanvasShaderChannels.Tangent;
                if ((v1 & v2) != v2)
                {
                    base.graphic.canvas.additionalShaderChannels |= v2;
                }
                v2 = AdditionalCanvasShaderChannels.Normal;
                if ((v1 & v2) != v2)
                {
                    base.graphic.canvas.additionalShaderChannels |= v2;
                }
            }
        this._Refresh();
        }
    }
#if UNITY_EDITOR
    //在编辑器下打开也刷新下
    // protected override void OnEnable()
    // {
    //     base.OnEnable();
    //     this._Refresh();
    // }
    protected override void OnValidate()
    {
        base.OnValidate();
        if (base.graphic.material != null)
        {
            if (base.graphic.material.shader.name != "TSF Shaders/UI/OutlineEx")
            {
                var texMaterial = ResManager.Instance.LoadAsset<Material>("Materials", "OutlineMat");
                if (texMaterial != null)
                {
                    base.graphic.material = texMaterial;
                }
                else
                {
                    Debug.LogError("没有找到材质OutlineMat.mat");
                }
                //var shader = Shader.Find("TSF Shaders/UI/OutlineEx");
                //base.graphic.material = new Material(shader);
            }
            this._Refresh();
        }
    }
@@ -108,29 +128,11 @@
    private void _Refresh()
    {
        // 检查当前材质是否与所需属性匹配,如果不匹配则从池中获取或创建新材质
        string key = GetMaterialKey(OutlineColor, OutlineWidth);
        Material material;
        if (!m_MaterialPool.TryGetValue(key, out material))
        /*if (base.graphic.material.GetInt("_OutlineWidth") != this.OutlineWidth || base.graphic.material.GetColor("_OutlineColor") != this.OutlineColor)
        {
            // 如果池中没有对应的材质,创建新材质
            var shader = Shader.Find("TSF Shaders/UI/OutlineEx");
            material = new Material(shader);
            m_MaterialPool[key] = material;
        }
        if (material == null)
        {
            // 防范材质被删的情况,创建新材质
            material = new Material(m_Shader);
            m_MaterialPool[key] = material;
        }
        material.SetColor("_OutlineColor", this.OutlineColor);
        material.SetInt("_OutlineWidth", this.OutlineWidth);
        // 更新图形材质
        base.graphic.material = material;
            base.graphic.material.SetColor("_OutlineColor", this.OutlineColor);
            base.graphic.material.SetInt("_OutlineWidth", this.OutlineWidth);
        }*/
        base.graphic.SetVerticesDirty();
    }
@@ -182,82 +184,30 @@
                uvY = v2.uv0 - v1.uv0;
            }
            // 计算原始UV框
            //
            var uvMin = _Min(v1.uv0, v2.uv0, v3.uv0);
            var uvMax = _Max(v1.uv0, v2.uv0, v3.uv0);
            var uvOrigin = new Vector4(uvMin.x, uvMin.y, uvMax.x, uvMax.y);
                //OutlineColor 和 OutlineWidth 也传入,避免出现不同的材质球
                var col_rg = new Vector2(OutlineColor.r, OutlineColor.g);       //描边颜色 用uv3 和 tangent的 zw传递
                var col_ba = new Vector4(0,0,OutlineColor.b, OutlineColor.a);
                var normal = new Vector3(0, 0, OutlineWidth);                   //描边的宽度 用normal的z传递
            // 为每个顶点设置新的Position和UV,并传入原始UV框
            v1 = _SetNewPosAndUV(v1, this.OutlineWidth, posCenter, triX, triY, uvX, uvY, uvOrigin);
            v2 = _SetNewPosAndUV(v2, this.OutlineWidth, posCenter, triX, triY, uvX, uvY, uvOrigin);
            v3 = _SetNewPosAndUV(v3, this.OutlineWidth, posCenter, triX, triY, uvX, uvY, uvOrigin);
            // 获取 RectMask2D 的边界
            // 确保描边顶点扩展不会超出 RectMask2D 的边界
            var mask = GetComponentInParent<RectMask2D>();
            if (mask != null)
            {
                // 将局部坐标转换为屏幕坐标
                Transform uiTransform = this.transform;
                Vector3 worldPos1 = uiTransform.TransformPoint(v1.position);
                Vector3 worldPos2 = uiTransform.TransformPoint(v2.position);
                Vector3 worldPos3 = uiTransform.TransformPoint(v3.position);
                Vector2 screenPos1 = CameraManager.uiCamera.WorldToScreenPoint(worldPos1);
                Vector2 screenPos2 = CameraManager.uiCamera.WorldToScreenPoint(worldPos2);
                Vector2 screenPos3 = CameraManager.uiCamera.WorldToScreenPoint(worldPos3);
                // 确保描边顶点扩展不会超出 RectMask2D 的边界
                var clipRect = mask.canvasRect;
                //clipRect是中心点为坐标0,0,转成屏幕的
                var maskMinX = Screen.width / 2 + clipRect.xMin;
                var maskMaxX = Screen.width / 2 + clipRect.xMax;
                var maskMinY = Screen.height / 2 + clipRect.yMin;
                var maskMaxY = Screen.height / 2 + clipRect.yMax;
                // //screenPos1 超出clipRect范围不显示
                bool isout = false;
                if (screenPos3.x < maskMinX || screenPos3.x > maskMaxX || screenPos3.y < maskMinY || screenPos3.y > maskMaxY)
                {
                    isout = true;
                }
                else if (screenPos2.x < maskMinX || screenPos2.x > maskMaxX || screenPos2.y < maskMinY || screenPos2.y > maskMaxY)
                {
                    isout = true;
                }
                else if (screenPos1.x < maskMinX || screenPos1.x > maskMaxX || screenPos1.y < maskMinY || screenPos1.y > maskMaxY)
                {
                    isout = true;
                }
                if (isout)
                {
                    v1.position = new Vector3(10000, 10000, 10000);
                    v2.position = new Vector3(10000, 10000, 10000);
                    v3.position = new Vector3(10000, 10000, 10000);
                }
                // screenPos1.x = Mathf.Clamp(screenPos1.x, maskMinX, maskMaxX);
                // screenPos1.y = Mathf.Clamp(screenPos1.y, maskMinY, maskMaxY);
                // screenPos2.x = Mathf.Clamp(screenPos2.x, maskMinX, maskMaxX);
                // screenPos2.y = Mathf.Clamp(screenPos2.y, maskMinY, maskMaxY);
                // screenPos3.x = Mathf.Clamp(screenPos3.x, maskMinX, maskMaxX);
                // screenPos3.y = Mathf.Clamp(screenPos3.y, maskMinY, maskMaxY);
                // // 将屏幕坐标转换为局部坐标
                // RectTransformUtility.ScreenPointToLocalPointInRectangle(uiTransform.GetComponent<RectTransform>(), screenPos1, CameraManager.uiCamera, out Vector2 localPos1);
                // RectTransformUtility.ScreenPointToLocalPointInRectangle(uiTransform.GetComponent<RectTransform>(), screenPos2, CameraManager.uiCamera, out Vector2 localPos2);
                // RectTransformUtility.ScreenPointToLocalPointInRectangle(uiTransform.GetComponent<RectTransform>(), screenPos3, CameraManager.uiCamera, out Vector2 localPos3);
                // v1.position = localPos1;
                // v2.position = localPos2;
                // v3.position = localPos3;
            }
                v1 = _SetNewPosAndUV(v1, this.OutlineWidth, posCenter, triX, triY, uvX, uvY, uvMin, uvMax);
                v1.uv3 = col_rg;
                v1.tangent = col_ba;
                v1.normal = normal;
                v2 = _SetNewPosAndUV(v2, this.OutlineWidth, posCenter, triX, triY, uvX, uvY, uvMin, uvMax);
                v2.uv3 = col_rg;
                v2.tangent = col_ba;
                v2.normal = normal;
                v3 = _SetNewPosAndUV(v3, this.OutlineWidth, posCenter, triX, triY, uvX, uvY, uvMin, uvMax);
                v3.uv3 = col_rg;
                v3.tangent = col_ba;
                v3.normal = normal;
            // 应用设置后的UIVertex
                //
            m_VetexList[i] = v1;
            m_VetexList[i + 1] = v2;
            m_VetexList[i + 2] = v3;
@@ -269,7 +219,7 @@
        Vector2 pPosCenter,
        Vector2 pTriangleX, Vector2 pTriangleY,
        Vector2 pUVX, Vector2 pUVY,
        Vector4 pUVOrigin)
            Vector2 pUVOriginMin, Vector2 pUVOriginMax)
    {
        // Position
        var pos = pVertex.position;
@@ -280,12 +230,15 @@
        pVertex.position = pos;
        // UV
        var uv = pVertex.uv0;
        uv += new Vector4((pUVX / pTriangleX.magnitude * posXOffset * (Vector2.Dot(pTriangleX, Vector2.right) > 0 ? 1 : -1)).x, (pUVX / pTriangleX.magnitude * posXOffset * (Vector2.Dot(pTriangleX, Vector2.right) > 0 ? 1 : -1)).y, 0, 0);
        uv += new Vector4((pUVY / pTriangleY.magnitude * posYOffset * (Vector2.Dot(pTriangleY, Vector2.up) > 0 ? 1 : -1)).x, (pUVY / pTriangleY.magnitude * posYOffset * (Vector2.Dot(pTriangleY, Vector2.up) > 0 ? 1 : -1)).y, 0, 0);
        pVertex.uv0 = uv;
        // 原始UV框
        pVertex.uv1 = new Vector2(pUVOrigin.x, pUVOrigin.y);
        pVertex.uv2 = new Vector2(pUVOrigin.z, pUVOrigin.w);
            pVertex.uv1 = pUVOriginMin;     //uv1 uv2 可用  tangent  normal 在缩放情况 会有问题
            pVertex.uv2 = pUVOriginMax;
        return pVertex;
    }
@@ -315,11 +268,4 @@
    }
    // 生成材质池的键
    private string GetMaterialKey(Color color, int width)
    {
        // 使用颜色的RGBA值和宽度生成唯一键
        return string.Format("{0}_{1}_{2}_{3}_{4}",
            color.r, color.g, color.b, color.a, width);
    }
}