Skip to content

Instantly share code, notes, and snippets.

@huiliu
Last active May 30, 2018 21:04
Show Gist options
  • Select an option

  • Save huiliu/45d7392146e56b8c7e3d89de13ddb95b to your computer and use it in GitHub Desktop.

Select an option

Save huiliu/45d7392146e56b8c7e3d89de13ddb95b to your computer and use it in GitHub Desktop.
九宫格形式拉伸贴图
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Sprites;
/// <summary>
/// 参照 https://www.cnblogs.com/ninomiya/p/8134497.html 修改
/// </summary>
public class SlicedImage : Image
{
public SlicedType slicedType;
protected override void OnPopulateMesh(VertexHelper toFill)
{
base.OnPopulateMesh(toFill);
if (overrideSprite == null)
{
base.OnPopulateMesh(toFill);
return;
}
if (type == Type.Sliced)
{
switch (slicedType)
{
case SlicedType.Center:
GenerateSlicedSpriteCenter(toFill);
break;
case SlicedType.Left:
GenerateSlicedSpriteLeft(toFill);
break;
case SlicedType.Right:
GenerateSlicedSpriteRight(toFill);
break;
}
}
}
Vector4 GetAdjustedBorders(Vector4 border, Rect rect)
{
for (int axis = 0; axis <= 1; axis++)
{
// If the rect is smaller than the combined borders, then there's not room for the borders at their normal size.
// In order to avoid artefacts with overlapping borders, we scale the borders down to fit.
float combinedBorders = border[axis] + border[axis + 2];
if (rect.size[axis] < combinedBorders && combinedBorders != 0)
{
float borderScaleRatio = rect.size[axis] / combinedBorders;
border[axis] *= borderScaleRatio;
border[axis + 2] *= borderScaleRatio;
}
}
return border;
}
static void AddQuad(VertexHelper vertexHelper, Vector2 posMin, Vector2 posMax, Color32 color, Vector2 uvMin, Vector2 uvMax)
{
int startIndex = vertexHelper.currentVertCount;
vertexHelper.AddVert(new Vector3(posMin.x, posMin.y, 0), color, new Vector2(uvMin.x, uvMin.y));
vertexHelper.AddVert(new Vector3(posMin.x, posMax.y, 0), color, new Vector2(uvMin.x, uvMax.y));
vertexHelper.AddVert(new Vector3(posMax.x, posMax.y, 0), color, new Vector2(uvMax.x, uvMax.y));
vertexHelper.AddVert(new Vector3(posMax.x, posMin.y, 0), color, new Vector2(uvMax.x, uvMin.y));
vertexHelper.AddTriangle(startIndex, startIndex + 1, startIndex + 2);
vertexHelper.AddTriangle(startIndex + 2, startIndex + 3, startIndex);
}
private void GenerateSlicedSpriteLeft(VertexHelper toFill)
{
Vector4 outer, inner, padding, border;
if (overrideSprite != null)
{
outer = DataUtility.GetOuterUV(overrideSprite);
inner = DataUtility.GetInnerUV(overrideSprite);
padding = DataUtility.GetPadding(overrideSprite);
border = overrideSprite.border;
}
else
{
outer = Vector4.zero;
inner = Vector4.zero;
padding = Vector4.zero;
border = Vector4.zero;
}
Rect rect = GetPixelAdjustedRect();
border = GetAdjustedBorders(border / pixelsPerUnit, rect);
padding = padding / pixelsPerUnit;
float condition = (border.z + border.x) / rect.width;
var realFillAmount = fillAmount;
#region 实际显示size
float[] x = { 0, 0, 0, 0 };
{
x[0] = 0;
if (realFillAmount < condition)
{
x[1] = realFillAmount / 2 * rect.width;
x[2] = x[1] + 0;
x[3] = x[1] * 2;
}
else
{
x[1] = border.x;
x[2] = rect.width * realFillAmount - border.z;
x[3] = x[2] + border.z;
}
}
float[] y = { 0 + rect.y, rect.height + rect.y };
Debug.Log(string.Format("xo: {0} {1} {2} {3}", x[0], x[1], x[2], x[3]));
for (int i = 0; i < 4; ++i)
{
x[i] += rect.x;
}
#endregion
#region uv值
float[] x_uv = { 0, 0, 0, 0 };
x_uv[0] = 0;
if (realFillAmount < condition)
{
x_uv[1] = realFillAmount * rect.width / 2 / sprite.rect.size.x;
x_uv[2] = 1 - x_uv[1];
}
else
{
x_uv[1] = inner.x;
x_uv[2] = inner.z;
}
x_uv[3] = outer.z;
float y_uv = 1;
#endregion
Debug.Log(string.Format("x: {0} {1} {2} {3}", x[0], x[1], x[2], x[3]));
Debug.Log(string.Format("x_uv: {0} {1} {2} {3}", x_uv[0], x_uv[1], x_uv[2], x_uv[3]));
toFill.Clear();
for (int i = 0; i < 3; i++)
{
int i2 = i + 1;
AddQuad(toFill,
new Vector2(x[i], y[0]),
new Vector2(x[i2], y[1]),
color,
new Vector2(x_uv[i], 0),
new Vector2(x_uv[i2], y_uv));
}
}
private void GenerateSlicedSpriteRight(VertexHelper toFill)
{
Vector4 outer, inner, padding, border;
if (overrideSprite != null)
{
outer = DataUtility.GetOuterUV(overrideSprite);
inner = DataUtility.GetInnerUV(overrideSprite);
padding = DataUtility.GetPadding(overrideSprite);
border = overrideSprite.border;
}
else
{
outer = Vector4.zero;
inner = Vector4.zero;
padding = Vector4.zero;
border = Vector4.zero;
}
Rect rect = GetPixelAdjustedRect();
border = GetAdjustedBorders(border / pixelsPerUnit, rect);
padding = padding / pixelsPerUnit;
float condition = (border.z + border.x) / rect.width;
var realFillAmount = fillAmount;
#region 实际显示size
float[] x = { 0, 0, 0, 0 };
Debug.Log(string.Format("rect.x: {0} rect.y: {1} rect.width: {2} rect.heigh: {3}", rect.x, rect.y, rect.width, rect.height));
Debug.Log(string.Format("border.x: {0} border.y: {1} border.z: {2} border.w: {3}", border.x, border.y, border.z, border.w));
{
x[0] = 0;
if (realFillAmount < condition)
{
x[1] = realFillAmount / 2 * rect.width;
x[2] = x[1] + 0;
x[3] = x[1] * 2;
}
else
{
x[1] = border.x;
x[2] = rect.width * realFillAmount - border.z;
x[3] = x[2] + border.z;
}
var tmp = x[0];
x[0] = x[3];
x[3] = tmp;
tmp = x[1];
x[1] = x[2];
x[2] = tmp;
}
float[] y = { 0 + rect.y, rect.height + rect.y };
Debug.Log(string.Format("xo: {0} {1} {2} {3}", x[0], x[1], x[2], x[3]));
for (int i = 0; i < 4; ++i)
{
x[i] += rect.x;
x[i] *= (-1);
}
#endregion
#region uv值
float[] x_uv = { 0, 0, 0, 0 };
x_uv[0] = 0;
if (realFillAmount < condition)
{
x_uv[1] = realFillAmount * rect.width / 2 / sprite.rect.size.x;
x_uv[2] = 1 - x_uv[1];
}
else
{
x_uv[1] = inner.x;
x_uv[2] = inner.z;
}
x_uv[3] = outer.z;
float y_uv = 1;
#endregion
Debug.Log(string.Format("x: {0} {1} {2} {3}", x[0], x[1], x[2], x[3]));
Debug.Log(string.Format("x_uv: {0} {1} {2} {3}", x_uv[0], x_uv[1], x_uv[2], x_uv[3]));
toFill.Clear();
for (int i = 0; i < 3; i++)
{
int i2 = i + 1;
AddQuad(toFill,
new Vector2(x[i], y[0]),
new Vector2(x[i2], y[1]),
color,
new Vector2(x_uv[i], 0),
new Vector2(x_uv[i2], y_uv));
}
}
private void GenerateSlicedSpriteCenter(VertexHelper toFill)
{
Vector4 outer, inner, padding, border;
if (overrideSprite != null)
{
outer = DataUtility.GetOuterUV(overrideSprite);
inner = DataUtility.GetInnerUV(overrideSprite);
padding = DataUtility.GetPadding(overrideSprite);
border = overrideSprite.border;
}
else
{
outer = Vector4.zero;
inner = Vector4.zero;
padding = Vector4.zero;
border = Vector4.zero;
}
Rect rect = GetPixelAdjustedRect();
border = GetAdjustedBorders(border / pixelsPerUnit, rect);
padding = padding / pixelsPerUnit;
float condition = (border.z + border.x) / rect.width;
var realFillAmount = fillAmount / 2;
#region 实际显示size
float[] x = { 0, 0, 0, 0 };
{
x[0] = 0;
if (realFillAmount < condition)
{
x[1] = realFillAmount / 2 * rect.width;
x[2] = x[1] + 0;
x[3] = x[1] * 2;
}
else
{
x[1] = border.x;
x[2] = rect.width * realFillAmount - border.z;
x[3] = x[2] + border.z;
}
}
float[] y = { 0 + rect.y, rect.height + rect.y };
Debug.Log(string.Format("xo: {0} {1} {2} {3}", x[0], x[1], x[2], x[3]));
for (int i = 0; i < 4; ++i)
{
x[i] += rect.x + rect.width / 2;
//x[i] += rect.x;
}
#endregion
#region uv值
float[] x_uv = { 0, 0, 0, 0 };
x_uv[0] = 0;
if (realFillAmount < condition)
{
x_uv[1] = realFillAmount * rect.width / 2 / sprite.rect.size.x;
x_uv[2] = 1 - x_uv[1];
}
else
{
x_uv[1] = inner.x;
x_uv[2] = inner.z;
}
x_uv[3] = outer.z;
float y_uv = 1;
#endregion
x[0] = -x[3];
x[1] = -x[2];
Debug.Log(string.Format("x: {0} {1} {2} {3}", x[0], x[1], x[2], x[3]));
Debug.Log(string.Format("x_uv: {0} {1} {2} {3}", x_uv[0], x_uv[1], x_uv[2], x_uv[3]));
toFill.Clear();
for (int i = 0; i < 3; i++)
{
int i2 = i + 1;
AddQuad(toFill,
new Vector2(x[i], y[0]),
new Vector2(x[i2], y[1]),
color,
new Vector2(x_uv[i], 0),
new Vector2(x_uv[i2], y_uv));
}
}
public enum SlicedType
{
Left,
Right,
Center
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor.UI;
using UnityEditor;
using UnityEditor.AnimatedValues;
using System.Linq;
[CustomEditor(typeof(SlicedImage))]
public class SlicedImageInspector : ImageEditor
{
SerializedProperty m_FillMethod;
SerializedProperty m_FillOrigin;
SerializedProperty m_FillAmount;
SerializedProperty m_FillClockwise;
SerializedProperty m_Type;
SerializedProperty m_FillCenter;
SerializedProperty m_Sprite;
SerializedProperty m_PreserveAspect;
GUIContent m_SpriteContent;
GUIContent m_SpriteTypeContent;
GUIContent m_ClockwiseContent;
AnimBool m_ShowSlicedOrTiled;
AnimBool m_ShowSliced;
AnimBool m_ShowFilled;
AnimBool m_ShowType;
void SetShowNativeSize(bool instant)
{
SlicedImage.Type type = (SlicedImage.Type)m_Type.enumValueIndex;
bool showNativeSize = (type == SlicedImage.Type.Simple || type == SlicedImage.Type.Filled);
base.SetShowNativeSize(showNativeSize, instant);
}
protected override void OnEnable()
{
base.OnEnable();
m_SpriteContent = new GUIContent("Source childImage");
m_SpriteTypeContent = new GUIContent("childImage Type");
m_ClockwiseContent = new GUIContent("Clockwise");
m_Sprite = serializedObject.FindProperty("m_Sprite");
m_Type = serializedObject.FindProperty("m_Type");
m_FillCenter = serializedObject.FindProperty("m_FillCenter");
m_FillMethod = serializedObject.FindProperty("m_FillMethod");
m_FillOrigin = serializedObject.FindProperty("m_FillOrigin");
m_FillClockwise = serializedObject.FindProperty("m_FillClockwise");
m_FillAmount = serializedObject.FindProperty("m_FillAmount");
m_PreserveAspect = serializedObject.FindProperty("m_PreserveAspect");
m_ShowType = new AnimBool(m_Sprite.objectReferenceValue != null);
m_ShowType.valueChanged.AddListener(Repaint);
var typeEnum = (SlicedImage.Type)m_Type.enumValueIndex;
m_ShowSlicedOrTiled = new AnimBool(!m_Type.hasMultipleDifferentValues && typeEnum == SlicedImage.Type.Sliced);
m_ShowSliced = new AnimBool(!m_Type.hasMultipleDifferentValues && typeEnum == SlicedImage.Type.Sliced);
m_ShowFilled = new AnimBool(!m_Type.hasMultipleDifferentValues && typeEnum == SlicedImage.Type.Filled);
m_ShowSlicedOrTiled.valueChanged.AddListener(Repaint);
m_ShowSliced.valueChanged.AddListener(Repaint);
m_ShowFilled.valueChanged.AddListener(Repaint);
SetShowNativeSize(true);
}
public override void OnInspectorGUI()
{
serializedObject.Update();
SpriteGUI();
AppearanceControlsGUI();
RaycastControlsGUI();
m_ShowType.target = m_Sprite.objectReferenceValue != null;
if (EditorGUILayout.BeginFadeGroup(m_ShowType.faded))
{
EditorGUILayout.PropertyField(m_Type, m_SpriteTypeContent);
++EditorGUI.indentLevel;
{
SlicedImage.Type typeEnum = (SlicedImage.Type)m_Type.enumValueIndex;
bool showSlicedOrTiled = (!m_Type.hasMultipleDifferentValues && (typeEnum == SlicedImage.Type.Sliced || typeEnum == SlicedImage.Type.Tiled));
if (showSlicedOrTiled && targets.Length > 1)
showSlicedOrTiled = targets.Select(obj => obj as SlicedImage).All(img => img.hasBorder);
m_ShowSlicedOrTiled.target = showSlicedOrTiled;
m_ShowSliced.target = (showSlicedOrTiled && !m_Type.hasMultipleDifferentValues && typeEnum == SlicedImage.Type.Sliced);
m_ShowFilled.target = (!m_Type.hasMultipleDifferentValues && (typeEnum == SlicedImage.Type.Filled));
SlicedImage cImage = target as SlicedImage;
if (EditorGUILayout.BeginFadeGroup(m_ShowSlicedOrTiled.faded))
{
if (cImage.hasBorder)
{
EditorGUILayout.PropertyField(m_FillCenter);
cImage.slicedType = (SlicedImage.SlicedType)EditorGUILayout.EnumPopup("Fill Origin", cImage.slicedType);
EditorGUILayout.PropertyField(m_FillAmount);
}
}
EditorGUILayout.EndFadeGroup();
if (EditorGUILayout.BeginFadeGroup(m_ShowSliced.faded))
{
if (cImage.sprite != null && !cImage.hasBorder)
EditorGUILayout.HelpBox("This childImage doesn't have a border.", MessageType.Warning);
}
EditorGUILayout.EndFadeGroup();
if (EditorGUILayout.BeginFadeGroup(m_ShowFilled.faded))
{
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_FillMethod);
if (EditorGUI.EndChangeCheck())
{
m_FillOrigin.intValue = 0;
}
switch ((SlicedImage.FillMethod)m_FillMethod.enumValueIndex)
{
case SlicedImage.FillMethod.Horizontal:
m_FillOrigin.intValue = (int)(SlicedImage.OriginHorizontal)EditorGUILayout.EnumPopup("Fill Origin", (SlicedImage.OriginHorizontal)m_FillOrigin.intValue);
break;
case SlicedImage.FillMethod.Vertical:
m_FillOrigin.intValue = (int)(SlicedImage.OriginVertical)EditorGUILayout.EnumPopup("Fill Origin", (SlicedImage.OriginVertical)m_FillOrigin.intValue);
break;
case SlicedImage.FillMethod.Radial90:
m_FillOrigin.intValue = (int)(SlicedImage.Origin90)EditorGUILayout.EnumPopup("Fill Origin", (SlicedImage.Origin90)m_FillOrigin.intValue);
break;
case SlicedImage.FillMethod.Radial180:
m_FillOrigin.intValue = (int)(SlicedImage.Origin180)EditorGUILayout.EnumPopup("Fill Origin", (SlicedImage.Origin180)m_FillOrigin.intValue);
break;
case SlicedImage.FillMethod.Radial360:
m_FillOrigin.intValue = (int)(SlicedImage.Origin360)EditorGUILayout.EnumPopup("Fill Origin", (SlicedImage.Origin360)m_FillOrigin.intValue);
break;
}
EditorGUILayout.PropertyField(m_FillAmount);
if ((SlicedImage.FillMethod)m_FillMethod.enumValueIndex > SlicedImage.FillMethod.Vertical)
{
EditorGUILayout.PropertyField(m_FillClockwise, m_ClockwiseContent);
}
}
EditorGUILayout.EndFadeGroup();
}
--EditorGUI.indentLevel;
}
EditorGUILayout.EndFadeGroup();
SetShowNativeSize(false);
if (EditorGUILayout.BeginFadeGroup(m_ShowNativeSize.faded))
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(m_PreserveAspect);
EditorGUI.indentLevel--;
}
EditorGUILayout.EndFadeGroup();
NativeSizeButtonGUI();
serializedObject.ApplyModifiedProperties();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment