I been trying for a while to draw smooth lines in Unity but with Line Renderer I obtained only jagged lines with the corners not rounded, in particular when the angle of curvatu
I finally got this working thanks to @Iggy's excellent answer.
Create a new Image on your canvas, delete the Image script and replace it with UICubicBezier.
To give:
using UnityEngine;
using UnityEngine.UI;
[ExecuteInEditMode]
public class UiCubicBezier : MaskableGraphic
{
public float thickness = 2;
public int anchors = 20;
protected override void OnPopulateMesh(VertexHelper vh)
{
// draws a cubic bezier curve from the lower left hand corner (start)
// to the upper right hand corner (end).
vh.Clear();
var rt = this.rectTransform;
var rect = rt.rect;
var start = new Vector2(-rect.width / 2, -rect.height / 2);
var cp1 = new Vector2(-rect.width / 6, -rect.height / 2);
var cp2 = new Vector2(rect.width / 6, rect.height / 2);
var end = new Vector2(rect.width / 2, rect.height / 2);
var data = new BezierData(start, cp1, cp2, end);
// all you need to know is that data.GetPoint generates a sequence of points
// between the start and end points.
var points = new Vector2[this.anchors];
for (var anchor = 0; anchor < points.Length; anchor++)
{
var t = (float)anchor / this.anchors;
points[anchor] = data.GetPoint(t);
}
// because the normals are at the mid-points between vertexes the start and end
// points don't touch the bounding box. to fix this some vertexes are added to
// the start and end that touch the bounding box.
this.DrawStartVertexes(vh, start);
for (var anchor = 0; anchor < points.Length - 1; anchor++)
{
this.DrawVertexes(vh, points[anchor], points[anchor + 1]);
}
this.DrawEndVertexes(vh, end);
for (var v = 0; v + 2 < vh.currentVertCount; v += 2)
{
vh.AddTriangle(v, v + 1, v + 2);
}
for (var v = 0; v + 3 < vh.currentVertCount; v += 2)
{
vh.AddTriangle(v + 1, v + 2, v + 3);
}
}
private void DrawStartVertexes(VertexHelper vh, Vector2 start)
{
// d = thickness * \sqrt{2}, so the distance between the vertexes
// is equal to the thickness (https://en.wikipedia.org/wiki/Triangle#Right_triangles)
var d = this.thickness * 0.70710678118f;
var vertex = UIVertex.simpleVert;
vertex.color = this.color;
vertex.position = new Vector2(start.x, start.y + d);
vh.AddVert(vertex);
vertex.position = new Vector2(start.x + d, start.y);
vh.AddVert(vertex);
}
private void DrawEndVertexes(VertexHelper vh, Vector2 end)
{
// d = thickness * \sqrt{2}, so the distance between the vertexes
// is equal to the thickness (https://en.wikipedia.org/wiki/Triangle#Right_triangles)
var d = this.thickness * 0.70710678118f;
var vertex = UIVertex.simpleVert;
vertex.color = this.color;
vertex.position = new Vector2(end.x - d, end.y);
vh.AddVert(vertex);
vertex.position = new Vector2(end.x, end.y - d);
vh.AddVert(vertex);
}
private void DrawVertexes(VertexHelper vh, Vector2 start, Vector2 end)
{
var v = end - start;
var mid = start + v / 2; // the mid-point between start and end.
var perp = Vector2.Perpendicular(v.normalized); // vector of length 1 perpendicular to v.
var vertex = UIVertex.simpleVert;
vertex.color = this.color;
// move half the thickness away from the mid-point.
vertex.position = mid + (perp * this.thickness / 2);
vh.AddVert(vertex);
// move half the thickness away from the mid-point in the opposite direction.
vertex.position = mid - (perp * this.thickness / 2);
vh.AddVert(vertex);
}
private struct BezierData
{
private readonly Vector2 start;
private readonly float cx;
private readonly float bx;
private readonly float ax;
private readonly float cy;
private readonly float by;
private readonly float ay;
public BezierData(Vector2 start, Vector2 cp1, Vector2 cp2, Vector2 end)
{
// cribbed from here: https://www.codeproject.com/articles/25237/bezier-curves-made-simple
this.start = start;
this.cx = 3 * (cp1.x - start.x);
this.bx = 3 * (cp2.x - cp1.x) - this.cx;
this.ax = end.x - start.x - this.cx - this.bx;
this.cy = 3 * (cp1.y - start.y);
this.by = 3 * (cp2.y - cp1.y) - this.cy;
this.ay = end.y - start.y - this.cy - this.by;
}
public Vector2 GetPoint(float t)
{
var tSquared = t * t;
var tCubed = tSquared * t;
return new Vector2(
(this.ax * tCubed) + (this.bx * tSquared) + (this.cx * t) + this.start.x,
(this.ay * tCubed) + (this.by * tSquared) + (this.cy * t) + this.start.y);
}
}
}