这里介绍一个通过路径点,动态自定义生成Mesh,来做到画线效果的一种实现方式:
首先通过点来定义线的形状。然后用AnimationCurve来平滑这个曲线,然后根据精确度和一些其他的设置来生成Mesh,最后通过设置材质球来给Mesh挂上皮肤。
代码如下:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
namespace LDFW.Math
{
public class LDFWLineRenderer : MonoBehaviour
{
// Public variables
public Transform[] pathPoints;
public float lineWidth = 1f;
public int lineSegmentCount = 10;
public AnimationCurve xAnimationCurve;
public AnimationCurve yAnimationCurve;
public AnimationCurve zAnimationCurve;
public Transform referenceCamera;
// Private variables
private MeshFilter meshFilter;
private MeshRenderer meshRenderer;
private Mesh mesh;
private List vertices = new List ();
private List uvs = new List ();
private List triangles = new List ();
void Awake()
{
meshFilter = GetComponent();
meshRenderer = GetComponent();
if (meshFilter == null)
{
meshFilter = gameObject.AddComponent();
}
if (meshRenderer == null)
{
meshRenderer = gameObject.AddComponent();
}
mesh = new Mesh();
meshFilter.mesh = mesh;
}
// Constant Z
[ContextMenu("InitWithRespectToXYPlane")]
public void InitWithRespectToXYPlane()
{
transform.position = Vector3.zero;
if (pathPoints == null)
{
Debug.LogError("path is null!");
return;
}
if (pathPoints.Length < 2)
{
Debug.LogError("path needs at least two points");
return;
}
// Get the camera position;
Vector3 cameraPosition = Vector3.zero;
if (referenceCamera == null)
{
cameraPosition = Camera.main.transform.position;
}
else
{
cameraPosition = referenceCamera.position;
}
// Calculate total segmented straight line lenth
float totalLength = 0;
for (int i = 1; i < pathPoints.Length; i++)
{
totalLength += (pathPoints[i].position - pathPoints[i - 1].position).magnitude;
}
//Debug.Log ("totalLength = " + totalLength);
// Construct the X,Y,Z animation curves
xAnimationCurve = new AnimationCurve();
yAnimationCurve = new AnimationCurve();
zAnimationCurve = new AnimationCurve();
Vector3 currentPosition = Vector3.zero;
float lineSegmentLength = 0f;
for (int i = 0; i < pathPoints.Length; i++)
{
currentPosition = pathPoints[i].position;
if (i == 0)
{
xAnimationCurve.AddKey(0, currentPosition.x);
yAnimationCurve.AddKey(0, currentPosition.y);
zAnimationCurve.AddKey(0, currentPosition.z);
}
else
{
lineSegmentLength += (currentPosition - pathPoints[i - 1].position).magnitude;
xAnimationCurve.AddKey(lineSegmentLength / totalLength, currentPosition.x);
yAnimationCurve.AddKey(lineSegmentLength / totalLength, currentPosition.y);
zAnimationCurve.AddKey(lineSegmentLength / totalLength, currentPosition.z);
}
}
// Construct vertices and UVs
vertices = new List();
uvs = new List();
triangles = new List();
float startSegmentTime;
float endSegmentTime;
float centerSegmentTime;
Vector3 perpendicualrVectorLHS;
Vector3 perpendicularVectorRHS;
Vector3 perpendicularRelativePositionLHS;
Vector3 perpendicularRelativePositionRHS;
Vector3 currentLineSegmentStartPosition;
Vector3 currentLineSegmentEndPosition;
Vector3 currentLineSegmentCenterPosition;
Vector3 cameraOffsetVector = Vector3.zero;
if ((cameraPosition - transform.position).z > 0)
{
cameraOffsetVector.z = 1f;
}
else
{
cameraOffsetVector.z = -1f;
}
for (int i = 0; i <= lineSegmentCount; i++)
{
if (i == 0)
{
startSegmentTime = (float)i / lineSegmentCount;
endSegmentTime = (float)(i + 1) / lineSegmentCount;
}
else if (i == lineSegmentCount)
{
startSegmentTime = (float)(i - 1) / lineSegmentCount;
endSegmentTime = (float)i / lineSegmentCount;
}
else
{
startSegmentTime = (float)(i - 1) / lineSegmentCount;
endSegmentTime = (float)(i + 1) / lineSegmentCount;
}
centerSegmentTime = (float)i / lineSegmentCount;
currentLineSegmentStartPosition = new Vector3(xAnimationCurve.Evaluate(startSegmentTime), yAnimationCurve.Evaluate(startSegmentTime), zAnimationCurve.Evaluate(startSegmentTime));
currentLineSegmentEndPosition = new Vector3(xAnimationCurve.Evaluate(endSegmentTime), yAnimationCurve.Evaluate(endSegmentTime), zAnimationCurve.Evaluate(endSegmentTime));
currentLineSegmentCenterPosition = new Vector3(xAnimationCurve.Evaluate(centerSegmentTime), yAnimationCurve.Evaluate(centerSegmentTime), zAnimationCurve.Evaluate(centerSegmentTime));
perpendicualrVectorLHS = Vector3.Cross(currentLineSegmentEndPosition - currentLineSegmentStartPosition, cameraOffsetVector);
perpendicularVectorRHS = Vector3.Cross(cameraOffsetVector, currentLineSegmentEndPosition - currentLineSegmentStartPosition);
perpendicularRelativePositionLHS = currentLineSegmentCenterPosition + perpendicualrVectorLHS.normalized * lineWidth / 2f;
perpendicularRelativePositionRHS = currentLineSegmentCenterPosition + perpendicularVectorRHS.normalized * lineWidth / 2f;
vertices.Add(perpendicularRelativePositionLHS);
vertices.Add(perpendicularRelativePositionRHS);
// calculate UV
if (i == 0)
{
uvs.Add(new Vector2(0, 0));
uvs.Add(new Vector2(0, 1));
}
else if (i == lineSegmentCount)
{
uvs.Add(new Vector2(1, 0));
uvs.Add(new Vector2(1, 1));
}
else
{
uvs.Add(new Vector2((float)i / lineSegmentCount, 0));
uvs.Add(new Vector2((float)i / lineSegmentCount, 1));
}
// calculate triangle
if (i < lineSegmentCount)
{
triangles.Add(i * 2);
triangles.Add(i * 2 + 2);
triangles.Add(i * 2 + 1);
triangles.Add(i * 2 + 1);
triangles.Add(i * 2 + 2);
triangles.Add(i * 2 + 3);
}
}
mesh.vertices = vertices.ToArray();
mesh.uv = uvs.ToArray();
}
public void DisplayLine(float percentage)
{
int arrayLength = (int)(percentage * triangles.Count);
while (arrayLength % 3 != 0)
{
arrayLength++;
}
arrayLength = Mathf.Min(arrayLength, triangles.Count);
List newTriangles = new List();
for (int i = 0; i < arrayLength; i++)
{
newTriangles.Add(triangles[i]);
}
mesh.triangles = newTriangles.ToArray();
}
[ContextMenu("PlayTest")]
public void Test()
{
StartCoroutine(TestCoroutine());
}
private IEnumerator TestCoroutine()
{
for (int i = 0; i < 200; i++)
{
DisplayLine(i * 0.01f);
yield return null;
}
}
}
}