Last active
May 31, 2023 03:06
-
-
Save LokoSoloGames/8ff7b131a33435068df3a7681bf3f9a2 to your computer and use it in GitHub Desktop.
Unity UI Image that expand sprite borders in Sliced Image Type
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine.Sprites; | |
namespace UnityEngine.UI { | |
public class InversedSlicedImage : Image { | |
private static readonly Vector2[] s_VertCache = new Vector2[4]; | |
private static readonly Vector2[] s_UVCache = new Vector2[4]; | |
protected override void OnPopulateMesh(VertexHelper toFill) { | |
if (type == Type.Sliced && hasBorder) { | |
GenerateInversedSlicedSprite(toFill); | |
} else { | |
base.OnPopulateMesh(toFill); | |
} | |
} | |
void GenerateInversedSlicedSprite(VertexHelper toFill) { | |
var activeSprite = overrideSprite; | |
Vector4 outer = DataUtility.GetOuterUV(activeSprite); | |
Vector4 inner = DataUtility.GetInnerUV(activeSprite); | |
Vector4 padding = DataUtility.GetPadding(activeSprite); | |
Vector4 border = activeSprite.border; | |
Rect rect = GetPixelAdjustedRect(); | |
Vector2 spriteSize = activeSprite.rect.size; | |
spriteSize.x = spriteSize.x - border.x - border.z; | |
spriteSize.y = spriteSize.y - border.y - border.w; | |
spriteSize = GetAdjustedSpriteSize(spriteSize / multipliedPixelsPerUnit, rect); | |
padding = padding / multipliedPixelsPerUnit; | |
s_VertCache[0] = new Vector2(padding.x, padding.y); | |
s_VertCache[3] = new Vector2(rect.width - padding.z, rect.height - padding.w); | |
var slicedWidth = rect.width - padding.x - padding.z - spriteSize.x; | |
var slicedHeight = rect.height - padding.y - padding.w - spriteSize.y; | |
var borderWidth = border.x + border.z; | |
if (borderWidth <= 0f) { | |
s_VertCache[1].x = s_VertCache[0].x; | |
s_VertCache[2].x = s_VertCache[3].x; | |
} else { | |
s_VertCache[1].x = slicedWidth * border.x / borderWidth + padding.x; | |
s_VertCache[2].x = rect.width - slicedWidth * border.z / borderWidth - padding.z; | |
} | |
var borderHeight = border.y + border.w; | |
if (borderHeight <= 0f) { | |
s_VertCache[1].y = s_VertCache[0].y; | |
s_VertCache[2].y = s_VertCache[3].y; | |
} else { | |
s_VertCache[1].y = slicedHeight * border.y / borderHeight + padding.y; | |
s_VertCache[2].y = rect.height - slicedHeight * border.w / borderHeight - padding.w; | |
} | |
for (int i = 0; i < 4; ++i) { | |
s_VertCache[i].x += rect.x; | |
s_VertCache[i].y += rect.y; | |
} | |
s_UVCache[0] = new Vector2(outer.x, outer.y); | |
s_UVCache[1] = new Vector2(inner.x, inner.y); | |
s_UVCache[2] = new Vector2(inner.z, inner.w); | |
s_UVCache[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_VertCache[x].x, s_VertCache[y].y), | |
new Vector2(s_VertCache[x2].x, s_VertCache[y2].y), | |
color, | |
new Vector2(s_UVCache[x].x, s_UVCache[y].y), | |
new Vector2(s_UVCache[x2].x, s_UVCache[y2].y)); | |
} | |
} | |
} | |
static void AddQuad(VertexHelper vertexHelper, in Vector2 posMin, in Vector2 posMax, in Color32 color, in Vector2 uvMin, in 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 Vector2 GetAdjustedSpriteSize(Vector2 spriteSize, in Rect adjustedRect) { | |
Rect originalRect = rectTransform.rect; | |
for (int axis = 0; axis <= 1; axis++) { | |
var size = adjustedRect.size[axis]; | |
// The adjusted rect (adjusted for pixel correctness) | |
if (originalRect.size[axis] != 0) { | |
var borderScaleRatio = size / originalRect.size[axis]; | |
spriteSize[axis] *= borderScaleRatio; | |
} | |
// If the rect is smaller than the sprite, then there's not room for the borders. | |
if (size < spriteSize[axis] && spriteSize[axis] != 0) { | |
spriteSize[axis] = size; | |
} | |
} | |
return spriteSize; | |
} | |
#if UNITY_EDITOR | |
protected override void Reset() { | |
base.Reset(); | |
type = Type.Sliced; | |
} | |
#endif | |
#if UNITY_EDITOR | |
static UnityEditor.MonoScript _script; | |
[UnityEditor.MenuItem("CONTEXT/Image/Replace as Inversed Sliced Image")] | |
static void ReplaceFromBuiltInImage(UnityEditor.MenuCommand command) { | |
if (!_script) { | |
var tmpGO = new GameObject("tempOBJ"); | |
var inst = tmpGO.AddComponent<InversedSlicedImage>(); | |
_script = UnityEditor.MonoScript.FromMonoBehaviour(inst); | |
DestroyImmediate(tmpGO); | |
} | |
var go = ((Component)command.context).gameObject; | |
UnityEditor.Undo.RegisterCompleteObjectUndo(go, string.Format("Replace Image as Inversed Sliced Image in {0}", go.name)); | |
UnityEditor.SerializedObject so = new UnityEditor.SerializedObject(command.context); | |
UnityEditor.SerializedProperty scriptProperty = so.FindProperty("m_Script"); | |
so.Update(); | |
scriptProperty.objectReferenceValue = _script; | |
so.ApplyModifiedProperties(); | |
} | |
#endif | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment