Skip to content

Instantly share code, notes, and snippets.

@huiliu
Last active May 30, 2018 21:04
Show Gist options
  • Save huiliu/45d7392146e56b8c7e3d89de13ddb95b to your computer and use it in GitHub Desktop.
Save huiliu/45d7392146e56b8c7e3d89de13ddb95b to your computer and use it in GitHub Desktop.
九宫格形式拉伸贴图
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Sprites;
public class ChildImage : Image{
protected override void OnPopulateMesh(VertexHelper toFill)
{
base.OnPopulateMesh(toFill);
if (overrideSprite == null)
{
base.OnPopulateMesh(toFill);
return;
}
if (type == Type.Sliced)
{
//GenerateSlicedSprite_(toFill);
GenerateSprite(toFill);
}
}
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 GenerateSlicedSprite_(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;
#region 实际显示size
float[] x={0,0,0,0};
x[0] = 0;
if (fillAmount <condition)
{
x[1] = fillAmount / 2 * rect.width;
x[2] = x[1]+ 0;
x[3] = x[1]*2;
}
else
{
x[1] = border.x;
x[2] = rect.width *fillAmount-border.z;
x[3] =x[2]+border.z;
}
float []y ={0+rect.y,rect.height+rect.y};
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 (fillAmount <condition)
{
x_uv[1] = fillAmount*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
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 SliceFilledType sliceFilledType;
static readonly Vector2[] s_VertScratch = new Vector2[4];
static readonly Vector2[] s_UVScratch = new Vector2[4];
private void GenerateSprite(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();
var adjustedBorders = GetAdjustedBorders(border / pixelsPerUnit, rect);
padding = padding / pixelsPerUnit;
var tx0 = outer.x;
var ty0 = outer.y;
var tx1 = outer.z;
var ty1 = outer.w;
var v = GetDrawingDimensions(preserveAspect);
var fill = (tx1 - tx0) * fillAmount;
v.x = v.z - (v.x - v.z) * fillAmount;
tx0 = tx1 - fill;
switch (sliceFilledType)
{
case SliceFilledType.Left:
{
s_VertScratch[0] = new Vector2(padding.x, padding.y);
//s_VertScratch[3] = new Vector2(rect.width - padding.z, rect.height - padding.w);
s_VertScratch[1].x = adjustedBorders.x;
s_VertScratch[1].y = adjustedBorders.y;
s_VertScratch[2].x = (rect.width - adjustedBorders.z) * fillAmount;
s_VertScratch[2].y = rect.height - adjustedBorders.w;
s_VertScratch[3] = new Vector2(s_VertScratch[2].x + adjustedBorders.z, rect.height - padding.w);
}
break;
case SliceFilledType.Right:
{
s_VertScratch[2].x = rect.width - adjustedBorders.z;
s_VertScratch[2].y = rect.height - adjustedBorders.w;
s_VertScratch[3] = new Vector2(rect.width - padding.z, rect.height - padding.w);
s_VertScratch[0].x = rect.width - (rect.width - adjustedBorders.x) * fillAmount - adjustedBorders.x;
s_VertScratch[0].y = padding.y;
s_VertScratch[1].x = s_VertScratch[0].x + adjustedBorders.x;
s_VertScratch[1].y = adjustedBorders.y;
}
break;
case SliceFilledType.Center:
{
s_VertScratch[0].x = (rect.width - adjustedBorders.x) * fillAmount / 2;
s_VertScratch[0].y = padding.y;
s_VertScratch[1].x = s_VertScratch[0].x + adjustedBorders.x;
s_VertScratch[1].y = adjustedBorders.y;
s_VertScratch[2].x = rect.width - (rect.width - adjustedBorders.z) * fillAmount/2 - adjustedBorders.z;
s_VertScratch[2].y = rect.height - adjustedBorders.w;
s_VertScratch[3] = new Vector2(s_VertScratch[2].x + adjustedBorders.z, rect.height - padding.w);
}
break;
}
for (int i = 0; i < 4; ++i)
{
s_VertScratch[i].x += rect.x;
s_VertScratch[i].y += rect.y;
}
s_UVScratch[0] = new Vector2(outer.x, outer.y);
s_UVScratch[1] = new Vector2(inner.x, inner.y);
s_UVScratch[2] = new Vector2(inner.z, inner.w);
s_UVScratch[3] = new Vector2(outer.z, outer.w);
toFill.Clear();
for (int x = 0; x < 3; ++x)
{
int x2 = x + 1;
for (int y = 0; y < 3; ++y)
{
if (!fillCenter && x == 1 && y == 1)
continue;
int y2 = y + 1;
AddQuad(toFill,
new Vector2(s_VertScratch[x].x, s_VertScratch[y].y),
new Vector2(s_VertScratch[x2].x, s_VertScratch[y2].y),
color,
new Vector2(s_UVScratch[x].x, s_UVScratch[y].y),
new Vector2(s_UVScratch[x2].x, s_UVScratch[y2].y));
}
}
}
/// 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 : UnityEngine.Sprites.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)
{
var spriteRatio = size.x / size.y;
var rectRatio = r.width / r.height;
if (spriteRatio > rectRatio)
{
var oldHeight = r.height;
r.height = r.width * (1.0f / spriteRatio);
r.y += (oldHeight - r.height) * rectTransform.pivot.y;
}
else
{
var oldWidth = r.width;
r.width = r.height * spriteRatio;
r.x += (oldWidth - r.width) * rectTransform.pivot.x;
}
}
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;
}
public enum SliceFilledType
{
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