unity 网格面片生成抛物线,折线
实现效果
一、 网格面片的建立方法
编写一个通用的网格面片线生成方法
线宽的处理
网格面片线建立相对于,管道线建立《unity 线绳管道纯代码创建方法》更为简单,建立方法也类似,可以把面片线理解成,任意个两点线段前后连接组成的多边形,线段的长度就是面片线的线宽。随之会发现,当六个以上点建立线,两端线和中间线的向量不一样时,会出现线宽不一致的情况,如下图。
线段a、b、c的长度是一样的组成的面片线的线宽不一致。
为了解决这个问题,我们需要动态改变所以中间线段的长度。
如上图所示,需要动态改变的是线段b。已知线段a的长度,∠α的度数(三点求夹角)求线段b的长度。
这里存在一个问题,如果∠β的角度足够小,AC线段会无限长,为了解决这个问题,加入了前后断点,当角度小于一个值时,不连接拐点。如下面的代码,∠β不小于30°
实现效果
轴心线的建立
/// <summary>
/// 轴心线的建立
/// </summary>
/// <param name="createPoint"></param>
/// <param name="lineWidth"></param>
/// <returns></returns>
List<LinePoint2D> SetLinePoint2D(Vector3[] createPoint, float lineWidth)
{
List<LinePoint2D> linePoint = new List<LinePoint2D>();
int length = createPoint.Length;
for (int i = 0; i < length; i++)
{
if (i == 0)
{
Add2DLinePoints(lineWidth, createPoint[i], (createPoint[i + 1] - createPoint[i]).GetPlaneVector().normalized, ref linePoint);
}
else if (i == length - 1)
{
Add2DLinePoints(lineWidth, createPoint[i], (createPoint[i] - createPoint[i - 1]).GetPlaneVector().normalized, ref linePoint);
}
else
{
Get2DElbowPoint(createPoint[i], createPoint[i - 1], createPoint[i + 1], lineWidth, ref linePoint);
}
}
return linePoint;
}
拐点的建立
/// <summary>
/// 得到拐点
/// </summary>
/// <param name="focusPoint">中间点</param>
/// <param name="frontPoint">前一点</param>
/// <param name="backPoint">后一点</param>
void Get2DElbowPoint(Vector3 focusPoint, Vector3 frontPoint, Vector3 backPoint, float lineWidth, ref List<LinePoint2D> linePoint)
{
//焦点前后向量
Vector3 frontVec = (frontPoint - focusPoint).normalized;
Vector3 backVec = (backPoint - focusPoint).normalized;
//平面点
Vector3 frontPlaneVec = (frontPoint - focusPoint).GetPlaneVector().normalized;
Vector3 backPlaneVec = (backPoint - focusPoint).GetPlaneVector().normalized;
Vector3 focusVec = (frontPlaneVec + backPlaneVec).normalized;
//得到前后断点
Vector3 tangencyFrontPoint = (lineWidth * frontVec*0.5f) + focusPoint;
Vector3 tangencyBackPoint = (lineWidth * backVec * 0.5f) + focusPoint;
//得到两个焦点向量的法线
Vector3 normalVec = Vector3.Cross(frontVec, backVec).normalized ;
Vector3 directionVec;
//前后向量相反时,法线向量为零
if (frontPlaneVec == -backPlaneVec)
{
directionVec = backPlaneVec;
}
else
{
directionVec = -Vector3.Cross(focusVec, normalVec).normalized;
}
Add2DLinePoints(lineWidth, tangencyFrontPoint, -frontPlaneVec, ref linePoint);
float angle = Vector3.Angle(frontPlaneVec, backPlaneVec) * 0.5f;
if (angle>30)
{
float newLineWidth = lineWidth / Mathf.Sin(angle * Mathf.Deg2Rad);
//增加管线点
Add2DLinePoints(newLineWidth, focusPoint, directionVec, ref linePoint);
}
Add2DLinePoints(lineWidth, tangencyBackPoint, backPlaneVec, ref linePoint);
}
创建网格点
#region 创建网格点
Vector3[] Create2DMeshPoint(List<LinePoint2D> linePoint,int type)
{
int length = linePoint.Count;
Vector3[] meshPoint = new Vector3[length * 2];
for (int i = 0; i < length; i++)
{
if (type==0)
{
meshPoint[(i * 2) + 0] = new Vector3(linePoint[i].LineWidth * 0.5f, 0, 0).FromToMoveRotation(linePoint[i].Location, linePoint[i].Direction);
meshPoint[(i * 2) + 1] = new Vector3(-linePoint[i].LineWidth * 0.5f, 0, 0).FromToMoveRotation(linePoint[i].Location, linePoint[i].Direction);
}
else if (type == 1)
{
meshPoint[(i * 2) + 0] = new Vector3( 0, 0,linePoint[i].LineWidth * 0.5f).FromToMoveRotation(linePoint[i].Location, linePoint[i].Direction);
meshPoint[(i * 2) + 1] = new Vector3(0, 0, -linePoint[i].LineWidth * 0.5f).FromToMoveRotation(linePoint[i].Location, linePoint[i].Direction);
}
else
{
meshPoint[(i * 2) + 0] = new Vector3(0, linePoint[i].LineWidth * 0.5f, 0).FromToMoveRotation(linePoint[i].Location, linePoint[i].Direction);
meshPoint[(i * 2) + 1] = new Vector3(0, -linePoint[i].LineWidth * 0.5f, 0).FromToMoveRotation(linePoint[i].Location, linePoint[i].Direction);
}
}
return meshPoint;
}
#endregion
网格创建
#region 网格创建
/// <summary>
/// 网格创建
/// </summary>
/// <param name="linePoints">线的轴心线组</param>
/// <param name="meshPoint">网格点</param>
/// <param name="count">段数</param>
/// <param name="uvX">uv宽度</param>
/// <returns></returns>
Mesh CreatMesh(List<LinePoint2D> linePoints, Vector3[] meshPoint, int count, int uvX)
{
Mesh mesh = new Mesh();
mesh.vertices = meshPoint;
mesh.triangles = GetTriangles(linePoints.Count, count);
mesh.uv = GetUV(linePoints, count, uvX);
mesh.RecalculateNormals();
mesh.RecalculateBounds();
return mesh;
}
Vector2[] GetUV(List<LinePoint2D> linePoints,int count,int uvX)
{
int length = linePoints.Count;
Vector2[] uvs = new Vector2[(count * length)];
float lineDis = 0;
int k = 0;
for (int i = 0; i < length; i ++)
{
if (i != 0)
{
lineDis += Vector3.Distance(linePoints[i].Location, linePoints[i - 1].Location);
}
for (int j = 0; j < count; j++)
{
Vector2 vector2;
if (j % 2 != 0)
{
vector2 = new Vector2(uvX, lineDis);
}
else
{
vector2 = new Vector2(0, lineDis);
}
uvs[k] = vector2;
k += 1;
}
}
return uvs;
}
/// <param name="length">线段段数</param>
/// <param name="count">横截面段数(也就是圆的段数)</param>
/// <returns></returns>
int[] GetTriangles(int length, int count)
{
int[] triangles = new int[(count * (length - 1)) * 6];
int k = 0;
for (int i = 0; i < length - 1; i++)
{
for (int j = 0; j < count; j++)
{
if (j == count - 1)
{
triangles[k] = (i * count) + j;
triangles[k + 1] = (i * count) + 0;
triangles[k + 2] = ((i + 1) * count) + 0;
triangles[k + 3] = (i * count) + j;
triangles[k + 4] = ((i + 1) * count) + 0;
triangles[k + 5] = ((i + 1) * count) + j;
}
else
{
triangles[k] = (i * count) + j;
triangles[k + 1] = (i * count) + j + 1;
triangles[k + 2] = ((i + 1) * count) + j + 1;
triangles[k + 3] = (i * count) + j;
triangles[k + 4] = ((i + 1) * count) + j + 1;
triangles[k + 5] = ((i + 1) * count) + j;
}
k += 6;
}
}
return triangles;
}
#endregion
与管道线生成的异同点
《unity 线绳管道纯代码创建方法》
相同点
与创建管道线方法一致,也是通过得到中心路径的位置和放心,将线段矩阵变换到相应位置,构建网格点数组,从而建立网格。
不同点
管道线的拐点是内切圆分段,片面线的拐点是调整线段长度,保持线宽一致。为了保持uv的一致,也加入了类似管道线内切圆分段的方法,只是只加入前后两个点。
补充的扩展方法
/// <summary>
/// 角度旋转
/// </summary>
/// <param name="vector3"></param>
/// <param name="angle">旋转角度</param>
/// <param name="center">旋转中心点</param>
/// <param name="direction">旋转轴</param>
/// <returns></returns>
public static Vector3 ToAngle(this Vector3 vector3, float angle, Vector3 center, Vector3 direction)
{
Vector3 pos = center;
Quaternion quaternion = Quaternion.AngleAxis(angle, direction);
Matrix4x4 matrix = new Matrix4x4();
matrix.SetTRS(pos, quaternion, Vector3.one);
vector3 = matrix.MultiplyPoint3x4(vector3);
return vector3;
}
/// <summary>
/// 矩阵变换
/// </summary>
/// <param name="vector3"></param>
/// <param name="location">位置</param>
/// <param name="direction">旋转向量</param>
/// <returns></returns>
public static Vector3 FromToMoveRotation(this Vector3 vector3, Vector3 location, Vector3 direction)
{
return vector3.FromToMoveRotation(location, direction,Vector3.one);
}
public static Vector3 FromToMoveRotation(this Vector3 vector3, Vector3 location, Vector3 direction,Vector3 scale)
{
Matrix4x4 matrix = Matrix4x4.identity;
Quaternion quaternion = Quaternion.LookRotation(direction, direction);
matrix.SetTRS(location, quaternion, scale);
vector3 = matrix.MultiplyPoint3x4(vector3);
return vector3;
}
/// <summary>
/// 得到平面投影向量
/// </summary>
/// <param name="point"></param>
/// <returns></returns>
public static Vector3 GetPlaneVector(this Vector3 vector3)
{
return new Vector3(vector3.x, 0, vector3.z);
}
二、 抛物线创建
抛物线公式
从公式上看,只知道起点和终点是不能确定一条抛物线的,所以我们要把定义成变量,,为焦准距。
把起点设为原点,,带入终点向量坐标,求出。
Vector3 originPlanePoint = GetPlaneVector(originPoint);
Vector3 endPlanePoint = GetPlaneVector(endPoint);
float xzMagnitude = Vector3.Distance(originPlanePoint, endPlanePoint);
float a;
if (p == 0)
{
a = -2 / xzMagnitude;
}
else
{
a = -1 / (2 * p);
}
float yMagnitude = (endPoint - originPoint).y;
float b = (yMagnitude - (xzMagnitude * xzMagnitude * a)) / xzMagnitude;