Reputation: 595
I am using the code below to create shapes with a line renderer based on the number of points. For points greater than 3 (triangle shape and so on) the first and last points don't close the shape in the the way that the other points do.
1.How can close shapes with more than 3 points without any visible gaps?
2.How can I animate the shape so it draws the lines over a specific duration (possibly using a coroutine)?
public class CircleDrawing : MonoBehaviour
{
[Tooltip("The radius of the circle, measured in world units.")]
public float Radius = 2;
[Tooltip("The number of vertices in the circle.")]
public int Points = 5;
[Tooltip("The color of the circle.")]
public Color Color;
private LineRenderer lineRenderer;
public void Awake()
{
lineRenderer = gameObject.AddComponent<LineRenderer>();
lineRenderer.material = new Material(Shader.Find("Sprites/Default"));
lineRenderer.material.color = Color;
lineRenderer.startWidth = lineRenderer.endWidth = 0.5f;
lineRenderer.positionCount = Points + 1; //+1 to close the shape
Draw();
}
private void Draw()
{
float angle = 0f;
for (int i = 0; i <= Points; i++)
{
float x = Radius * Mathf.Cos(angle) + transform.position.x;
float y = Radius * Mathf.Sin(angle) + transform.position.y;
lineRenderer.SetPosition(i, new Vector3(x, y, 0.01f)); //Z is slightly behind the paddle so it draws in front
angle += (2f * Mathf.PI) / Points;
}
}
private void OnDestroy()
{
Destroy(lineRenderer.material);
}
}
Upvotes: 17
Views: 1811
Reputation: 1461
The first (maybe obvious) solution would be to set the loop option on the LineRenderer. However, this doesn't seem to give visually pleasing results in many cases.
I would fix the problem simply like this, which gives nice visual results:
lineRenderer.positionCount = Points + 2; //+2 to close the shape and create one more piece to extend over the gap.
and:
for (int i = 0; i <= Points + 1; i++) // one more piece
Draw the complete shape, then just create one more piece, which loops back over the start. This might not feel elegant, but it's so simple and looks exactly how I imagine it should look.
As for the animation, try something like this:
using UnityEngine;
using System.Collections;
public class CircleDrawing : MonoBehaviour
{
public float Radius = 2f;
public int Points = 5;
public Color Color = Color.red;
public float DrawSpeed = 1f;
private LineRenderer lineRenderer;
private float progress; // [0..1] animated value.
public void Awake()
{
lineRenderer = gameObject.AddComponent<LineRenderer>();
lineRenderer.material = new Material(Shader.Find("Sprites/Default"));
lineRenderer.material.color = Color;
lineRenderer.startWidth = lineRenderer.endWidth = 0.5f;
lineRenderer.positionCount = Points + 2; //+2 to close the shape and create one more piece to extend over the gap.
progress = 0;
StartCoroutine(Draw());
}
private void Update_Disabled() // Regular Update() loop
{
float angle = 0f;
for (int i = 0; i <= Points + 1; i++) // one more piece
{
float x = Radius * Mathf.Cos(angle) + transform.position.x;
float y = Radius * Mathf.Sin(angle) + transform.position.y;
lineRenderer.SetPosition(i, new Vector3(x, y, 0.01f));
angle += (2f * Mathf.PI) / Points;
angle *= progress;
}
progress += DrawSpeed * Time.deltaTime;
progress = Mathf.Clamp01(progress);
}
private IEnumerator Draw()
{
yield return new WaitForSeconds(1f); // Debug
bool done = false;
while(!done)
{
if(progress >= 1)
done = true; // The done check can be handled better, but it's late.
float angle = 0f;
for (int i = 0; i <= Points + 1; i++) // One additional piece to loop over start.
{
float x = Radius * Mathf.Cos(angle) + transform.position.x;
float y = Radius * Mathf.Sin(angle) + transform.position.y;
lineRenderer.SetPosition(i, new Vector3(x, y, 0.01f));
angle += (2f * Mathf.PI) / Points;
angle *= progress; // Animate progress turning.
}
progress += DrawSpeed * Time.deltaTime;
progress = Mathf.Clamp01(progress);
yield return null;
}
}
}
You basically already have everything in place, now animate it by animating the values you are already using. Of course, there are many ways to achieve different effects, but most of them will be applying the animated progress value to a different parameter of your drawing. You can use a coroutine or just the regular Update() loop as shown in the example. I hope this helps ;)
Upvotes: 0
Reputation: 838
If you make sure that the last point of your LineRenderer is the same as the first point it should always close any given shape. Run the for
loop like this for (int i = 0; i < Points - 1; i++)
(so each point but the last one, also <
and not <=
). Then close the shape with lineRenderer.SetPosition(Point - 1, lineRenderer.GetPosition(0));
when the for loop is done.
Note here that Arrays start at 0, that's why Point - 1
is the last point of your lineRenderer.
For the animation I don't know an easy way to do it. What I would do is use a coroutine to move each point toward is final destination over time. For example you start by adding the first point and you add the second point on top of the first point. Then you move (over time in a coroutine) the second point toward it's final position (use SetPosition
to move it). When it has reached it's final position add the third point on top of it, and move it to it's final position. Repeat for every points.
Upvotes: 1