Как анимировать формы рендеринга линии, не оставляя пробела
Я использую приведенный ниже код для создания фигур с помощью средства визуализации линии на основе количества точек. Для точек больше 3 (форма треугольника и т.д.) Первая и последняя точки не закрывают форму так, как это делают другие точки.
1. Как закрыть фигуры с более чем тремя точками без видимых пробелов?
2. Как я могу анимировать фигуру, чтобы рисовать линии в течение определенной продолжительности (возможно, используя сопрограмму)?
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);
}
}
Ответы
Ответ 1
Если вы убедитесь, что последняя точка вашего LineRenderer совпадает с первой точкой, она всегда должна закрывать любую данную фигуру. Запустите цикл for
следующим образом for (int i = 0; i < Points - 1; i++)
(поэтому каждая точка, но последняя, также <
, а не <=
). Затем закройте форму с помощью lineRenderer.SetPosition(Point - 1, lineRenderer.GetPosition(0));
, когда цикл for сделан.
Обратите внимание, что массивы начинаются с 0, поэтому Point - 1
- это последняя точка вашего lineRenderer.
Для анимации я не знаю простого способа сделать это. То, что я сделал бы, это использовать сопрограмму для перемещения каждой точки в сторону конечного пункта назначения с течением времени. Например, вы начинаете с добавления первой точки, и вы добавляете вторую точку поверх первой точки. Затем вы перемещаете (со временем в сопрограмме) вторую точку к ее окончательной позиции (используйте SetPosition
для ее перемещения). Когда он достигнет конечной позиции, добавьте третью точку поверх нее и переместите ее в конечную позицию. Повторите для каждой точки.
Ответ 2
Первым (возможно, очевидным) решением было бы установить опцию loop на LineRenderer. Однако во многих случаях это не дает визуально приятных результатов.
Я бы исправил проблему просто так, что дает приятные визуальные результаты:
lineRenderer.positionCount = Points + 2; //+2 to close the shape and create one more piece to extend over the gap.
и
for (int i = 0; i <= Points + 1; i++) // one more piece
Нарисуйте полную форму, затем просто создайте еще одну фигуру, которая возвращается назад. Это может не выглядеть элегантно, но это так просто и выглядит так, как я думаю, он должен выглядеть.
Что касается анимации, попробуйте что-то вроде этого:
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 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;
}
}
}
В основном у вас уже есть все на своем месте, анимируйте его, оживляя значения, которые вы уже используете. Конечно, существует множество способов достижения разных эффектов, но большинство из них будет применять анимированное значение прогресса к другому параметру вашего чертежа. Вы можете использовать сопрограмму или только обычный цикл Update(), как показано в примере. Надеюсь, это поможет;)
Ответ 3
Итак, идея состоит в том, чтобы увеличить рендеринг линии, соединяющий вершину с вершиной.
Решение ниже для куба, но я добавил дополнительную вершину, и все идет хорошо.
Единственное, что я не мог проверить, это настройка цикла, поскольку я на 5.5... Я предполагаю, что это работает...
Кроме того, я не передал никаких материалов, я думаю, у вас это есть.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Test : MonoBehaviour
{
[SerializeField]
private float step = 0.5f; // How fast it draws
private List<Vector3> vertices = null;
private LineRenderer line = null;
private void Start()
{
// This creates a square
this.vertices = new List<Vector3>()
{
new Vector3(-1,-1,0), new Vector3(1,-1,0), new Vector3(1,1,0),new Vector3(-1, 1, 0)
};
StartCoroutine(Draw());
}
private IEnumerator Draw()
{
int index = 1;
int vertexPos = 1;
// Set the LineRenderer basics with 2 vertices
this.line = this.gameObject.AddComponent<LineRenderer>();
this.line.startWidth = this.line.endWidth= 0.1f;
this.line.numPositions = 2;
// Set both point at same starting position
this.line.SetPosition(0,this.vertices[0]);
this.line.SetPosition(1, this.vertices[0]);
Vector3 temp = this.vertices[0]; // Get the current vertex position
Vector3 target = this.vertices[index]; // Get the target vertex position
while (true)
{
// Move the current pos to the target pos
temp = Vector3.MoveTowards(temp, target, Time.deltaTime * this.step);
this.line.SetPosition(vertexPos, temp);
// Is the target reached
if (temp == target)
{
// This is for final run when closing the shape
// It means we reach target and index is 0 so end of shape
// Break the coroutine
if(index == 0)
{
this.line.loop = true; // Could not test this one on 5.5
yield break;
}
// Increase the LineRenderer size by 1
vertexPos++;
this.line.numPositions++;
// Place the new vertex at the position of the previous
this.line.SetPosition(vertexPos, temp);
// Increase the index and check if we reached the end of the vertex list
// If end of vertex list, then we use the first one to close
if (++index == this.vertices.Count)
{
index = 0;
}
// Set new target
target = this.vertices[index];
}
yield return null;
}
}
}