NodeScript+ 导入了个 UI Extend

Signed-off-by: TRADER_FOER <lhf190@outlook.com>
This commit is contained in:
2026-05-23 21:05:16 +08:00
parent 7ea1f1d8c3
commit 51878f15ae
531 changed files with 198095 additions and 144473 deletions

View File

@@ -0,0 +1,66 @@
/// Credit koohddang
/// Sourced from - http://forum.unity3d.com/threads/onfillvbo-to-onpopulatemesh-help.353977/#post-2299311
using System;
namespace UnityEngine.UI.Extensions
{
[AddComponentMenu("UI/Extensions/Primitives/Diamond Graph")]
public class DiamondGraph : UIPrimitiveBase
{
[SerializeField]
private float m_a = 1;
[SerializeField]
private float m_b = 1;
[SerializeField]
private float m_c = 1;
[SerializeField]
private float m_d = 1;
public float A
{
get { return m_a; }
set { m_a = value; }
}
public float B
{
get { return m_b; }
set { m_b = value; }
}
public float C
{
get { return m_c; }
set { m_c = value; }
}
public float D
{
get { return m_d; }
set { m_d = value; }
}
protected override void OnPopulateMesh(VertexHelper vh)
{
vh.Clear();
float wHalf = rectTransform.rect.width / 2;
//float hHalf = rectTransform.rect.height / 2;
m_a = Math.Min(1, Math.Max(0, m_a));
m_b = Math.Min(1, Math.Max(0, m_b));
m_c = Math.Min(1, Math.Max(0, m_c));
m_d = Math.Min(1, Math.Max(0, m_d));
Color32 color32 = color;
vh.AddVert(new Vector3(-wHalf * m_a, 0), color32, new Vector2(0f, 0f));
vh.AddVert(new Vector3(0, wHalf * m_b), color32, new Vector2(0f, 1f));
vh.AddVert(new Vector3(wHalf * m_c, 0), color32, new Vector2(1f, 1f));
vh.AddVert(new Vector3(0, -wHalf * m_d), color32, new Vector2(1f, 0f));
vh.AddTriangle(0, 1, 2);
vh.AddTriangle(2, 3, 0);
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 891530df3841c0041ab9982359e02e0f
timeCreated: 1442708993
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,226 @@
/// Credit zge, jeremie sellam
/// Sourced from - http://forum.unity3d.com/threads/draw-circles-or-primitives-on-the-new-ui-canvas.272488/#post-2293224
/// Updated from - https://bitbucket.org/SimonDarksideJ/unity-ui-extensions/issues/65/a-better-uicircle
/// Update 10.9.2017 (tswalker, https://bitbucket.org/tswalker/)
///
/// * Modified component to utilize vertex stream instead of quads
/// * Improved accuracy of geometry fill to prevent edge "sliding" and redundant tris
/// * Added progress capability to allow component to be used as an indicator
/// * Added methods for use during runtime and event system(s) with other components
/// * Change some terminology of members to reflect other component property changes
/// * Added padding capability
/// * Only utilizes UV0 set for sprite/texture mapping (maps UV to geometry 0,1 boundary)
/// * Sample usage in scene "UICircleProgress"
/// Note: moving the pivot around from center to an edge can cause strange things
/// as well as having the RectTransform be smaller than the Thickness and/or Padding.
/// When making an initial layout for the component, it would be best to test multiple
/// aspect ratios and resolutions to ensure consistent behaviour.
using System.Collections.Generic;
namespace UnityEngine.UI.Extensions
{
[AddComponentMenu("UI/Extensions/Primitives/UI Circle")]
public class UICircle : UIPrimitiveBase
{
[Tooltip("The Arc Invert property will invert the construction of the Arc.")]
public bool ArcInvert = true;
[Tooltip("The Arc property is a percentage of the entire circumference of the circle.")]
[Range(0, 1)]
public float Arc = 1;
[Tooltip("The Arc Steps property defines the number of segments that the Arc will be divided into.")]
[Range(0, 1000)]
public int ArcSteps = 100;
[Tooltip("The Arc Rotation property permits adjusting the geometry orientation around the Z axis.")]
[Range(0, 360)]
public int ArcRotation = 0;
[Tooltip("The Progress property allows the primitive to be used as a progression indicator.")]
[Range(0, 1)]
public float Progress = 0;
private float _progress = 0;
public Color ProgressColor = new Color(255, 255, 255, 255);
public bool Fill = true; //solid circle
public float Thickness = 5;
public int Padding = 0;
private List<int> indices = new List<int>(); //ordered list of vertices per tri
private List<UIVertex> vertices = new List<UIVertex>();
private Vector2 uvCenter = new Vector2(0.5f, 0.5f);
protected override void OnPopulateMesh(VertexHelper vh)
{
int _inversion = ArcInvert ? -1 : 1;
float Diameter = (rectTransform.rect.width < rectTransform.rect.height ? rectTransform.rect.width : rectTransform.rect.height) - Padding; //correct for padding and always fit RectTransform
float outerDiameter = -rectTransform.pivot.x * Diameter;
float innerDiameter = -rectTransform.pivot.x * Diameter + Thickness;
vh.Clear();
indices.Clear();
vertices.Clear();
int i = 0;
int j = 1;
int k = 0;
float stepDegree = (Arc * 360f) / ArcSteps;
_progress = ArcSteps * Progress;
float rad = _inversion * Mathf.Deg2Rad * ArcRotation;
float X = Mathf.Cos(rad);
float Y = Mathf.Sin(rad);
var vertex = UIVertex.simpleVert;
vertex.color = _progress > 0 ? ProgressColor : color;
//initial vertex
vertex.position = new Vector2(outerDiameter * X, outerDiameter * Y);
vertex.uv0 = new Vector2(vertex.position.x / Diameter + 0.5f, vertex.position.y / Diameter + 0.5f);
vertices.Add(vertex);
var iV = new Vector2(innerDiameter * X, innerDiameter * Y);
if (Fill) iV = Vector2.zero; //center vertex to pivot
vertex.position = iV;
vertex.uv0 = Fill ? uvCenter : new Vector2(vertex.position.x / Diameter + 0.5f, vertex.position.y / Diameter + 0.5f);
vertices.Add(vertex);
for (int counter = 1; counter <= ArcSteps; counter++)
{
rad = _inversion * Mathf.Deg2Rad * (counter * stepDegree + ArcRotation);
X = Mathf.Cos(rad);
Y = Mathf.Sin(rad);
vertex.color = counter > _progress ? color : ProgressColor;
vertex.position = new Vector2(outerDiameter * X, outerDiameter * Y);
vertex.uv0 = new Vector2(vertex.position.x / Diameter + 0.5f, vertex.position.y / Diameter + 0.5f);
vertices.Add(vertex);
//add additional vertex if required and generate indices for tris in clockwise order
if (!Fill)
{
vertex.position = new Vector2(innerDiameter * X, innerDiameter * Y);
vertex.uv0 = new Vector2(vertex.position.x / Diameter + 0.5f, vertex.position.y / Diameter + 0.5f);
vertices.Add(vertex);
k = j;
indices.Add(i);
indices.Add(j + 1);
indices.Add(j);
j++;
i = j;
j++;
indices.Add(i);
indices.Add(j);
indices.Add(k);
}
else
{
indices.Add(i);
indices.Add(j + 1);
//Fills (solid circle) with progress require an additional vertex to
// prevent the base circle from becoming a gradient from center to edge
if (counter > _progress)
{
indices.Add(ArcSteps + 2);
}
else
{
indices.Add(1);
}
j++;
i = j;
}
}
//this vertex is added to the end of the list to simplify index ordering on geometry fill
if (Fill)
{
vertex.position = iV;
vertex.color = color;
vertex.uv0 = uvCenter;
vertices.Add(vertex);
}
vh.AddUIVertexStream(vertices, indices);
}
//the following methods may be used during run-time
//to update the properties of the component
public void SetProgress(float progress)
{
Progress = progress;
SetVerticesDirty();
}
public void SetArc(float arc)
{
Arc = arc;
SetVerticesDirty();
}
public void SetArcSteps(int steps)
{
ArcSteps = steps;
SetVerticesDirty();
}
public void SetInvertArc(bool invert)
{
ArcInvert = invert;
SetVerticesDirty();
}
public void SetArcRotation(int rotation)
{
ArcRotation = rotation;
SetVerticesDirty();
}
public void SetFill(bool fill)
{
Fill = fill;
SetVerticesDirty();
}
public void SetBaseColor(Color color)
{
this.color = color;
SetVerticesDirty();
}
public void UpdateBaseAlpha(float value)
{
var _color = this.color;
_color.a = value;
this.color = _color;
SetVerticesDirty();
}
public void SetProgressColor(Color color)
{
ProgressColor = color;
SetVerticesDirty();
}
public void UpdateProgressAlpha(float value)
{
ProgressColor.a = value;
SetVerticesDirty();
}
public void SetPadding(int padding)
{
Padding = padding;
SetVerticesDirty();
}
public void SetThickness(int thickness)
{
Thickness = thickness;
SetVerticesDirty();
}
}
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 8185cad1aa202d04ebb9e14ffa533a87
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@@ -0,0 +1,219 @@
/// <summary>
/// Created by Freezy - ElicitIce.nl
/// Posted on Unity Forums http://forum.unity3d.com/threads/cut-corners-primative.359494/
///
/// Free for any use and alteration, source code may not be sold without my permission.
/// If you make improvements on this script please share them with the community.
///
///
/// Here is a script that will take a rectangular TransformRect and cut off some corners based on the corner size.
/// This is great for when you need a quick and easy non-square panel/image.
/// Enjoy!
/// It adds an additional square if the relevant side has a corner cut, it then offsets the ends to simulate a cut corner.
/// UVs are being set, but might be skewed when a texture is applied.
/// You could hide the additional colors by using the following:
/// http://rumorgames.com/hide-in-inspector/
///
/// </summary>
namespace UnityEngine.UI.Extensions
{
[AddComponentMenu("UI/Extensions/Primitives/Cut Corners")]
public class UICornerCut : UIPrimitiveBase
{
public Vector2 cornerSize = new Vector2(16, 16);
[Header("Corners to cut")]
[SerializeField]
private bool m_cutUL = true;
[SerializeField]
private bool m_cutUR;
[SerializeField]
private bool m_cutLL;
[SerializeField]
private bool m_cutLR;
[Tooltip("Up-Down colors become Left-Right colors")]
[SerializeField]
private bool m_makeColumns;
[Header("Color the cut bars differently")]
[SerializeField]
private bool m_useColorUp;
[SerializeField]
private Color32 m_colorUp;
[SerializeField]
private bool m_useColorDown;
[SerializeField]
private Color32 m_colorDown;
public bool CutUL
{
get { return m_cutUL; }
set { m_cutUL = value; SetAllDirty(); }
}
public bool CutUR
{
get { return m_cutUR; }
set { m_cutUR = value; SetAllDirty(); }
}
public bool CutLL
{
get { return m_cutLL; }
set { m_cutLL = value; SetAllDirty(); }
}
public bool CutLR
{
get { return m_cutLR; }
set { m_cutLR = value; SetAllDirty(); }
}
public bool MakeColumns
{
get { return m_makeColumns; }
set { m_makeColumns = value; SetAllDirty(); }
}
public bool UseColorUp
{
get { return m_useColorUp; }
set { m_useColorUp = value; }
}
public Color32 ColorUp
{
get { return m_colorUp; }
set { m_colorUp = value; }
}
public bool UseColorDown
{
get { return m_useColorDown; }
set { m_useColorDown = value; }
}
public Color32 ColorDown
{
get { return m_colorDown; }
set { m_colorDown = value; }
}
protected override void OnPopulateMesh(VertexHelper vh)
{
var rect = rectTransform.rect;
var rectNew = rect;
Color32 color32 = color;
bool up = m_cutUL | m_cutUR;
bool down = m_cutLL | m_cutLR;
bool left = m_cutLL | m_cutUL;
bool right = m_cutLR | m_cutUR;
bool any = up | down;
if (any && cornerSize.sqrMagnitude > 0)
{
//nibble off the sides
vh.Clear();
if (left)
rectNew.xMin += cornerSize.x;
if (down)
rectNew.yMin += cornerSize.y;
if (up)
rectNew.yMax -= cornerSize.y;
if (right)
rectNew.xMax -= cornerSize.x;
//add two squares to the main square
Vector2 ul, ur, ll, lr;
if (m_makeColumns)
{
ul = new Vector2(rect.xMin, m_cutUL ? rectNew.yMax : rect.yMax);
ur = new Vector2(rect.xMax, m_cutUR ? rectNew.yMax : rect.yMax);
ll = new Vector2(rect.xMin, m_cutLL ? rectNew.yMin : rect.yMin);
lr = new Vector2(rect.xMax, m_cutLR ? rectNew.yMin : rect.yMin);
if (left)
AddSquare(
ll, ul,
new Vector2(rectNew.xMin, rect.yMax),
new Vector2(rectNew.xMin, rect.yMin),
rect, m_useColorUp ? m_colorUp : color32, vh);
if (right)
AddSquare(
ur, lr,
new Vector2(rectNew.xMax, rect.yMin),
new Vector2(rectNew.xMax, rect.yMax),
rect, m_useColorDown ? m_colorDown : color32, vh);
}
else
{
ul = new Vector2(m_cutUL ? rectNew.xMin : rect.xMin, rect.yMax);
ur = new Vector2(m_cutUR ? rectNew.xMax : rect.xMax, rect.yMax);
ll = new Vector2(m_cutLL ? rectNew.xMin : rect.xMin, rect.yMin);
lr = new Vector2(m_cutLR ? rectNew.xMax : rect.xMax, rect.yMin);
if (down)
AddSquare(
lr, ll,
new Vector2(rect.xMin, rectNew.yMin),
new Vector2(rect.xMax, rectNew.yMin),
rect, m_useColorDown ? m_colorDown : color32, vh);
if (up)
AddSquare(
ul, ur,
new Vector2(rect.xMax, rectNew.yMax),
new Vector2(rect.xMin, rectNew.yMax),
rect, m_useColorUp ? m_colorUp : color32, vh);
}
//center
if (m_makeColumns)
AddSquare(new Rect(rectNew.xMin, rect.yMin, rectNew.width, rect.height), rect, color32, vh);
else
AddSquare(new Rect(rect.xMin, rectNew.yMin, rect.width, rectNew.height), rect, color32, vh);
}
}
private static void AddSquare(Rect rect, Rect rectUV, Color32 color32, VertexHelper vh) {
int v0 = AddVert(rect.xMin, rect.yMin, rectUV, color32, vh);
int v1 = AddVert(rect.xMin, rect.yMax, rectUV, color32, vh);
int v2 = AddVert(rect.xMax, rect.yMax, rectUV, color32, vh);
int v3 = AddVert(rect.xMax, rect.yMin, rectUV, color32, vh);
vh.AddTriangle(v0, v1, v2);
vh.AddTriangle(v2, v3, v0);
}
private static void AddSquare(Vector2 a, Vector2 b, Vector2 c, Vector2 d, Rect rectUV, Color32 color32, VertexHelper vh) {
int v0 = AddVert(a.x, a.y, rectUV, color32, vh);
int v1 = AddVert(b.x, b.y, rectUV, color32, vh);
int v2 = AddVert(c.x, c.y, rectUV, color32, vh);
int v3 = AddVert(d.x, d.y, rectUV, color32, vh);
vh.AddTriangle(v0, v1, v2);
vh.AddTriangle(v2, v3, v0);
}
/// <summary>
/// Auto UV handler within the assigned area
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="area"></param>
/// <param name="color32"></param>
/// <param name="vh"></param>
private static int AddVert(float x, float y, Rect area, Color32 color32, VertexHelper vh) {
var uv = new Vector2(
Mathf.InverseLerp(area.xMin, area.xMax, x),
Mathf.InverseLerp(area.yMin, area.yMax, y)
);
vh.AddVert(new Vector3(x, y), color32, uv);
return vh.currentVertCount - 1;
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 7a6e620ffa5a4e64b8875761130d0139
timeCreated: 1444419848
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,141 @@
/// Credit John Hattan (http://thecodezone.com/)
/// Sourced from - https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/issues/117/uigridrenderer
namespace UnityEngine.UI.Extensions
{
[AddComponentMenu("UI/Extensions/Primitives/UIGridRenderer")]
public class UIGridRenderer : UILineRenderer
{
[SerializeField]
private int m_GridColumns = 10;
[SerializeField]
private int m_GridRows = 10;
/// <summary>
/// Number of columns in the Grid
/// </summary>
public int GridColumns
{
get
{
return m_GridColumns;
}
set
{
if (m_GridColumns == value)
return;
m_GridColumns = value;
SetAllDirty();
}
}
/// <summary>
/// Number of rows in the grid.
/// </summary>
public int GridRows
{
get
{
return m_GridRows;
}
set
{
if (m_GridRows == value)
return;
m_GridRows = value;
SetAllDirty();
}
}
protected override void OnPopulateMesh(VertexHelper vh)
{
relativeSize = true;
int ArraySize = (GridRows * 3) + 1;
if(GridRows % 2 == 0)
++ArraySize; // needs one more line
ArraySize += (GridColumns * 3) + 1;
m_points = new Vector2[ArraySize];
int Index = 0;
for(int i = 0; i < GridRows; ++i)
{
float xFrom = 1;
float xTo = 0;
if(i % 2 == 0)
{
// reach left instead
xFrom = 0;
xTo = 1;
}
float y = ((float)i) / GridRows;
m_points[Index].x = xFrom;
m_points[Index].y = y;
++Index;
m_points[Index].x = xTo;
m_points[Index].y = y;
++Index;
m_points[Index].x = xTo;
m_points[Index].y = (float)(i + 1) / GridRows;
++Index;
}
if(GridRows % 2 == 0)
{
// two lines to get to 0, 1
m_points[Index].x = 1;
m_points[Index].y = 1;
++Index;
}
m_points[Index].x = 0;
m_points[Index].y = 1;
++Index;
// line is now at 0,1, so we can draw the columns
for(int i = 0; i < GridColumns; ++i)
{
float yFrom = 1;
float yTo = 0;
if(i % 2 == 0)
{
// reach up instead
yFrom = 0;
yTo = 1;
}
float x = ((float)i) / GridColumns;
m_points[Index].x = x;
m_points[Index].y = yFrom;
++Index;
m_points[Index].x = x;
m_points[Index].y = yTo;
++Index;
m_points[Index].x = (float)(i + 1) / GridColumns;
m_points[Index].y = yTo;
++Index;
}
if(GridColumns % 2 == 0)
{
// one more line to get to 1, 1
m_points[Index].x = 1;
m_points[Index].y = 1;
}
else
{
// one more line to get to 1, 0
m_points[Index].x = 1;
m_points[Index].y = 0;
}
base.OnPopulateMesh(vh);
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 35ff6e8800018654d9558db07c4cd080
timeCreated: 1487335372
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,508 @@
/// Credit jack.sydorenko, firagon
/// Sourced from - http://forum.unity3d.com/threads/new-ui-and-line-drawing.253772/
/// Updated/Refactored from - http://forum.unity3d.com/threads/new-ui-and-line-drawing.253772/#post-2528050
using System.Collections.Generic;
namespace UnityEngine.UI.Extensions
{
[AddComponentMenu("UI/Extensions/Primitives/UILineRenderer")]
[RequireComponent(typeof(RectTransform))]
public class UILineRenderer : UIPrimitiveBase
{
private enum SegmentType
{
Start,
Middle,
End,
Full,
}
public enum JoinType
{
Bevel,
Miter
}
public enum BezierType
{
None,
Quick,
Basic,
Improved,
Catenary,
}
private const float MIN_MITER_JOIN = 15 * Mathf.Deg2Rad;
// A bevel 'nice' join displaces the vertices of the line segment instead of simply rendering a
// quad to connect the endpoints. This improves the look of textured and transparent lines, since
// there is no overlapping.
private const float MIN_BEVEL_NICE_JOIN = 30 * Mathf.Deg2Rad;
private static Vector2 UV_TOP_LEFT, UV_BOTTOM_LEFT, UV_TOP_CENTER_LEFT, UV_TOP_CENTER_RIGHT, UV_BOTTOM_CENTER_LEFT, UV_BOTTOM_CENTER_RIGHT, UV_TOP_RIGHT, UV_BOTTOM_RIGHT;
private static Vector2[] startUvs, middleUvs, endUvs, fullUvs;
[SerializeField, Tooltip("Points to draw lines between\n Can be improved using the Resolution Option")]
internal Vector2[] m_points;
[SerializeField, Tooltip("Segments to be drawn\n This is a list of arrays of points")]
internal List<Vector2[]> m_segments;
[SerializeField, Tooltip("Thickness of the line")]
internal float lineThickness = 2;
[SerializeField, Tooltip("Use the relative bounds of the Rect Transform (0,0 -> 0,1) or screen space coordinates")]
internal bool relativeSize;
[SerializeField, Tooltip("Do the points identify a single line or split pairs of lines")]
internal bool lineList;
[SerializeField, Tooltip("Add end caps to each line\nMultiple caps when used with Line List")]
internal bool lineCaps;
[SerializeField, Tooltip("Resolution of the Bezier curve, different to line Resolution")]
internal int bezierSegmentsPerCurve = 10;
public float LineThickness
{
get { return lineThickness; }
set { lineThickness = value; SetAllDirty(); }
}
public bool RelativeSize
{
get { return relativeSize; }
set { relativeSize = value; SetAllDirty(); }
}
public bool LineList
{
get { return lineList; }
set { lineList = value; SetAllDirty(); }
}
public bool LineCaps
{
get { return lineCaps; }
set { lineCaps = value; SetAllDirty(); }
}
[Tooltip("The type of Join used between lines, Square/Mitre or Curved/Bevel")]
public JoinType LineJoins = JoinType.Bevel;
[Tooltip("Bezier method to apply to line, see docs for options\nCan't be used in conjunction with Resolution as Bezier already changes the resolution")]
public BezierType BezierMode = BezierType.None;
public int BezierSegmentsPerCurve
{
get { return bezierSegmentsPerCurve; }
set { bezierSegmentsPerCurve = value; }
}
[HideInInspector]
public bool drivenExternally = false;
/// <summary>
/// Points to be drawn in the line.
/// </summary>
public Vector2[] Points
{
get
{
return m_points;
}
set
{
if (m_points == value) return;
if (value == null || value.Length == 0)
{
m_points = new Vector2[1];
}
else
{
m_points = value;
}
SetAllDirty();
}
}
/// <summary>
/// List of Segments to be drawn.
/// </summary>
public List<Vector2[]> Segments
{
get
{
return m_segments;
}
set
{
m_segments = value;
SetAllDirty();
}
}
private void PopulateMesh(VertexHelper vh, Vector2[] pointsToDraw)
{
//If Bezier is desired, pick the implementation
if (BezierMode != BezierType.None && BezierMode != BezierType.Catenary && pointsToDraw.Length > 3) {
BezierPath bezierPath = new BezierPath ();
bezierPath.SetControlPoints (pointsToDraw);
bezierPath.SegmentsPerCurve = bezierSegmentsPerCurve;
List<Vector2> drawingPoints;
switch (BezierMode) {
case BezierType.Basic:
drawingPoints = bezierPath.GetDrawingPoints0 ();
break;
case BezierType.Improved:
drawingPoints = bezierPath.GetDrawingPoints1 ();
break;
default:
drawingPoints = bezierPath.GetDrawingPoints2 ();
break;
}
pointsToDraw = drawingPoints.ToArray ();
}
if (BezierMode == BezierType.Catenary && pointsToDraw.Length == 2) {
CableCurve cable = new CableCurve (pointsToDraw);
cable.slack = Resolution;
cable.steps = BezierSegmentsPerCurve;
pointsToDraw = cable.Points ();
}
if (ImproveResolution != ResolutionMode.None) {
pointsToDraw = IncreaseResolution (pointsToDraw);
}
// scale based on the size of the rect or use absolute, this is switchable
var sizeX = !relativeSize ? 1 : rectTransform.rect.width;
var sizeY = !relativeSize ? 1 : rectTransform.rect.height;
var offsetX = -rectTransform.pivot.x * sizeX;
var offsetY = -rectTransform.pivot.y * sizeY;
// Generate the quads that make up the wide line
var segments = new List<UIVertex[]> ();
if (lineList) {
//Loop through list in line pairs, skipping drawing between lines
for (var i = 1; i < pointsToDraw.Length; i += 2) {
var start = pointsToDraw [i - 1];
var end = pointsToDraw [i];
start = new Vector2 (start.x * sizeX + offsetX, start.y * sizeY + offsetY);
end = new Vector2 (end.x * sizeX + offsetX, end.y * sizeY + offsetY);
if (lineCaps) {
segments.Add (CreateLineCap (start, end, SegmentType.Start));
}
// Originally, UV's had to be wrapped per segment to ensure textures rendered correctly, however when tested in 2019.4, this no longer seems to be an issue.
segments.Add(CreateLineSegment(start, end, SegmentType.Middle));
if (lineCaps) {
segments.Add (CreateLineCap (start, end, SegmentType.End));
}
}
} else {
//Draw full lines
for (var i = 1; i < pointsToDraw.Length; i++) {
var start = pointsToDraw [i - 1];
var end = pointsToDraw [i];
start = new Vector2 (start.x * sizeX + offsetX, start.y * sizeY + offsetY);
end = new Vector2 (end.x * sizeX + offsetX, end.y * sizeY + offsetY);
if (lineCaps && i == 1) {
segments.Add (CreateLineCap (start, end, SegmentType.Start));
}
segments.Add (CreateLineSegment (start, end, SegmentType.Middle));
if (lineCaps && i == pointsToDraw.Length - 1) {
segments.Add (CreateLineCap (start, end, SegmentType.End));
}
}
}
// Add the line segments to the vertex helper, creating any joins as needed
for (var i = 0; i < segments.Count; i++) {
if (!lineList && i < segments.Count - 1) {
var vec1 = segments [i] [1].position - segments [i] [2].position;
var vec2 = segments [i + 1] [2].position - segments [i + 1] [1].position;
var angle = Vector2.Angle (vec1, vec2) * Mathf.Deg2Rad;
// Positive sign means the line is turning in a 'clockwise' direction
var sign = Mathf.Sign (Vector3.Cross (vec1.normalized, vec2.normalized).z);
// Calculate the miter point
var miterDistance = lineThickness / (2 * Mathf.Tan (angle / 2));
var miterPointA = segments [i] [2].position - vec1.normalized * miterDistance * sign;
var miterPointB = segments [i] [3].position + vec1.normalized * miterDistance * sign;
var joinType = LineJoins;
if (joinType == JoinType.Miter) {
// Make sure we can make a miter join without too many artifacts.
if (miterDistance < vec1.magnitude / 2 && miterDistance < vec2.magnitude / 2 && angle > MIN_MITER_JOIN) {
segments [i] [2].position = miterPointA;
segments [i] [3].position = miterPointB;
segments [i + 1] [0].position = miterPointB;
segments [i + 1] [1].position = miterPointA;
} else {
joinType = JoinType.Bevel;
}
}
if (joinType == JoinType.Bevel) {
if (miterDistance < vec1.magnitude / 2 && miterDistance < vec2.magnitude / 2 && angle > MIN_BEVEL_NICE_JOIN) {
if (sign < 0) {
segments [i] [2].position = miterPointA;
segments [i + 1] [1].position = miterPointA;
} else {
segments [i] [3].position = miterPointB;
segments [i + 1] [0].position = miterPointB;
}
}
var join = new UIVertex[] { segments [i] [2], segments [i] [3], segments [i + 1] [0], segments [i + 1] [1] };
vh.AddUIVertexQuad (join);
}
}
vh.AddUIVertexQuad (segments [i]);
}
if (vh.currentVertCount > 64000) {
Debug.LogError ("Max Verticies size is 64000, current mesh verticies count is [" + vh.currentVertCount + "] - Cannot Draw");
vh.Clear ();
return;
}
}
protected override void OnPopulateMesh(VertexHelper vh)
{
if (m_points != null && m_points.Length > 0) {
GeneratedUVs ();
vh.Clear ();
PopulateMesh (vh, m_points);
}
if (m_segments != null && m_segments.Count > 0) {
GeneratedUVs ();
vh.Clear ();
for (int s = 0; s < m_segments.Count; s++) {
Vector2[] pointsToDraw = m_segments [s];
PopulateMesh (vh, pointsToDraw);
}
}
}
private UIVertex[] CreateLineCap(Vector2 start, Vector2 end, SegmentType type)
{
if (type == SegmentType.Start)
{
var capStart = start - ((end - start).normalized * lineThickness / 2);
return CreateLineSegment(capStart, start, SegmentType.Start);
}
else if (type == SegmentType.End)
{
var capEnd = end + ((end - start).normalized * lineThickness / 2);
return CreateLineSegment(end, capEnd, SegmentType.End);
}
Debug.LogError("Bad SegmentType passed in to CreateLineCap. Must be SegmentType.Start or SegmentType.End");
return null;
}
private UIVertex[] CreateLineSegment(Vector2 start, Vector2 end, SegmentType type, UIVertex[] previousVert = null)
{
Vector2 offset = new Vector2((start.y - end.y), end.x - start.x).normalized * lineThickness / 2;
Vector2 v1 = Vector2.zero;
Vector2 v2 = Vector2.zero;
if (previousVert != null) {
v1 = new Vector2(previousVert[3].position.x, previousVert[3].position.y);
v2 = new Vector2(previousVert[2].position.x, previousVert[2].position.y);
} else {
v1 = start - offset;
v2 = start + offset;
}
var v3 = end + offset;
var v4 = end - offset;
//Return the VDO with the correct uvs
switch (type)
{
case SegmentType.Start:
return SetVbo(new[] { v1, v2, v3, v4 }, startUvs);
case SegmentType.End:
return SetVbo(new[] { v1, v2, v3, v4 }, endUvs);
case SegmentType.Full:
return SetVbo(new[] { v1, v2, v3, v4 }, fullUvs);
default:
return SetVbo(new[] { v1, v2, v3, v4 }, middleUvs);
}
}
protected override void GeneratedUVs()
{
if (activeSprite != null)
{
var outer = Sprites.DataUtility.GetOuterUV(activeSprite);
var inner = Sprites.DataUtility.GetInnerUV(activeSprite);
UV_TOP_LEFT = new Vector2(outer.x, outer.y);
UV_BOTTOM_LEFT = new Vector2(outer.x, outer.w);
UV_TOP_CENTER_LEFT = new Vector2(inner.x, inner.y);
UV_TOP_CENTER_RIGHT = new Vector2(inner.z, inner.y);
UV_BOTTOM_CENTER_LEFT = new Vector2(inner.x, inner.w);
UV_BOTTOM_CENTER_RIGHT = new Vector2(inner.z, inner.w);
UV_TOP_RIGHT = new Vector2(outer.z, outer.y);
UV_BOTTOM_RIGHT = new Vector2(outer.z, outer.w);
}
else
{
UV_TOP_LEFT = Vector2.zero;
UV_BOTTOM_LEFT = new Vector2(0, 1);
UV_TOP_CENTER_LEFT = new Vector2(0.5f, 0);
UV_TOP_CENTER_RIGHT = new Vector2(0.5f, 0);
UV_BOTTOM_CENTER_LEFT = new Vector2(0.5f, 1);
UV_BOTTOM_CENTER_RIGHT = new Vector2(0.5f, 1);
UV_TOP_RIGHT = new Vector2(1, 0);
UV_BOTTOM_RIGHT = Vector2.one;
}
startUvs = new[] { UV_TOP_LEFT, UV_BOTTOM_LEFT, UV_BOTTOM_CENTER_LEFT, UV_TOP_CENTER_LEFT };
middleUvs = new[] { UV_TOP_CENTER_LEFT, UV_BOTTOM_CENTER_LEFT, UV_BOTTOM_CENTER_RIGHT, UV_TOP_CENTER_RIGHT };
endUvs = new[] { UV_TOP_CENTER_RIGHT, UV_BOTTOM_CENTER_RIGHT, UV_BOTTOM_RIGHT, UV_TOP_RIGHT };
fullUvs = new[] { UV_TOP_LEFT, UV_BOTTOM_LEFT, UV_BOTTOM_RIGHT, UV_TOP_RIGHT };
}
protected override void ResolutionToNativeSize(float distance)
{
if (UseNativeSize)
{
m_Resolution = distance / (activeSprite.rect.width / pixelsPerUnit);
lineThickness = activeSprite.rect.height / pixelsPerUnit;
}
}
private int GetSegmentPointCount()
{
if (Segments?.Count > 0)
{
int pointCount = 0;
foreach (var segment in Segments)
{
pointCount += segment.Length;
}
return pointCount;
}
return Points.Length;
}
/// <summary>
/// Get the Vector2 position of a line index
/// </summary>
/// <remarks>
/// Positive numbers should be used to specify Index and Segment
/// </remarks>
/// <param name="index">Required Index of the point, starting from point 1</param>
/// <param name="segmentIndex">(optional) Required Segment the point is held in, Starting from Segment 1</param>
/// <returns>Vector2 position of the point within UI Space</returns>
public Vector2 GetPosition(int index, int segmentIndex = 0)
{
if (segmentIndex > 0)
{
return Segments[segmentIndex - 1][index - 1];
}
else if (Segments?.Count > 0)
{
var segmentIndexCount = 0;
var indexCount = index;
foreach (var segment in Segments)
{
if (indexCount - segment.Length > 0)
{
indexCount -= segment.Length;
segmentIndexCount += 1;
}
else
{
break;
}
}
return Segments[segmentIndexCount][indexCount - 1];
}
else
{
return Points[index - 1];
}
}
/// <summary>
/// Calculates the position of a point on the curve, given t (0-1), start point, control points and end point.
/// </summary>
/// <param name="t">Required Percentage between start and end point, in the range 0 to 1</param>
/// <param name="p1">Required Starting point</param>
/// <param name="p1">Required Control point 1</param>
/// <param name="p1">Required Control point 2</param>
/// <param name="p1">Required End point</param>
/// <returns>Vector2 position of point on curve at t percentage between p1 and p4</returns>
public Vector2 CalculatePointOnCurve(float t, Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4)
{
var t2 = t * t;
var t3 = t2 * t;
var x = p1.x + (-p1.x * 3 + t * (3 * p1.x - p1.x * t)) * t + (3 * p2.x + t * (-6 * p2.x + p2.x * 3 * t)) * t +
(p3.x * 3 - p3.x * 3 * t) * t2 + p4.x * t3;
var y = p1.y + (-p1.y * 3 + t * (3 * p1.y - p1.y * t)) * t + (3 * p2.y + t * (-6 * p2.y + p2.y * 3 * t)) * t +
(p3.y * 3 - p3.y * 3 * t) * t2 + p4.y * t3;
return new Vector2(x, y);
}
/// <summary>
/// Get the Vector2 position of a line within a specific segment
/// </summary>
/// <param name="index">Required Index of the point, starting from point 1</param>
/// <param name="segmentIndex"> Required Segment the point is held in, Starting from Segment 1</param>
/// <returns>Vector2 position of the point within UI Space</returns>
public Vector2 GetPositionBySegment(int index, int segment)
{
return Segments[segment][index - 1];
}
/// <summary>
/// Get the closest point between two given Vector2s from a given Vector2 point
/// </summary>
/// <param name="p1">Starting position</param>
/// <param name="p2">End position</param>
/// <param name="p3">Desired / Selected point</param>
/// <returns>Closest Vector2 position of the target within UI Space</returns>
public Vector2 GetClosestPoint(Vector2 p1, Vector2 p2, Vector2 p3)
{
Vector2 from_p1_to_p3 = p3 - p1;
Vector2 from_p1_to_p2 = p2 - p1;
float dot = Vector2.Dot(from_p1_to_p3, from_p1_to_p2.normalized);
dot /= from_p1_to_p2.magnitude;
float t = Mathf.Clamp01(dot);
return p1 + from_p1_to_p2 * t;
}
protected override void OnEnable()
{
base.OnEnable();
if (m_points == null || m_points?.Length == 0)
{
m_points = new Vector2[1];
}
if (transform.GetComponent<RectTransform>().position != Vector3.zero)
{
Debug.LogWarning("A Line Renderer component should be on a RectTransform positioned at (0,0,0), do not use in child Objects.\nFor best results, create separate RectTransforms as children of the canvas positioned at (0,0) for a UILineRenderer and do not move.");
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 5b33b2e663e78774c9f0c9af55018725
timeCreated: 1440845580
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,146 @@
/// Credit Steve Westhoff, jack.sydorenko, firagon
/// Sourced from - https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/issues/324
/// Refactored and updated for performance from UILineRenderer by Steve Westhoff
using System.Collections.Generic;
namespace UnityEngine.UI.Extensions
{
[AddComponentMenu("UI/Extensions/Primitives/UILineRendererFIFO")]
[RequireComponent(typeof(RectTransform))]
public class UILineRendererFIFO : UIPrimitiveBase
{
private static readonly Vector2[] middleUvs = new[] { new Vector2(0.5f, 0), new Vector2(0.5f, 1), new Vector2(0.5f, 1), new Vector2(0.5f, 0) };
private List<Vector2> addedPoints = new List<Vector2>();
private bool needsResize;
[SerializeField, Tooltip("Thickness of the line")]
private float lineThickness = 1;
[SerializeField, Tooltip("Points to draw lines between\n Can be improved using the Resolution Option")]
private List<Vector2> points = new List<Vector2>();
[SerializeField, Tooltip("Segments to be drawn\n This is a list of arrays of points")]
private List<UIVertex[]> segments = new List<UIVertex[]>();
/// <summary>
/// Thickness of the line
/// </summary>
public float LineThickness
{
get { return lineThickness; }
set { lineThickness = value; SetAllDirty(); }
}
/// <summary>
/// Points to be drawn in the line.
/// </summary>
/// <remarks>Don't add points to the list directly, use the add / remove functions</remarks>
public List<Vector2> Points
{
get
{
return points;
}
set
{
if (points == value)
return;
points = value;
SetAllDirty();
}
}
/// <summary>
/// Adds to head
/// </summary>
/// <param name="point"></param>
public void AddPoint(Vector2 point) {
points.Add(point);
addedPoints.Add(point);
}
/// <summary>
/// Removes from tail (FIFO)
/// </summary>
public void RemovePoint() {
points.RemoveAt(0);
needsResize = true;
}
/// <summary>
/// Clear all the points from the LineRenderer
/// </summary>
public void ClearPoints()
{
segments.Clear();
points.Clear();
addedPoints.Clear();
needsResize = false;
}
public void Resize() {
needsResize = true;
}
protected override void OnPopulateMesh(VertexHelper vertexHelper) {
vertexHelper.Clear();
if(needsResize) {
needsResize = false;
segments.Clear();
addedPoints = new List<Vector2>(points);
}
int count = addedPoints.Count;
if(count > 1) {
PopulateMesh(addedPoints, vertexHelper);
if(count % 2 == 0) {
addedPoints.Clear();
} else {
Vector2 extraPoint = addedPoints[count - 1];
addedPoints.Clear();
addedPoints.Add(extraPoint);
}
}
}
void PopulateMesh(List<Vector2> pointsToDraw, VertexHelper vertexHelper) {
if(ImproveResolution != ResolutionMode.None) {
pointsToDraw = IncreaseResolution(pointsToDraw);
}
float sizeX = rectTransform.rect.width;
float sizeY = rectTransform.rect.height;
float offsetX = -rectTransform.pivot.x * sizeX;
float offsetY = -rectTransform.pivot.y * sizeY;
for(int i = 1; i < pointsToDraw.Count; i += 2) {
Vector2 start = pointsToDraw[i - 1];
Vector2 end = pointsToDraw[i];
start = new Vector2(start.x * sizeX + offsetX, start.y * sizeY + offsetY);
end = new Vector2(end.x * sizeX + offsetX, end.y * sizeY + offsetY);
UIVertex[] segment = CreateLineSegment(start, end, segments.Count > 1 ? segments[segments.Count - 2] : null);
segments.Add(segment);
}
for(int i = 0; i < segments.Count; i++) {
vertexHelper.AddUIVertexQuad(segments[i]);
}
if(vertexHelper.currentVertCount > 64000) {
Debug.LogError("Max Verticies size is 64000, current mesh vertcies count is [" + vertexHelper.currentVertCount + "] - Cannot Draw");
vertexHelper.Clear();
}
}
UIVertex[] CreateLineSegment(Vector2 start, Vector2 end, UIVertex[] previousVert = null) {
Vector2 offset = new Vector2(start.y - end.y, end.x - start.x).normalized * lineThickness * 0.5f;
Vector2 v1;
Vector2 v2;
if(previousVert != null) {
v1 = new Vector2(previousVert[3].position.x, previousVert[3].position.y);
v2 = new Vector2(previousVert[2].position.x, previousVert[2].position.y);
} else {
v1 = start - offset;
v2 = start + offset;
}
return SetVbo(new[] { v1, v2, end + offset, end - offset }, middleUvs);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a3a91607af301f241b9f0a860c720b21
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,367 @@
/// Credit jack.sydorenko, firagon
/// Sourced from - http://forum.unity3d.com/threads/new-ui-and-line-drawing.253772/
/// Updated/Refactored from - http://forum.unity3d.com/threads/new-ui-and-line-drawing.253772/#post-2528050
using System.Collections.Generic;
namespace UnityEngine.UI.Extensions
{
[AddComponentMenu("UI/Extensions/Primitives/UILineRendererList")]
[RequireComponent(typeof(RectTransform))]
public class UILineRendererList : UIPrimitiveBase
{
private enum SegmentType
{
Start,
Middle,
End,
Full,
}
public enum JoinType
{
Bevel,
Miter
}
public enum BezierType
{
None,
Quick,
Basic,
Improved,
Catenary,
}
private const float MIN_MITER_JOIN = 15 * Mathf.Deg2Rad;
// A bevel 'nice' join displaces the vertices of the line segment instead of simply rendering a
// quad to connect the endpoints. This improves the look of textured and transparent lines, since
// there is no overlapping.
private const float MIN_BEVEL_NICE_JOIN = 30 * Mathf.Deg2Rad;
private static Vector2 UV_TOP_LEFT, UV_BOTTOM_LEFT, UV_TOP_CENTER_LEFT, UV_TOP_CENTER_RIGHT, UV_BOTTOM_CENTER_LEFT, UV_BOTTOM_CENTER_RIGHT, UV_TOP_RIGHT, UV_BOTTOM_RIGHT;
private static Vector2[] startUvs, middleUvs, endUvs, fullUvs;
[SerializeField, Tooltip("Points to draw lines between\n Can be improved using the Resolution Option")]
internal List<Vector2> m_points;
// [SerializeField, Tooltip("Segments to be drawn\n This is a list of arrays of points")]
//internal List<Vector2[]> m_segments;
[SerializeField, Tooltip("Thickness of the line")]
internal float lineThickness = 2;
[SerializeField, Tooltip("Use the relative bounds of the Rect Transform (0,0 -> 0,1) or screen space coordinates")]
internal bool relativeSize;
[SerializeField, Tooltip("Do the points identify a single line or split pairs of lines")]
internal bool lineList;
[SerializeField, Tooltip("Add end caps to each line\nMultiple caps when used with Line List")]
internal bool lineCaps;
[SerializeField, Tooltip("Resolution of the Bezier curve, different to line Resolution")]
internal int bezierSegmentsPerCurve = 10;
public float LineThickness
{
get { return lineThickness; }
set { lineThickness = value; SetAllDirty(); }
}
public bool RelativeSize
{
get { return relativeSize; }
set { relativeSize = value; SetAllDirty(); }
}
public bool LineList
{
get { return lineList; }
set { lineList = value; SetAllDirty(); }
}
public bool LineCaps
{
get { return lineCaps; }
set { lineCaps = value; SetAllDirty(); }
}
[Tooltip("The type of Join used between lines, Square/Mitre or Curved/Bevel")]
public JoinType LineJoins = JoinType.Bevel;
[Tooltip("Bezier method to apply to line, see docs for options\nCan't be used in conjunction with Resolution as Bezier already changes the resolution")]
public BezierType BezierMode = BezierType.None;
public int BezierSegmentsPerCurve
{
get { return bezierSegmentsPerCurve; }
set { bezierSegmentsPerCurve = value; }
}
[HideInInspector]
public bool drivenExternally = false;
/// <summary>
/// Points to be drawn in the line.
/// </summary>
/// <remarks>Don't add points to the list directly, use the add / remove functions</remarks>
public List<Vector2> Points
{
get
{
return m_points;
}
set
{
if (m_points == value)
return;
m_points = value;
SetAllDirty();
}
}
public void AddPoint(Vector2 pointToAdd)
{
m_points.Add(pointToAdd);
SetAllDirty();
}
public void RemovePoint(Vector2 pointToRemove)
{
m_points.Remove(pointToRemove);
SetAllDirty();
}
public void ClearPoints()
{
m_points.Clear();
SetAllDirty();
}
private void PopulateMesh(VertexHelper vh, List<Vector2> pointsToDraw)
{
//If Bezier is desired, pick the implementation
if (BezierMode != BezierType.None && BezierMode != BezierType.Catenary && pointsToDraw.Count > 3) {
BezierPath bezierPath = new BezierPath ();
bezierPath.SetControlPoints (pointsToDraw);
bezierPath.SegmentsPerCurve = bezierSegmentsPerCurve;
List<Vector2> drawingPoints;
switch (BezierMode) {
case BezierType.Basic:
drawingPoints = bezierPath.GetDrawingPoints0 ();
break;
case BezierType.Improved:
drawingPoints = bezierPath.GetDrawingPoints1 ();
break;
default:
drawingPoints = bezierPath.GetDrawingPoints2 ();
break;
}
pointsToDraw = drawingPoints;
}
if (BezierMode == BezierType.Catenary && pointsToDraw.Count == 2) {
CableCurve cable = new CableCurve (pointsToDraw);
cable.slack = Resolution;
cable.steps = BezierSegmentsPerCurve;
pointsToDraw.Clear();
pointsToDraw.AddRange(cable.Points());
}
if (ImproveResolution != ResolutionMode.None) {
pointsToDraw = IncreaseResolution (pointsToDraw);
}
// scale based on the size of the rect or use absolute, this is switchable
var sizeX = !relativeSize ? 1 : rectTransform.rect.width;
var sizeY = !relativeSize ? 1 : rectTransform.rect.height;
var offsetX = -rectTransform.pivot.x * sizeX;
var offsetY = -rectTransform.pivot.y * sizeY;
// Generate the quads that make up the wide line
var segments = new List<UIVertex[]> ();
if (lineList) {
for (var i = 1; i < pointsToDraw.Count; i += 2) {
var start = pointsToDraw [i - 1];
var end = pointsToDraw [i];
start = new Vector2 (start.x * sizeX + offsetX, start.y * sizeY + offsetY);
end = new Vector2 (end.x * sizeX + offsetX, end.y * sizeY + offsetY);
if (lineCaps) {
segments.Add (CreateLineCap (start, end, SegmentType.Start));
}
//segments.Add(CreateLineSegment(start, end, SegmentType.Full));
segments.Add (CreateLineSegment (start, end, SegmentType.Middle));
if (lineCaps) {
segments.Add (CreateLineCap (start, end, SegmentType.End));
}
}
} else {
for (var i = 1; i < pointsToDraw.Count; i++) {
var start = pointsToDraw [i - 1];
var end = pointsToDraw [i];
start = new Vector2 (start.x * sizeX + offsetX, start.y * sizeY + offsetY);
end = new Vector2 (end.x * sizeX + offsetX, end.y * sizeY + offsetY);
if (lineCaps && i == 1) {
segments.Add (CreateLineCap (start, end, SegmentType.Start));
}
segments.Add (CreateLineSegment (start, end, SegmentType.Middle));
//segments.Add(CreateLineSegment(start, end, SegmentType.Full));
if (lineCaps && i == pointsToDraw.Count - 1) {
segments.Add (CreateLineCap (start, end, SegmentType.End));
}
}
}
// Add the line segments to the vertex helper, creating any joins as needed
for (var i = 0; i < segments.Count; i++) {
if (!lineList && i < segments.Count - 1) {
var vec1 = segments [i] [1].position - segments [i] [2].position;
var vec2 = segments [i + 1] [2].position - segments [i + 1] [1].position;
var angle = Vector2.Angle (vec1, vec2) * Mathf.Deg2Rad;
// Positive sign means the line is turning in a 'clockwise' direction
var sign = Mathf.Sign (Vector3.Cross (vec1.normalized, vec2.normalized).z);
// Calculate the miter point
var miterDistance = lineThickness / (2 * Mathf.Tan (angle / 2));
var miterPointA = segments [i] [2].position - vec1.normalized * miterDistance * sign;
var miterPointB = segments [i] [3].position + vec1.normalized * miterDistance * sign;
var joinType = LineJoins;
if (joinType == JoinType.Miter) {
// Make sure we can make a miter join without too many artifacts.
if (miterDistance < vec1.magnitude / 2 && miterDistance < vec2.magnitude / 2 && angle > MIN_MITER_JOIN) {
segments [i] [2].position = miterPointA;
segments [i] [3].position = miterPointB;
segments [i + 1] [0].position = miterPointB;
segments [i + 1] [1].position = miterPointA;
} else {
joinType = JoinType.Bevel;
}
}
if (joinType == JoinType.Bevel) {
if (miterDistance < vec1.magnitude / 2 && miterDistance < vec2.magnitude / 2 && angle > MIN_BEVEL_NICE_JOIN) {
if (sign < 0) {
segments [i] [2].position = miterPointA;
segments [i + 1] [1].position = miterPointA;
} else {
segments [i] [3].position = miterPointB;
segments [i + 1] [0].position = miterPointB;
}
}
var join = new UIVertex[] { segments [i] [2], segments [i] [3], segments [i + 1] [0], segments [i + 1] [1] };
vh.AddUIVertexQuad (join);
}
}
vh.AddUIVertexQuad (segments [i]);
}
if (vh.currentVertCount > 64000) {
Debug.LogError ("Max Verticies size is 64000, current mesh verticies count is [" + vh.currentVertCount + "] - Cannot Draw");
vh.Clear ();
return;
}
}
protected override void OnPopulateMesh(VertexHelper vh)
{
if (m_points != null && m_points.Count > 0) {
GeneratedUVs ();
vh.Clear ();
PopulateMesh (vh, m_points);
}
}
private UIVertex[] CreateLineCap(Vector2 start, Vector2 end, SegmentType type)
{
if (type == SegmentType.Start)
{
var capStart = start - ((end - start).normalized * lineThickness / 2);
return CreateLineSegment(capStart, start, SegmentType.Start);
}
else if (type == SegmentType.End)
{
var capEnd = end + ((end - start).normalized * lineThickness / 2);
return CreateLineSegment(end, capEnd, SegmentType.End);
}
Debug.LogError("Bad SegmentType passed in to CreateLineCap. Must be SegmentType.Start or SegmentType.End");
return null;
}
private UIVertex[] CreateLineSegment(Vector2 start, Vector2 end, SegmentType type)
{
Vector2 offset = new Vector2((start.y - end.y), end.x - start.x).normalized * lineThickness / 2;
var v1 = start - offset;
var v2 = start + offset;
var v3 = end + offset;
var v4 = end - offset;
//Return the VDO with the correct uvs
switch (type)
{
case SegmentType.Start:
return SetVbo(new[] { v1, v2, v3, v4 }, startUvs);
case SegmentType.End:
return SetVbo(new[] { v1, v2, v3, v4 }, endUvs);
case SegmentType.Full:
return SetVbo(new[] { v1, v2, v3, v4 }, fullUvs);
default:
return SetVbo(new[] { v1, v2, v3, v4 }, middleUvs);
}
}
protected override void GeneratedUVs()
{
if (activeSprite != null)
{
var outer = Sprites.DataUtility.GetOuterUV(activeSprite);
var inner = Sprites.DataUtility.GetInnerUV(activeSprite);
UV_TOP_LEFT = new Vector2(outer.x, outer.y);
UV_BOTTOM_LEFT = new Vector2(outer.x, outer.w);
UV_TOP_CENTER_LEFT = new Vector2(inner.x, inner.y);
UV_TOP_CENTER_RIGHT = new Vector2(inner.z, inner.y);
UV_BOTTOM_CENTER_LEFT = new Vector2(inner.x, inner.w);
UV_BOTTOM_CENTER_RIGHT = new Vector2(inner.z, inner.w);
UV_TOP_RIGHT = new Vector2(outer.z, outer.y);
UV_BOTTOM_RIGHT = new Vector2(outer.z, outer.w);
}
else
{
UV_TOP_LEFT = Vector2.zero;
UV_BOTTOM_LEFT = new Vector2(0, 1);
UV_TOP_CENTER_LEFT = new Vector2(0.5f, 0);
UV_TOP_CENTER_RIGHT = new Vector2(0.5f, 0);
UV_BOTTOM_CENTER_LEFT = new Vector2(0.5f, 1);
UV_BOTTOM_CENTER_RIGHT = new Vector2(0.5f, 1);
UV_TOP_RIGHT = new Vector2(1, 0);
UV_BOTTOM_RIGHT = Vector2.one;
}
startUvs = new[] { UV_TOP_LEFT, UV_BOTTOM_LEFT, UV_BOTTOM_CENTER_LEFT, UV_TOP_CENTER_LEFT };
middleUvs = new[] { UV_TOP_CENTER_LEFT, UV_BOTTOM_CENTER_LEFT, UV_BOTTOM_CENTER_RIGHT, UV_TOP_CENTER_RIGHT };
endUvs = new[] { UV_TOP_CENTER_RIGHT, UV_BOTTOM_CENTER_RIGHT, UV_BOTTOM_RIGHT, UV_TOP_RIGHT };
fullUvs = new[] { UV_TOP_LEFT, UV_BOTTOM_LEFT, UV_BOTTOM_RIGHT, UV_TOP_RIGHT };
}
protected override void ResolutionToNativeSize(float distance)
{
if (UseNativeSize)
{
m_Resolution = distance / (activeSprite.rect.width / pixelsPerUnit);
lineThickness = activeSprite.rect.height / pixelsPerUnit;
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 34861a5dcec3dbf438f8288be97dd133
timeCreated: 1440845580
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,159 @@
/// Credit jonbro5556
/// Based on original LineRender script by jack.sydorenko
/// Sourced from - http://forum.unity3d.com/threads/new-ui-and-line-drawing.253772/
using System.Collections.Generic;
namespace UnityEngine.UI.Extensions
{
[AddComponentMenu("UI/Extensions/Primitives/UILineTextureRenderer")]
public class UILineTextureRenderer : UIPrimitiveBase
{
[SerializeField]
Rect m_UVRect = new Rect(0f, 0f, 1f, 1f);
[SerializeField]
private Vector2[] m_points;
public float LineThickness = 2;
public bool UseMargins;
public Vector2 Margin;
public bool relativeSize;
/// <summary>
/// UV rectangle used by the texture.
/// </summary>
public Rect uvRect
{
get
{
return m_UVRect;
}
set
{
if (m_UVRect == value)
return;
m_UVRect = value;
SetVerticesDirty();
}
}
/// <summary>
/// Points to be drawn in the line.
/// </summary>
public Vector2[] Points
{
get
{
return m_points;
}
set
{
if (m_points == value)
return;
m_points = value;
SetAllDirty();
}
}
protected override void OnPopulateMesh(VertexHelper vh)
{
// requires sets of quads
if (m_points == null || m_points.Length < 2)
m_points = new[] { new Vector2(0, 0), new Vector2(1, 1) };
var capSize = 24;
var sizeX = rectTransform.rect.width;
var sizeY = rectTransform.rect.height;
var offsetX = -rectTransform.pivot.x * rectTransform.rect.width;
var offsetY = -rectTransform.pivot.y * rectTransform.rect.height;
// don't want to scale based on the size of the rect, so this is switchable now
if (!relativeSize)
{
sizeX = 1;
sizeY = 1;
}
// build a new set of m_points taking into account the cap sizes.
// would be cool to support corners too, but that might be a bit tough :)
var pointList = new List<Vector2>();
pointList.Add(m_points[0]);
var capPoint = m_points[0] + (m_points[1] - m_points[0]).normalized * capSize;
pointList.Add(capPoint);
// should bail before the last point to add another cap point
for (int i = 1; i < m_points.Length - 1; i++)
{
pointList.Add(m_points[i]);
}
capPoint = m_points[m_points.Length - 1] - (m_points[m_points.Length - 1] - m_points[m_points.Length - 2]).normalized * capSize;
pointList.Add(capPoint);
pointList.Add(m_points[m_points.Length - 1]);
var Tempm_points = pointList.ToArray();
if (UseMargins)
{
sizeX -= Margin.x;
sizeY -= Margin.y;
offsetX += Margin.x / 2f;
offsetY += Margin.y / 2f;
}
vh.Clear();
Vector2 prevV1 = Vector2.zero;
Vector2 prevV2 = Vector2.zero;
for (int i = 1; i < Tempm_points.Length; i++)
{
var prev = Tempm_points[i - 1];
var cur = Tempm_points[i];
prev = new Vector2(prev.x * sizeX + offsetX, prev.y * sizeY + offsetY);
cur = new Vector2(cur.x * sizeX + offsetX, cur.y * sizeY + offsetY);
float angle = Mathf.Atan2(cur.y - prev.y, cur.x - prev.x) * 180f / Mathf.PI;
var v1 = prev + new Vector2(0, -LineThickness / 2);
var v2 = prev + new Vector2(0, +LineThickness / 2);
var v3 = cur + new Vector2(0, +LineThickness / 2);
var v4 = cur + new Vector2(0, -LineThickness / 2);
v1 = RotatePointAroundPivot(v1, prev, new Vector3(0, 0, angle));
v2 = RotatePointAroundPivot(v2, prev, new Vector3(0, 0, angle));
v3 = RotatePointAroundPivot(v3, cur, new Vector3(0, 0, angle));
v4 = RotatePointAroundPivot(v4, cur, new Vector3(0, 0, angle));
Vector2 uvTopLeft = Vector2.zero;
Vector2 uvBottomLeft = new Vector2(0, 1);
Vector2 uvTopCenter = new Vector2(0.5f, 0);
Vector2 uvBottomCenter = new Vector2(0.5f, 1);
Vector2 uvTopRight = new Vector2(1, 0);
Vector2 uvBottomRight = new Vector2(1, 1);
Vector2[] uvs = new[] { uvTopCenter, uvBottomCenter, uvBottomCenter, uvTopCenter };
if (i > 1)
vh.AddUIVertexQuad(SetVbo(new[] { prevV1, prevV2, v1, v2 }, uvs));
if (i == 1)
uvs = new[] { uvTopLeft, uvBottomLeft, uvBottomCenter, uvTopCenter };
else if (i == Tempm_points.Length - 1)
uvs = new[] { uvTopCenter, uvBottomCenter, uvBottomRight, uvTopRight };
vh.AddUIVertexQuad(SetVbo(new[] { v1, v2, v3, v4 }, uvs));
prevV1 = v3;
prevV2 = v4;
}
}
public Vector3 RotatePointAroundPivot(Vector3 point, Vector3 pivot, Vector3 angles)
{
Vector3 dir = point - pivot; // get point direction relative to pivot
dir = Quaternion.Euler(angles) * dir; // rotate it
point = dir + pivot; // calculate rotated point
return point; // return it
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: b801876ddb189f94f905cd1646564fbf
timeCreated: 1440845581
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,103 @@
/// Credit CiaccoDavide
/// Sourced from - http://ciaccodavi.de/unity/UIPolygon
namespace UnityEngine.UI.Extensions
{
[AddComponentMenu("UI/Extensions/Primitives/UI Polygon")]
public class UIPolygon : UIPrimitiveBase
{
public bool fill = true;
public float thickness = 5;
[Range(3, 360)]
public int sides = 3;
[Range(0, 360)]
public float rotation = 0;
[Range(0, 1)]
public float[] VerticesDistances = new float[3];
private float size = 0;
public void DrawPolygon(int _sides)
{
sides = _sides;
VerticesDistances = new float[_sides + 1];
for (int i = 0; i < _sides; i++) VerticesDistances[i] = 1; ;
rotation = 0;
SetAllDirty();
}
public void DrawPolygon(int _sides, float[] _VerticesDistances)
{
sides = _sides;
VerticesDistances = _VerticesDistances;
rotation = 0;
SetAllDirty();
}
public void DrawPolygon(int _sides, float[] _VerticesDistances, float _rotation)
{
sides = _sides;
VerticesDistances = _VerticesDistances;
rotation = _rotation;
SetAllDirty();
}
void Update()
{
size = rectTransform.rect.width;
if (rectTransform.rect.width > rectTransform.rect.height)
size = rectTransform.rect.height;
else
size = rectTransform.rect.width;
thickness = (float)Mathf.Clamp(thickness, 0, size / 2);
}
protected override void OnPopulateMesh(VertexHelper vh)
{
vh.Clear();
Vector2 prevX = Vector2.zero;
Vector2 prevY = Vector2.zero;
Vector2 uv0 = new Vector2(0, 0);
Vector2 uv1 = new Vector2(0, 1);
Vector2 uv2 = new Vector2(1, 1);
Vector2 uv3 = new Vector2(1, 0);
Vector2 pos0;
Vector2 pos1;
Vector2 pos2;
Vector2 pos3;
float degrees = 360f / sides;
int vertices = sides + 1;
if (VerticesDistances.Length != vertices)
{
VerticesDistances = new float[vertices];
for (int i = 0; i < vertices - 1; i++) VerticesDistances[i] = 1;
}
// last vertex is also the first!
VerticesDistances[vertices - 1] = VerticesDistances[0];
for (int i = 0; i < vertices; i++)
{
float outer = -rectTransform.pivot.x * size * VerticesDistances[i];
float inner = -rectTransform.pivot.x * size * VerticesDistances[i] + thickness;
float rad = Mathf.Deg2Rad * (i * degrees + rotation);
float c = Mathf.Cos(rad);
float s = Mathf.Sin(rad);
uv0 = new Vector2(0, 1);
uv1 = new Vector2(1, 1);
uv2 = new Vector2(1, 0);
uv3 = new Vector2(0, 0);
pos0 = prevX;
pos1 = new Vector2(outer * c, outer * s);
if (fill)
{
pos2 = Vector2.zero;
pos3 = Vector2.zero;
}
else
{
pos2 = new Vector2(inner * c, inner * s);
pos3 = prevY;
}
prevX = pos1;
prevY = pos2;
vh.AddUIVertexQuad(SetVbo(new[] { pos0, pos1, pos2, pos3 }, new[] { uv0, uv1, uv2, uv3 }));
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: fcd1b8078a416f844b695454a4358409
timeCreated: 1450200166
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,362 @@
using System;
using System.Collections.Generic;
namespace UnityEngine.UI.Extensions
{
public enum ResolutionMode
{
None,
PerSegment,
PerLine
}
[RequireComponent(typeof(CanvasRenderer))]
public class UIPrimitiveBase : MaskableGraphic, ILayoutElement, ICanvasRaycastFilter
{
static protected Material s_ETC1DefaultUI = null;
List<Vector2> outputList = new List<Vector2>();
[SerializeField] private Sprite m_Sprite;
public Sprite sprite { get { return m_Sprite; } set { if (SetPropertyUtility.SetClass(ref m_Sprite, value)) GeneratedUVs(); SetAllDirty(); } }
[NonSerialized]
private Sprite m_OverrideSprite;
public Sprite overrideSprite { get { return activeSprite; } set { if (SetPropertyUtility.SetClass(ref m_OverrideSprite, value)) GeneratedUVs(); SetAllDirty(); } }
protected Sprite activeSprite { get { return m_OverrideSprite != null ? m_OverrideSprite : sprite; } }
// Not serialized until we support read-enabled sprites better.
internal float m_EventAlphaThreshold = 1;
public float eventAlphaThreshold { get { return m_EventAlphaThreshold; } set { m_EventAlphaThreshold = value; } }
[SerializeField]
private ResolutionMode m_improveResolution;
public ResolutionMode ImproveResolution { get { return m_improveResolution; } set { m_improveResolution = value; SetAllDirty(); } }
[SerializeField]
protected float m_Resolution;
public float Resolution { get { return m_Resolution; } set { m_Resolution = value; SetAllDirty(); } }
[SerializeField]
private bool m_useNativeSize;
public bool UseNativeSize { get { return m_useNativeSize; } set { m_useNativeSize = value; SetAllDirty(); } }
protected UIPrimitiveBase()
{
useLegacyMeshGeneration = false;
}
/// <summary>
/// Default material used to draw everything if no explicit material was specified.
/// </summary>
static public Material defaultETC1GraphicMaterial
{
get
{
if (s_ETC1DefaultUI == null)
s_ETC1DefaultUI = Canvas.GetETC1SupportedCanvasMaterial();
return s_ETC1DefaultUI;
}
}
/// <summary>
/// Image's texture comes from the UnityEngine.Image.
/// </summary>
public override Texture mainTexture
{
get
{
if (activeSprite == null)
{
if (material != null && material.mainTexture != null)
{
return material.mainTexture;
}
return s_WhiteTexture;
}
return activeSprite.texture;
}
}
/// <summary>
/// Whether the Image has a border to work with.
/// </summary>
public bool hasBorder
{
get
{
if (activeSprite != null)
{
Vector4 v = activeSprite.border;
return v.sqrMagnitude > 0f;
}
return false;
}
}
public float pixelsPerUnit
{
get
{
float spritePixelsPerUnit = 100;
if (activeSprite)
spritePixelsPerUnit = activeSprite.pixelsPerUnit;
float referencePixelsPerUnit = 100;
if (canvas)
referencePixelsPerUnit = canvas.referencePixelsPerUnit;
return spritePixelsPerUnit / referencePixelsPerUnit;
}
}
public override Material material
{
get
{
if (m_Material != null)
return m_Material;
if (activeSprite && activeSprite.associatedAlphaSplitTexture != null)
return defaultETC1GraphicMaterial;
return defaultMaterial;
}
set
{
base.material = value;
}
}
protected UIVertex[] SetVbo(Vector2[] vertices, Vector2[] uvs)
{
UIVertex[] vbo = new UIVertex[4];
for (int i = 0; i < vertices.Length; i++)
{
var vert = UIVertex.simpleVert;
vert.color = color;
vert.position = vertices[i];
vert.uv0 = uvs[i];
vbo[i] = vert;
}
return vbo;
}
protected Vector2[] IncreaseResolution(Vector2[] input)
{
return IncreaseResolution(new List<Vector2>(input)).ToArray();
}
protected List<Vector2> IncreaseResolution(List<Vector2> input)
{
outputList.Clear();
switch (ImproveResolution)
{
case ResolutionMode.PerLine:
float totalDistance = 0, increments = 0;
for (int i = 0; i < input.Count - 1; i++)
{
totalDistance += Vector2.Distance(input[i], input[i + 1]);
}
ResolutionToNativeSize(totalDistance);
increments = totalDistance / m_Resolution;
var incrementCount = 0;
for (int i = 0; i < input.Count - 1; i++)
{
var p1 = input[i];
outputList.Add(p1);
var p2 = input[i + 1];
var segmentDistance = Vector2.Distance(p1, p2) / increments;
var incrementTime = 1f / segmentDistance;
for (int j = 0; j < segmentDistance; j++)
{
outputList.Add(Vector2.Lerp(p1, (Vector2)p2, j * incrementTime));
incrementCount++;
}
outputList.Add(p2);
}
break;
case ResolutionMode.PerSegment:
for (int i = 0; i < input.Count - 1; i++)
{
var p1 = input[i];
outputList.Add(p1);
var p2 = input[i + 1];
ResolutionToNativeSize(Vector2.Distance(p1, p2));
increments = 1f / m_Resolution;
for (Single j = 1; j < m_Resolution; j++)
{
outputList.Add(Vector2.Lerp(p1, (Vector2)p2, increments * j));
}
outputList.Add(p2);
}
break;
}
return outputList;
}
protected virtual void GeneratedUVs() { }
protected virtual void ResolutionToNativeSize(float distance) { }
#region ILayoutElement Interface
public virtual void CalculateLayoutInputHorizontal() { }
public virtual void CalculateLayoutInputVertical() { }
public virtual float minWidth { get { return 0; } }
public virtual float preferredWidth
{
get
{
if (overrideSprite == null)
return 0;
return overrideSprite.rect.size.x / pixelsPerUnit;
}
}
public virtual float flexibleWidth { get { return -1; } }
public virtual float minHeight { get { return 0; } }
public virtual float preferredHeight
{
get
{
if (overrideSprite == null)
return 0;
return overrideSprite.rect.size.y / pixelsPerUnit;
}
}
public virtual float flexibleHeight { get { return -1; } }
public virtual int layoutPriority { get { return 0; } }
#endregion
#region ICanvasRaycastFilter Interface
public virtual bool IsRaycastLocationValid(Vector2 screenPoint, Camera eventCamera)
{
// add test for line check
if (m_EventAlphaThreshold >= 1)
return true;
Sprite sprite = overrideSprite;
if (sprite == null)
return true;
Vector2 local;
RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, screenPoint, eventCamera, out local);
Rect rect = GetPixelAdjustedRect();
// Convert to have lower left corner as reference point.
local.x += rectTransform.pivot.x * rect.width;
local.y += rectTransform.pivot.y * rect.height;
local = MapCoordinate(local, rect);
//test local coord with Mesh
// Normalize local coordinates.
Rect spriteRect = sprite.textureRect;
Vector2 normalized = new Vector2(local.x / spriteRect.width, local.y / spriteRect.height);
// Convert to texture space.
float x = Mathf.Lerp(spriteRect.x, spriteRect.xMax, normalized.x) / sprite.texture.width;
float y = Mathf.Lerp(spriteRect.y, spriteRect.yMax, normalized.y) / sprite.texture.height;
try
{
return sprite.texture.GetPixelBilinear(x, y).a >= m_EventAlphaThreshold;
}
catch (UnityException e)
{
Debug.LogError("Using clickAlphaThreshold lower than 1 on Image whose sprite texture cannot be read. " + e.Message + " Also make sure to disable sprite packing for this sprite.", this);
return true;
}
}
/// <summary>
/// Return image adjusted position
/// **Copied from Unity's Image component for now and simplified for UI Extensions primitives
/// </summary>
/// <param name="local"></param>
/// <param name="rect"></param>
/// <returns></returns>
private Vector2 MapCoordinate(Vector2 local, Rect rect)
{
Rect spriteRect = sprite.rect;
//if (type == Type.Simple || type == Type.Filled)
return new Vector2(local.x * rect.width, local.y * rect.height);
//Vector4 border = sprite.border;
//Vector4 adjustedBorder = GetAdjustedBorders(border / pixelsPerUnit, rect);
//for (int i = 0; i < 2; i++)
//{
// if (local[i] <= adjustedBorder[i])
// continue;
// if (rect.size[i] - local[i] <= adjustedBorder[i + 2])
// {
// local[i] -= (rect.size[i] - spriteRect.size[i]);
// continue;
// }
// if (type == Type.Sliced)
// {
// float lerp = Mathf.InverseLerp(adjustedBorder[i], rect.size[i] - adjustedBorder[i + 2], local[i]);
// local[i] = Mathf.Lerp(border[i], spriteRect.size[i] - border[i + 2], lerp);
// continue;
// }
// else
// {
// local[i] -= adjustedBorder[i];
// local[i] = Mathf.Repeat(local[i], spriteRect.size[i] - border[i] - border[i + 2]);
// local[i] += border[i];
// continue;
// }
//}
//return local;
}
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 artefact's 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;
}
#endregion
#region onEnable
protected override void OnEnable()
{
base.OnEnable();
SetAllDirty();
}
#endregion
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 65af11d29c6cb8c45b3d78d97d98440d
timeCreated: 1464131942
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,141 @@
/// Credit Soprachev Andrei
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
namespace UnityEngine.UI.Extensions
{
[AddComponentMenu("UI/Extensions/Primitives/Squircle")]
public class UISquircle : UIPrimitiveBase
{
const float C = 1.0f;
public enum Type
{
Classic,
Scaled
}
[Space]
public Type squircleType = Type.Scaled;
[Range(1, 40)]
public float n = 4;
[Min(0.1f)]
public float delta = 5f;
public float quality = 0.1f;
[Min(0)]
public float radius = 1000;
private float a, b;
private List<Vector2> vert = new List<Vector2>();
private float SquircleFunc(float t, bool xByY)
{
if (xByY)
return (float)System.Math.Pow(C - System.Math.Pow(t / a, n), 1f / n) * b;
return (float)System.Math.Pow(C - System.Math.Pow(t / b, n), 1f / n) * a;
}
protected override void OnPopulateMesh(VertexHelper vh)
{
float dx = 0;
float dy = 0;
float width = rectTransform.rect.width / 2;
float height = rectTransform.rect.height / 2;
if (squircleType == Type.Classic)
{
a = width;
b = height;
}
else
{
a = Mathf.Min(width, height, radius);
b = a;
dx = width - a;
dy = height - a;
}
float x = 0;
float y = 1;
vert.Clear();
vert.Add(new Vector2(0, height));
while (x < y)
{
y = SquircleFunc(x, true);
vert.Add(new Vector2(dx + x, dy + y));
x += delta;
}
if (float.IsNaN(vert.Last().y))
{
vert.RemoveAt(vert.Count - 1);
}
while (y > 0)
{
x = SquircleFunc(y, false);
vert.Add(new Vector2(dx + x, dy + y));
y -= delta;
}
vert.Add(new Vector2(width, 0));
for (int i = 1; i < vert.Count - 1; i++)
{
if (vert[i].x < vert[i].y)
{
if (vert[i - 1].y - vert[i].y < quality)
{
vert.RemoveAt(i);
i -= 1;
}
}
else
{
if (vert[i].x - vert[i - 1].x < quality)
{
vert.RemoveAt(i);
i -= 1;
}
}
}
vert.AddRange(vert.AsEnumerable().Reverse().Select(t => new Vector2(t.x, -t.y)));
vert.AddRange(vert.AsEnumerable().Reverse().Select(t => new Vector2(-t.x, t.y)));
vh.Clear();
for (int i = 0; i < vert.Count - 1; i++)
{
vh.AddVert(vert[i], color, Vector2.zero);
vh.AddVert(vert[i + 1], color, Vector2.zero);
vh.AddVert(Vector2.zero, color, Vector2.zero);
vh.AddTriangle(i * 3, i * 3 + 1, i * 3 + 2);
}
}
#if UNITY_EDITOR
[CustomEditor(typeof(UISquircle))]
public class UISquircleEditor : Editor
{
public override void OnInspectorGUI()
{
DrawDefaultInspector();
UISquircle script = (UISquircle)target;
GUILayout.Label("Vertex count: " + script.vert.Count().ToString());
}
}
#endif
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d7948ca2f24604c4aa5340622512a976
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: