Created
January 6, 2017 02:13
-
-
Save benloong/cec2f0741cb16a8b3b0159330e8dab00 to your computer and use it in GitHub Desktop.
Unity RectTransform Border
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 System.Collections; | |
using System.Collections.Generic; | |
using UnityEngine; | |
using UnityEngine.UI; | |
public class UIBorder : MaskableGraphic { | |
[SerializeField] | |
float cornerRadius = 5; | |
[SerializeField] | |
[Range(1, 10)] | |
int cornerSegment = 2; | |
[SerializeField] | |
float width = 10; | |
//[SerializeField] | |
Texture texture; | |
[SerializeField] | |
Vector2 tiling = new Vector2(1, 1); | |
public override Texture mainTexture | |
{ | |
get | |
{ | |
return texture; | |
} | |
} | |
static List<UIVertex> vertexStream = new List<UIVertex>(); | |
static List<int> indexStream = new List<int>(); | |
protected override void OnPopulateMesh(VertexHelper vh) | |
{ | |
var r = GetPixelAdjustedRect(); | |
vh.Clear(); | |
GenerateCorner(r, vh); | |
} | |
void GenerateBox(Rect r, VertexHelper vh) | |
{ | |
float perimeter = r.width * 2 + r.height * 2; | |
Color color = this.color; | |
if (width > 0) | |
{ | |
//for (int i = 0; i < 4; i++) | |
{ | |
float uv = r.height / perimeter * tiling.x; | |
vh.AddVert(new Vector3(r.xMin, r.yMin), color, new Vector2(0, 0)); | |
vh.AddVert(new Vector3(r.xMin, r.yMax), color, new Vector2(uv, 0)); | |
vh.AddVert(new Vector3(r.xMin+width, r.yMax-width), color, new Vector2(uv, tiling.y)); | |
vh.AddVert(new Vector3(r.xMin+ width, r.yMin+width), color, new Vector2(0, tiling.y)); | |
vh.AddTriangle(0, 1, 2); | |
vh.AddTriangle(2, 3, 0); | |
} | |
{ | |
float uv = (r.height + r.width) / perimeter * tiling.x; | |
vh.AddVert(new Vector3(r.xMax, r.yMax), color, new Vector2(uv, 0)); | |
vh.AddVert(new Vector3(r.xMax-width, r.yMax-width), color, new Vector2(uv, tiling.y)); | |
vh.AddTriangle(1, 4, 5); | |
vh.AddTriangle(5, 2, 1); | |
} | |
{ | |
float uv = (r.height + r.width + r.height) / perimeter * tiling.x; | |
vh.AddVert(new Vector3(r.xMax, r.yMin), color, new Vector2(uv, 0)); | |
vh.AddVert(new Vector3(r.xMax - width, r.yMin + width), color, new Vector2(uv, tiling.y)); | |
vh.AddTriangle(4, 6, 7); | |
vh.AddTriangle(7, 5, 4); | |
} | |
{ | |
float uv = tiling.x; | |
vh.AddVert(new Vector3(r.xMin, r.yMin), color, new Vector2(uv, 0)); | |
vh.AddVert(new Vector3(r.xMin + width, r.yMin + width), color, new Vector2(uv, tiling.y)); | |
vh.AddTriangle(6, 8, 9); | |
vh.AddTriangle(9, 7, 6); | |
} | |
} | |
} | |
void GenerateCorner(Rect rect, VertexHelper vh) | |
{ | |
vertexStream.Clear(); | |
indexStream.Clear(); | |
vh.Clear(); | |
float cornerSize = Mathf.Max(width, cornerRadius); | |
float cornerOffset = cornerSize; | |
{ | |
Vector3 begin = new Vector2(rect.xMin, rect.yMin) + new Vector2(0, cornerOffset); | |
Vector3 end = new Vector3(rect.xMin, rect.yMax) - new Vector3(0, cornerOffset); | |
BeginLine(vertexStream, indexStream, begin, end); | |
AddLine(vertexStream, indexStream, begin, end); | |
Vector3 cornerBegin = end; | |
Vector3 cornerEnd = new Vector3(rect.xMin, rect.yMax) + new Vector3(cornerOffset, 0); | |
AddTopLeftCorner(vertexStream, indexStream, cornerBegin, cornerEnd); | |
} | |
{ | |
Vector3 begin = new Vector3(rect.xMin, rect.yMax) + new Vector3(cornerOffset, 0); | |
Vector3 end = new Vector3(rect.xMax, rect.yMax) - new Vector3(cornerOffset, 0); | |
AddLine(vertexStream, indexStream, begin, end); | |
Vector3 cornerBegin = end; | |
Vector3 cornerEnd = new Vector3(rect.xMax, rect.yMax) - new Vector3(0, cornerOffset, 0); | |
AddTopRightCorner(vertexStream, indexStream, cornerBegin, cornerEnd); | |
} | |
{ | |
Vector3 begin = new Vector3(rect.xMax, rect.yMax) - new Vector3(0, cornerOffset, 0); | |
Vector3 end = new Vector3(rect.xMax, rect.yMin) + new Vector3(0, cornerOffset, 0); | |
AddLine(vertexStream, indexStream, begin, end); | |
Vector3 cornerBegin = end; | |
Vector3 cornerEnd = new Vector3(rect.xMax, rect.yMin) - new Vector3(cornerOffset, 0); | |
AddBottomRightCorner(vertexStream, indexStream, cornerBegin, cornerEnd); | |
} | |
{ | |
Vector3 begin = new Vector3(rect.xMax, rect.yMin) - new Vector3(cornerOffset, 0); | |
Vector3 end = new Vector3(rect.xMin, rect.yMin) + new Vector3(cornerOffset, 0); | |
AddLine(vertexStream, indexStream, begin, end); | |
Vector3 cornerBegin = end; | |
Vector3 cornerEnd = new Vector2(rect.xMin, rect.yMin) + new Vector2(0, cornerOffset); | |
AddBottomLeftCorner(vertexStream, indexStream, cornerBegin, cornerEnd); | |
} | |
EndLine(); | |
PopulateUV(vertexStream); | |
vh.AddUIVertexStream(vertexStream, indexStream); | |
} | |
void PopulateUV(List<UIVertex> vertexStream) | |
{ | |
float length = 0; | |
for (int i = 0; i < vertexStream.Count-4; i += 2) | |
{ | |
var p0 = vertexStream[i].position; | |
var p1 = vertexStream[i + 1].position; | |
var p2 = vertexStream[i + 2].position; | |
var p3 = vertexStream[i + 3].position; | |
length += Vector3.Distance((p0 + p1) * 0.5f, (p2 + p3) * 0.5f); | |
} | |
float d = 0; | |
for (int i = 0; i < vertexStream.Count - 3; i += 2) | |
{ | |
UIVertex v1 = vertexStream[i]; | |
UIVertex v2 = vertexStream[i + 1]; | |
UIVertex v3 = vertexStream[i + 2]; | |
UIVertex v4 = vertexStream[i + 3]; | |
float y = d / length * tiling.y; | |
v1.uv0 = new Vector2(d / length * tiling.y, 0); | |
v2.uv0 = new Vector2(y, tiling.x); | |
vertexStream[i] = v1; | |
vertexStream[i + 1] = v2; | |
Vector3 p12 = (v1.position + v2.position) * 0.5f; | |
Vector3 p34 = (v3.position + v4.position) * 0.5f; | |
d += Vector3.Distance(p12, p34); | |
y = d / length * tiling.y; | |
v3.uv0 = new Vector2(d / length * tiling.y, 0); | |
v4.uv0 = new Vector2( y, tiling.x); | |
vertexStream[i + 2] = v3; | |
vertexStream[i + 3] = v4; | |
} | |
} | |
/// <summary> | |
/// 添加直线 | |
/// </summary> | |
/// <param name="vertexStream"></param> | |
/// <param name="indexStream"></param> | |
/// <param name="begin"></param> | |
/// <param name="end"></param> | |
void AddLine(List<UIVertex> vertexStream, List<int> indexStream, Vector3 begin, Vector3 end) | |
{ | |
UIVertex outer = UIVertex.simpleVert; | |
UIVertex inner = UIVertex.simpleVert; | |
outer.color = color; | |
inner.color = color; | |
float offset = width; | |
Vector3 perpendicular = Vector3.Cross(end - begin, Vector3.forward).normalized; | |
outer.position = end; | |
inner.position = end + perpendicular * offset; | |
vertexStream.Add(outer); | |
vertexStream.Add(inner); | |
int count = vertexStream.Count; | |
{ | |
indexStream.Add(count - 4); | |
indexStream.Add(count - 2); | |
indexStream.Add(count - 1); | |
} | |
{ | |
indexStream.Add(count - 1); | |
indexStream.Add(count - 3); | |
indexStream.Add(count - 4); | |
} | |
} | |
void BeginLine(List<UIVertex> vertexStream, List<int> indexStream, Vector3 begin, Vector3 end) | |
{ | |
UIVertex outer = UIVertex.simpleVert; | |
UIVertex inner = UIVertex.simpleVert; | |
outer.color = color; | |
inner.color = color; | |
float offset = width; | |
Vector3 perpendicular = Vector3.Cross(end - begin, Vector3.forward).normalized; | |
outer.position = begin; | |
inner.position = begin + perpendicular * offset; | |
vertexStream.Add(outer); | |
vertexStream.Add(inner); | |
} | |
void EndLine() | |
{ | |
} | |
void AddTopLeftCorner(List<UIVertex> vertexStream, List<int> indexStream, Vector3 begin, Vector3 end) | |
{ | |
/// 使用 innerEdge 半径小于 宽度时,否则 计算 inner Point | |
float offset = width - cornerRadius; | |
if (offset > 0) | |
{ | |
Vector3 inner = end - new Vector3(0, width); | |
AddArc(vertexStream, indexStream, begin + new Vector3(0, offset, 0), end - new Vector3(offset, 0), inner); | |
AddSegment(vertexStream, indexStream, end, inner); | |
} | |
else | |
{ | |
AddArc(vertexStream, indexStream, begin, end); | |
} | |
} | |
void AddTopRightCorner(List<UIVertex> vertexStream, List<int> indexStream, Vector3 begin, Vector3 end) | |
{ | |
/// 使用 innerEdge 半径小于 宽度时,否则 计算 inner Point | |
float offset = width - cornerRadius; | |
if (offset > 0) | |
{ | |
Vector3 inner = end - new Vector3(width, 0); | |
AddArc(vertexStream, indexStream, begin + new Vector3(offset, 0, 0), end + new Vector3(0, offset, 0), inner); | |
AddSegment(vertexStream, indexStream, end, inner); | |
} | |
else | |
{ | |
AddArc(vertexStream, indexStream, begin, end); | |
} | |
} | |
void AddBottomRightCorner(List<UIVertex> vertexStream, List<int> indexStream, Vector3 begin, Vector3 end) | |
{ | |
/// 使用 innerEdge 半径小于 宽度时,否则 计算 inner Point | |
float offset = width - cornerRadius; | |
if (offset > 0) | |
{ | |
Vector3 inner = end + new Vector3(0, width); | |
AddArc(vertexStream, indexStream, begin + new Vector3(0, -offset, 0), end + new Vector3(offset, 0, 0), inner); | |
AddSegment(vertexStream, indexStream, end, inner); | |
} | |
else | |
{ | |
AddArc(vertexStream, indexStream, begin, end); | |
} | |
} | |
void AddBottomLeftCorner(List<UIVertex> vertexStream, List<int> indexStream, Vector3 begin, Vector3 end) | |
{ | |
/// 使用 innerEdge 半径小于 宽度时,否则 计算 inner Point | |
float offset = width - cornerRadius; | |
if (offset > 0) | |
{ | |
Vector3 inner = end + new Vector3(width, 0); | |
AddArc(vertexStream, indexStream, begin + new Vector3(-offset, 0, 0), end + new Vector3(0, -offset, 0), inner); | |
AddSegment(vertexStream, indexStream, end, inner); | |
} | |
else | |
{ | |
AddArc(vertexStream, indexStream, begin, end); | |
} | |
} | |
void AddArc(List<UIVertex> vertexStream, List<int> indexStream, Vector3 arcBegin, Vector3 arcEnd, Vector3? innerEdge = null) | |
{ | |
Vector3 dir = arcEnd - arcBegin; | |
Vector3 perpendicular = Vector3.Cross(dir, Vector3.forward).normalized; | |
Vector3 center = arcBegin + dir * 0.5f + perpendicular * dir.magnitude * 0.5f; | |
//Vector3 innerEdge = center + perpendicular * (width - cornerRadius); | |
Vector3 centerDir = arcBegin - center; | |
//bool isUseInnerEdge = cornerRadius < width; | |
float angle = 90f / cornerSegment; | |
for (int i = 0; i <= cornerSegment; i++) | |
{ | |
Quaternion rotation = Quaternion.AngleAxis(-angle * i, Vector3.forward); | |
Vector3 newDir = rotation * centerDir; | |
Vector3 point1 = center + newDir; | |
Vector3 point2 = innerEdge ?? (point1 - newDir.normalized * width); | |
AddSegment(vertexStream, indexStream, point1, point2); | |
} | |
} | |
void AddSegment(List<UIVertex> vertexStream, List<int> indexStream, Vector3 outter, Vector3 inner) | |
{ | |
UIVertex v0 = UIVertex.simpleVert; | |
v0.position = outter; | |
v0.color = color; | |
UIVertex v1 = UIVertex.simpleVert; | |
v1.position = inner; | |
v1.color = color; | |
AddSegment(vertexStream, indexStream, v0, v1); | |
} | |
void AddSegment(List<UIVertex> vertexStream, List<int> indexStream, UIVertex outter, UIVertex inner) | |
{ | |
vertexStream.Add(outter); | |
vertexStream.Add(inner); | |
int count = vertexStream.Count; | |
{ | |
indexStream.Add(count - 4); | |
indexStream.Add(count - 2); | |
indexStream.Add(count - 1); | |
} | |
{ | |
indexStream.Add(count - 1); | |
indexStream.Add(count - 3); | |
indexStream.Add(count - 4); | |
} | |
} | |
#if UNITY_EDITOR | |
protected override void OnValidate() | |
{ | |
var r = GetPixelAdjustedRect(); | |
width = Mathf.Max(0, Mathf.Min(width, Mathf.Min(r.width, r.height) * 0.5f)); | |
cornerRadius = Mathf.Max(0, Mathf.Min(cornerRadius, Mathf.Min(r.width, r.height) * 0.5f)); | |
SetVerticesDirty(); | |
} | |
#endif | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment