UGUI拓展双向可控Fill Amount的Image

用到的地方不多
实现方法是继承Image,重写OnPopulateMesh
GenerateFilledSprite方法复制过来改改就行了
主要部分

GenerateFilledSprite方法
 void GenerateFilledSprite(VertexHelper toFill, bool preserveAspect)
        {
            toFill.Clear();

            if (fillAmount < 0.001f)
                return;

            Vector4 v = GetDrawingDimensions(preserveAspect);
            Vector4 outer = overrideSprite != null ? DataUtility.GetOuterUV(overrideSprite) : Vector4.zero;
            UIVertex uiv = UIVertex.simpleVert;
            uiv.color = color;

            float tx0 = outer.x;
            float ty0 = outer.y;
            float tx1 = outer.z;
            float ty1 = outer.w;

            // Horizontal and vertical filled sprites are simple -- just end the Image prematurely
            if (fillMethod == FillMethod.Horizontal || fillMethod == FillMethod.Vertical)
            {
                if (fillMethod == FillMethod.Horizontal)
                {
                    float rightFill = (tx1 - tx0) * rightFillAmount;
                    float leftFill = (tx1 - tx0) * leftFillAmount;

                    float originalTx0 = tx0;
                    float originalVx = v.x;

                    v.x = v.z - (v.z - v.x) * rightFillAmount;
                    tx0 = tx1 - rightFill;

                    v.z = originalVx + (v.z - originalVx) * leftFillAmount;
                    tx1 = originalTx0 + leftFill;
                }
                else if (fillMethod == FillMethod.Vertical)
                {
                    float fill = (ty1 - ty0) * fillAmount;

                    float topFill = (ty1 - ty0) * topFillAmount;
                    float bottomFill = (ty1 - ty0) * bottomFillAmount;

                    float originalTy0 = ty0;
                    float originalVy = v.y;

                    v.y = v.w - (v.w - v.y) * topFillAmount;
                    ty0 = ty1 - topFill;

                    v.w = originalVy + (v.w - originalVy) * bottomFillAmount;
                    ty1 = originalTy0 + bottomFill;
                }
            }

            s_Xy[0] = new Vector2(v.x, v.y);
            s_Xy[1] = new Vector2(v.x, v.w);
            s_Xy[2] = new Vector2(v.z, v.w);
            s_Xy[3] = new Vector2(v.z, v.y);

            s_Uv[0] = new Vector2(tx0, ty0);
            s_Uv[1] = new Vector2(tx0, ty1);
            s_Uv[2] = new Vector2(tx1, ty1);
            s_Uv[3] = new Vector2(tx1, ty0);


            AddQuad(toFill, s_Xy, color, s_Uv);
        }

最后编辑器代码继承ImageEditor,自动选择Fill

FillAmountImageEditor:

using UnityEditor;
using UnityEditor.UI;
using UnityEngine.UI;

[CustomEditor(typeof(FillAmountImage))]
public class FillAmountImageEditor : ImageEditor
{
    SerializedProperty m_RightFillAmount;
    SerializedProperty m_LeftFillAmount;

    SerializedProperty m_TopFillAmount;
    SerializedProperty m_BottomFillAmount;

    SerializedProperty m_FillMethod;

    protected override void OnEnable()
    {
        base.OnEnable();
        FillAmountImage img = target as FillAmountImage;
        img.type = Image.Type.Filled;
        img.fillMethod = Image.FillMethod.Horizontal;


        SerializedObject obj = serializedObject;

        m_FillMethod = serializedObject.FindProperty("m_FillMethod");

        m_RightFillAmount = obj.FindProperty("rightFillAmount");
        m_LeftFillAmount = obj.FindProperty("leftFillAmount");

        m_TopFillAmount = obj.FindProperty("topFillAmount");
        m_BottomFillAmount = obj.FindProperty("bottomFillAmount");
    }
    
    

    public override void OnInspectorGUI()
    {
        SpriteGUI();
        AppearanceControlsGUI();
        RaycastControlsGUI();
        MaskableControlsGUI();

        EditorGUILayout.PropertyField(m_FillMethod);

        switch ((Image.FillMethod) m_FillMethod.enumValueIndex)
        {
            case Image.FillMethod.Horizontal:
                LeftRightFillAmountGUI();
                break;
            case Image.FillMethod.Vertical:
                TopBottomFillAmountGUI();
                break;
            default:
                EditorGUILayout.LabelField("请选择Horizontal或Vertical");
                break;
        }

        SetShowNativeSize(true, false);
        NativeSizeButtonGUI();


        serializedObject.ApplyModifiedProperties();
    }

    private void LeftRightFillAmountGUI()
    {
        using (var changeScope = new EditorGUI.ChangeCheckScope())
        {
            EditorGUILayout.PropertyField(m_LeftFillAmount);

            if (changeScope.changed)
            {
                if (m_LeftFillAmount.floatValue + m_RightFillAmount.floatValue < 1)
                {
                    m_LeftFillAmount.floatValue = 1 - m_RightFillAmount.floatValue;
                }
            }
        }

        using (var changeScope = new EditorGUI.ChangeCheckScope())
        {
            EditorGUILayout.PropertyField(m_RightFillAmount);

            if (changeScope.changed)
            {
                if (m_LeftFillAmount.floatValue + m_RightFillAmount.floatValue < 1)
                {
                    m_RightFillAmount.floatValue = 1 - m_LeftFillAmount.floatValue;
                }
            }
        }
    }

    private void TopBottomFillAmountGUI()
    {
        using (var changeScope = new EditorGUI.ChangeCheckScope())
        {
            EditorGUILayout.PropertyField(m_TopFillAmount);

            if (changeScope.changed)
            {
                if (m_TopFillAmount.floatValue + m_BottomFillAmount.floatValue < 1)
                {
                    m_TopFillAmount.floatValue = 1 - m_BottomFillAmount.floatValue;
                }
            }
        }

        using (var changeScope = new EditorGUI.ChangeCheckScope())
        {
            EditorGUILayout.PropertyField(m_BottomFillAmount);

            if (changeScope.changed)
            {
                if (m_BottomFillAmount.floatValue + m_TopFillAmount.floatValue < 1)
                {
                    m_BottomFillAmount.floatValue = 1 - m_TopFillAmount.floatValue;
                }
            }
        }
    }
}

FillAmountImage:

using UnityEngine;
using UnityEngine.Sprites;
using UnityEngine.UI;
    public class FillAmountImage : Image
    {
        [Range(0, 1)]
        public float rightFillAmount = 1;

        [Range(0, 1)]
        public float leftFillAmount = 1;

        [Range(0, 1)]
        public float topFillAmount = 1;

        [Range(0, 1)]
        public float bottomFillAmount = 1;


        static readonly Vector3[] s_Xy = new Vector3[4];
        static readonly Vector3[] s_Uv = new Vector3[4];

        protected override void OnPopulateMesh(VertexHelper toFill)
        {
            if (overrideSprite == null)
            {
                base.OnPopulateMesh(toFill);
                return;
            }

            switch (type)
            {
                case Type.Filled:
                    GenerateFilledSprite(toFill, preserveAspect);
                    return;
            }


            base.OnPopulateMesh(toFill);
        }

        /// 
        /// Generate vertices for a filled Image.
        /// 
        void GenerateFilledSprite(VertexHelper toFill, bool preserveAspect)
        {
            toFill.Clear();

            if (fillAmount < 0.001f)
                return;

            Vector4 v = GetDrawingDimensions(preserveAspect);
            Vector4 outer = overrideSprite != null ? DataUtility.GetOuterUV(overrideSprite) : Vector4.zero;
            UIVertex uiv = UIVertex.simpleVert;
            uiv.color = color;

            float tx0 = outer.x;
            float ty0 = outer.y;
            float tx1 = outer.z;
            float ty1 = outer.w;

            // Horizontal and vertical filled sprites are simple -- just end the Image prematurely
            if (fillMethod == FillMethod.Horizontal || fillMethod == FillMethod.Vertical)
            {
                if (fillMethod == FillMethod.Horizontal)
                {
                    float rightFill = (tx1 - tx0) * rightFillAmount;
                    float leftFill = (tx1 - tx0) * leftFillAmount;

                    float originalTx0 = tx0;
                    float originalVx = v.x;

                    v.x = v.z - (v.z - v.x) * rightFillAmount;
                    tx0 = tx1 - rightFill;

                    v.z = originalVx + (v.z - originalVx) * leftFillAmount;
                    tx1 = originalTx0 + leftFill;
                }
                else if (fillMethod == FillMethod.Vertical)
                {
                    float fill = (ty1 - ty0) * fillAmount;

                    float topFill = (ty1 - ty0) * topFillAmount;
                    float bottomFill = (ty1 - ty0) * bottomFillAmount;

                    float originalTy0 = ty0;
                    float originalVy = v.y;

                    v.y = v.w - (v.w - v.y) * topFillAmount;
                    ty0 = ty1 - topFill;

                    v.w = originalVy + (v.w - originalVy) * bottomFillAmount;
                    ty1 = originalTy0 + bottomFill;
                }
            }

            s_Xy[0] = new Vector2(v.x, v.y);
            s_Xy[1] = new Vector2(v.x, v.w);
            s_Xy[2] = new Vector2(v.z, v.w);
            s_Xy[3] = new Vector2(v.z, v.y);

            s_Uv[0] = new Vector2(tx0, ty0);
            s_Uv[1] = new Vector2(tx0, ty1);
            s_Uv[2] = new Vector2(tx1, ty1);
            s_Uv[3] = new Vector2(tx1, ty0);


            AddQuad(toFill, s_Xy, color, s_Uv);
        }

        /// Image's dimensions used for drawing. X = left, Y = bottom, Z = right, W = top.
        private Vector4 GetDrawingDimensions(bool shouldPreserveAspect)
        {
            var padding = overrideSprite == null ? Vector4.zero : DataUtility.GetPadding(overrideSprite);
            var size = overrideSprite == null ? Vector2.zero : new Vector2(overrideSprite.rect.width, overrideSprite.rect.height);

            Rect r = GetPixelAdjustedRect();
            // Debug.Log(string.Format("r:{2}, size:{0}, padding:{1}", size, padding, r));

            int spriteW = Mathf.RoundToInt(size.x);
            int spriteH = Mathf.RoundToInt(size.y);

            var v = new Vector4(
                padding.x / spriteW,
                padding.y / spriteH,
                (spriteW - padding.z) / spriteW,
                (spriteH - padding.w) / spriteH);

            if (shouldPreserveAspect && size.sqrMagnitude > 0.0f)
            {
                PreserveSpriteAspectRatio(ref r, size);
            }

            v = new Vector4(
                r.x + r.width * v.x,
                r.y + r.height * v.y,
                r.x + r.width * v.z,
                r.y + r.height * v.w
            );

            return v;
        }

        private void PreserveSpriteAspectRatio(ref Rect rect, Vector2 spriteSize)
        {
            var spriteRatio = spriteSize.x / spriteSize.y;
            var rectRatio = rect.width / rect.height;

            if (spriteRatio > rectRatio)
            {
                var oldHeight = rect.height;
                rect.height = rect.width * (1.0f / spriteRatio);
                rect.y += (oldHeight - rect.height) * rectTransform.pivot.y;
            }
            else
            {
                var oldWidth = rect.width;
                rect.width = rect.height * spriteRatio;
                rect.x += (oldWidth - rect.width) * rectTransform.pivot.x;
            }
        }

        static void AddQuad(VertexHelper vertexHelper, Vector3[] quadPositions, Color32 color, Vector3[] quadUVs)
        {
            int startIndex = vertexHelper.currentVertCount;

            for (int i = 0; i < 4; ++i)
                vertexHelper.AddVert(quadPositions[i], color, quadUVs[i]);

            vertexHelper.AddTriangle(startIndex, startIndex + 1, startIndex + 2);
            vertexHelper.AddTriangle(startIndex + 2, startIndex + 3, startIndex);
        }
    }

你可能感兴趣的:(Unity,ugui,unity3d,c#)