I was looking for a way to create a roll out effect just like this in a 2D Unity project https://www.desmos.com/calculator/mrfrugwvm5
The problem is I'm not that goot at math, so I'm not sure how to translate the function to say a line renderer's points.
Can someone help me with that?
Thanks!
So to break this down a bit there are actually two functions involved.
The first
y = -1 { 0 <= X <= p }
is quite trivial: In the range 0
to P
paint a straight line with fixed y = -1
.
The other one is returning x
and y
coordinates and basically translates to
var x = (1 - t / totalLength) * Mathf.Sin(t) + p;
var y = -(1 - t / totalLength) * Mathf.Cos(t);
There are also some ranges involved such as
{0 <= p <= 14}
=> p
lies within 0
and 14
You will see the number 14
at various places => This is the maximum total length of the line when it is fully unrolled
{0 <= t <= 30}
=> Not sure why they used a 30
here - the result is actually the same as with 14
, again the total length
{t < 14 - p}
=> This limits t
further down so the actual range is {0 <= t <= 14 - p}
The p
basically determines at which point the swirl starts and how big the range for t
is.
So as far as I can tell in a LineRenderer
this somewhat boils down to e.g.
public static class LineRendererExtensions
{
public static void DrawRolledLine(this LineRenderer line, float totalLength, float p, int maxPoints = 51, float swirlRadius = 1f)
{
// ensure p is between 0 and 1
p = Mathf.Clamp01(p);
// As a performance boost do not use more points than necessary
// The higher p the less swirl => requires less points to draw
var resolution = Mathf.CeilToInt(Mathf.Lerp(0, maxPoints, 1 - p));
// ensure we have at least 2 points to draw a line
resolution = Mathf.Max(2, resolution);
// map p to the total length
p *= totalLength;
// calculate the range for the t value
var maxT = (totalLength - p) / swirlRadius;
// prepare the points array
var points = new Vector3[resolution];
// begin of straight line - for convenience I move the whole thing up by 1
points[0] = new Vector3(0, 0, 0);
// calculate the points of the swirl
for (var i = 1; i < resolution; i++)
{
// spread the points equally over the range of t
var t = (i - 1f) / (resolution - 1f) * maxT;
// use given functions to calculate x and y positions
var x = (1 - t / totalLength) * Mathf.Sin(t) * swirlRadius + p;
var y = (-(1 - t / totalLength) * Mathf.Cos(t) + 1) * swirlRadius; // moving up by 1 * radius
points[i] = new Vector3(x, y);
}
// finally apply the calculated points to the line renderer
line.positionCount = resolution;
line.SetPositions(points);
}
}
As you can see as parameters I added a bit more flexibility
totalLength
: The maximum length of the line when unrolled
p
: Factor in the range {0, 1}
=> This will be mapped into {0, totalLength}
maxPoints
: Up to how many points to use to draw the result
If there is lesser swirl this resolution is reduced for better performance
swirlRadius
: In case you want the swirl part to have a different radius than the default of 1
I also modified the functions slightly so that the line is always at 0
instead.
As a little demo use e.g. this component
[ExecuteAlways] // <- also run in edit mode
public class Example : MonoBehaviour
{
[SerializeField]
private LineRenderer m_LineRenderer;
[SerializeField]
[Range(0f, 1f)]
private float m_P = 0.5f;
[SerializeField]
[Min(0)]
private int m_Resolution = 51;
[SerializeField]
private float m_TotalLength = 14.0f;
[SerializeField]
[Min(Mathf.Epsilon)]
private float m_Radius = 1.0f;
private void Update()
{
if (!m_LineRenderer)
{
m_LineRenderer = GetComponent<LineRenderer>();
if (!m_LineRenderer)
{
m_LineRenderer = gameObject.AddComponent<LineRenderer>();
m_LineRenderer.startWidth = 0.1f;
m_LineRenderer.endWidth = 0.1f;
}
}
m_LineRenderer.DrawRolledLine(m_TotalLength, m_P, m_Resolution, m_Radius);
}
}