目的:
游戏中经常会用到进度条,但是美术给的图片用filled一拉伸就很难看,如下图
第一种模式是九宫格模式,第二种是filled。而我们需要的是两种可结合的。
如何实现:
新建一个类,继承image类后,我选择改写sliced模式下的渲染方式,将horizont填充方式的功能添加进去。
因为大部分进度条只需要切割左右两头,所以为了省事只实现了“三宫格”,并且两头切割的长度要相等。
然而inspector中的sliced模式下不包括fillmount属性,所以我们还需要改写ImageEditor,也是新建类继承。
注意继承ImageEditor的文本要放在Asset文件夹下的Editor文件夹里,unity会自动编译添加的。
代码:
1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 using UnityEngine.UI; 5 using UnityEngine.Sprites; 6 7 public class childImage : Image{ 8 9 protected override void OnPopulateMesh(VertexHelper toFill) 10 { 11 base.OnPopulateMesh(toFill); 12 if (overrideSprite == null) 13 { 14 base.OnPopulateMesh(toFill); 15 return; 16 } 17 if (type == Type.Sliced) 18 { 19 GenerateSlicedSprite_(toFill); 20 } 21 } 22 23 Vector4 GetAdjustedBorders(Vector4 border, Rect rect) 24 { 25 for (int axis = 0; axis <= 1; axis++) 26 { 27 // If the rect is smaller than the combined borders, then there‘s not room for the borders at their normal size. 28 // In order to avoid artefacts with overlapping borders, we scale the borders down to fit. 29 float combinedBorders = border[axis] + border[axis + 2]; 30 if (rect.size[axis] < combinedBorders && combinedBorders != 0) 31 { 32 float borderScaleRatio = rect.size[axis] / combinedBorders; 33 border[axis] *= borderScaleRatio; 34 border[axis + 2] *= borderScaleRatio; 35 } 36 } 37 return border; 38 } 39 40 static void AddQuad(VertexHelper vertexHelper, Vector2 posMin, Vector2 posMax, Color32 color, Vector2 uvMin, Vector2 uvMax) 41 { 42 int startIndex = vertexHelper.currentVertCount; 43 44 vertexHelper.AddVert(new Vector3(posMin.x, posMin.y, 0), color, new Vector2(uvMin.x, uvMin.y)); 45 vertexHelper.AddVert(new Vector3(posMin.x, posMax.y, 0), color, new Vector2(uvMin.x, uvMax.y)); 46 vertexHelper.AddVert(new Vector3(posMax.x, posMax.y, 0), color, new Vector2(uvMax.x, uvMax.y)); 47 vertexHelper.AddVert(new Vector3(posMax.x, posMin.y, 0), color, new Vector2(uvMax.x, uvMin.y)); 48 49 vertexHelper.AddTriangle(startIndex, startIndex + 1, startIndex + 2); 50 vertexHelper.AddTriangle(startIndex + 2, startIndex + 3, startIndex); 51 } 52 private void GenerateSlicedSprite_(VertexHelper toFill) 53 { 54 Vector4 outer, inner, padding, border; 55 56 if (overrideSprite != null) 57 { 58 outer = DataUtility.GetOuterUV(overrideSprite); 59 inner = DataUtility.GetInnerUV(overrideSprite); 60 padding = DataUtility.GetPadding(overrideSprite); 61 border = overrideSprite.border; 62 } 63 else 64 { 65 outer = Vector4.zero; 66 inner = Vector4.zero; 67 padding = Vector4.zero; 68 border = Vector4.zero; 69 } 70 71 Rect rect = GetPixelAdjustedRect(); 72 border = GetAdjustedBorders(border / pixelsPerUnit, rect); 73 padding = padding / pixelsPerUnit; 74 float condition = (border.z + border.x) / rect.width; 75 #region 实际显示size 76 float[] x={0,0,0,0}; 77 78 x[0] = 0; 79 if (fillAmount <condition) 80 { 81 x[1] = fillAmount / 2 * rect.width; 82 x[2] = x[1]+ 0; 83 x[3] = x[1]*2; 84 } 85 else 86 { 87 x[1] = border.x; 88 x[2] = rect.width *fillAmount-border.z; 89 x[3] =x[2]+border.z; 90 } 91 float []y ={0+rect.y,rect.height+rect.y}; 92 93 for (int i = 0; i < 4; ++i) 94 { 95 x[i] += rect.x; 96 97 } 98 #endregion 99 100 #region uv值 101 float[] x_uv = {0,0,0,0 }; 102 103 x_uv[0] =0; 104 if (fillAmount <condition) 105 { 106 x_uv[1] = fillAmount*rect.width/2/sprite.rect.size.x; 107 x_uv[2] = 1 - x_uv[1]; 108 } 109 else 110 { 111 x_uv[1] = inner.x; 112 x_uv[2] = inner.z; 113 } 114 x_uv[3] = outer.z; 115 116 float y_uv = 1; 117 #endregion 118 119 toFill.Clear(); 120 for (int i = 0; i < 3; i++) 121 { 122 int i2 = i + 1; 123 AddQuad(toFill, 124 new Vector2(x[i],y[0]), 125 new Vector2(x[i2],y[1]), 126 color, 127 new Vector2(x_uv[i],0), 128 new Vector2(x_uv[i2],y_uv)); 129 } 130 131 } 132 133 134 }
1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 using UnityEditor.UI; 5 using UnityEditor; 6 using UnityEditor.AnimatedValues; 7 using System.Linq; 8 9 [CustomEditor(typeof(childImage))] 10 public class ChildchildImageInspector : ImageEditor 11 { 12 SerializedProperty m_FillMethod; 13 SerializedProperty m_FillOrigin; 14 SerializedProperty m_FillAmount; 15 SerializedProperty m_FillClockwise; 16 SerializedProperty m_Type; 17 SerializedProperty m_FillCenter; 18 SerializedProperty m_Sprite; 19 SerializedProperty m_PreserveAspect; 20 GUIContent m_SpriteContent; 21 GUIContent m_SpriteTypeContent; 22 GUIContent m_ClockwiseContent; 23 AnimBool m_ShowSlicedOrTiled; 24 AnimBool m_ShowSliced; 25 AnimBool m_ShowFilled; 26 AnimBool m_ShowType; 27 28 void SetShowNativeSize(bool instant) 29 { 30 childImage.Type type = (childImage.Type)m_Type.enumValueIndex; 31 bool showNativeSize = (type == childImage.Type.Simple || type == childImage.Type.Filled); 32 base.SetShowNativeSize(showNativeSize, instant); 33 } 34 protected override void OnEnable() 35 { 36 base.OnEnable(); 37 m_SpriteContent = new GUIContent("Source childImage"); 38 m_SpriteTypeContent = new GUIContent("childImage Type"); 39 m_ClockwiseContent = new GUIContent("Clockwise"); 40 41 m_Sprite = serializedObject.FindProperty("m_Sprite"); 42 m_Type = serializedObject.FindProperty("m_Type"); 43 m_FillCenter = serializedObject.FindProperty("m_FillCenter"); 44 m_FillMethod = serializedObject.FindProperty("m_FillMethod"); 45 m_FillOrigin = serializedObject.FindProperty("m_FillOrigin"); 46 m_FillClockwise = serializedObject.FindProperty("m_FillClockwise"); 47 m_FillAmount = serializedObject.FindProperty("m_FillAmount"); 48 m_PreserveAspect = serializedObject.FindProperty("m_PreserveAspect"); 49 50 m_ShowType = new AnimBool(m_Sprite.objectReferenceValue != null); 51 m_ShowType.valueChanged.AddListener(Repaint); 52 53 var typeEnum = (childImage.Type)m_Type.enumValueIndex; 54 m_ShowSlicedOrTiled = new AnimBool(!m_Type.hasMultipleDifferentValues && typeEnum == childImage.Type.Sliced); 55 m_ShowSliced = new AnimBool(!m_Type.hasMultipleDifferentValues && typeEnum == childImage.Type.Sliced); 56 m_ShowFilled = new AnimBool(!m_Type.hasMultipleDifferentValues && typeEnum == childImage.Type.Filled); 57 m_ShowSlicedOrTiled.valueChanged.AddListener(Repaint); 58 m_ShowSliced.valueChanged.AddListener(Repaint); 59 m_ShowFilled.valueChanged.AddListener(Repaint); 60 61 SetShowNativeSize(true); 62 } 63 public override void OnInspectorGUI() 64 { 65 serializedObject.Update(); 66 67 SpriteGUI(); 68 AppearanceControlsGUI(); 69 RaycastControlsGUI(); 70 71 m_ShowType.target = m_Sprite.objectReferenceValue != null; 72 if (EditorGUILayout.BeginFadeGroup(m_ShowType.faded)) 73 { 74 EditorGUILayout.PropertyField(m_Type, m_SpriteTypeContent); 75 76 ++EditorGUI.indentLevel; 77 { 78 childImage.Type typeEnum = (childImage.Type)m_Type.enumValueIndex; 79 bool showSlicedOrTiled = (!m_Type.hasMultipleDifferentValues && (typeEnum ==childImage.Type.Sliced|| typeEnum == childImage.Type.Tiled)); 80 if (showSlicedOrTiled && targets.Length > 1) 81 showSlicedOrTiled = targets.Select(obj => obj as childImage).All(img => img.hasBorder); 82 83 m_ShowSlicedOrTiled.target = showSlicedOrTiled; 84 m_ShowSliced.target = (showSlicedOrTiled && !m_Type.hasMultipleDifferentValues && typeEnum == childImage.Type.Sliced); 85 m_ShowFilled.target = (!m_Type.hasMultipleDifferentValues && typeEnum == childImage.Type.Filled); 86 87 childImage cImage = target as childImage; 88 89 if (EditorGUILayout.BeginFadeGroup(m_ShowSlicedOrTiled.faded)) 90 { 91 if (cImage.hasBorder) 92 { 93 EditorGUILayout.PropertyField(m_FillCenter); 94 EditorGUILayout.PropertyField(m_FillAmount); 95 } 96 97 } 98 EditorGUILayout.EndFadeGroup(); 99 100 if (EditorGUILayout.BeginFadeGroup(m_ShowSliced.faded)) 101 { 102 if (cImage.sprite != null && !cImage.hasBorder) 103 EditorGUILayout.HelpBox("This childImage doesn‘t have a border.", MessageType.Warning); 104 } 105 EditorGUILayout.EndFadeGroup(); 106 107 if (EditorGUILayout.BeginFadeGroup(m_ShowFilled.faded)) 108 { 109 EditorGUI.BeginChangeCheck(); 110 EditorGUILayout.PropertyField(m_FillMethod); 111 if (EditorGUI.EndChangeCheck()) 112 { 113 m_FillOrigin.intValue = 0; 114 } 115 switch ((childImage.FillMethod)m_FillMethod.enumValueIndex) 116 { 117 case childImage.FillMethod.Horizontal: 118 m_FillOrigin.intValue = (int)(childImage.OriginHorizontal)EditorGUILayout.EnumPopup("Fill Origin", (childImage.OriginHorizontal)m_FillOrigin.intValue); 119 break; 120 case childImage.FillMethod.Vertical: 121 m_FillOrigin.intValue = (int)(childImage.OriginVertical)EditorGUILayout.EnumPopup("Fill Origin", (childImage.OriginVertical)m_FillOrigin.intValue); 122 break; 123 case childImage.FillMethod.Radial90: 124 m_FillOrigin.intValue = (int)(childImage.Origin90)EditorGUILayout.EnumPopup("Fill Origin", (childImage.Origin90)m_FillOrigin.intValue); 125 break; 126 case childImage.FillMethod.Radial180: 127 m_FillOrigin.intValue = (int)(childImage.Origin180)EditorGUILayout.EnumPopup("Fill Origin", (childImage.Origin180)m_FillOrigin.intValue); 128 break; 129 case childImage.FillMethod.Radial360: 130 m_FillOrigin.intValue = (int)(childImage.Origin360)EditorGUILayout.EnumPopup("Fill Origin", (childImage.Origin360)m_FillOrigin.intValue); 131 break; 132 } 133 EditorGUILayout.PropertyField(m_FillAmount); 134 if ((childImage.FillMethod)m_FillMethod.enumValueIndex > childImage.FillMethod.Vertical) 135 { 136 EditorGUILayout.PropertyField(m_FillClockwise, m_ClockwiseContent); 137 } 138 } 139 EditorGUILayout.EndFadeGroup(); 140 } 141 --EditorGUI.indentLevel; 142 } 143 144 EditorGUILayout.EndFadeGroup(); 145 146 SetShowNativeSize(false); 147 if (EditorGUILayout.BeginFadeGroup(m_ShowNativeSize.faded)) 148 { 149 EditorGUI.indentLevel++; 150 EditorGUILayout.PropertyField(m_PreserveAspect); 151 EditorGUI.indentLevel--; 152 } 153 EditorGUILayout.EndFadeGroup(); 154 NativeSizeButtonGUI(); 155 156 serializedObject.ApplyModifiedProperties(); 157 } 158 }