用到的地方不多
实现方法是继承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);
}
}