[Unity]運(yùn)行時(shí)創(chuàng)建線(貝塞爾的運(yùn)用)
1. 實(shí)現(xiàn)的目標(biāo)
在運(yùn)行狀態(tài)下創(chuàng)建一條可以使用貝塞爾方法實(shí)時(shí)編輯的網(wǎng)格曲線。
2. 原理介紹
2.1 曲線的創(chuàng)建
unity建立網(wǎng)格曲線可以參考Unity程序化網(wǎng)格體的實(shí)現(xiàn)方法。主要分為頂點(diǎn),三角面,UV和法線。筆者有類似的文章unity 線繩管道純代碼創(chuàng)建方法_,詳細(xì)的講解了網(wǎng)格線的創(chuàng)建方法,這次的不同點(diǎn)在于法線的確立方法上。
2.2貝塞爾曲線點(diǎn)的確立
筆者有文章Unity 貝塞爾曲線的創(chuàng)建_描述了貝塞爾的創(chuàng)建方法。
3. 實(shí)現(xiàn)過程
3.1曲線的創(chuàng)建方法
線的組成原理
曲線由橫截面圓和中心軸線組成。橫截面的法線方向?yàn)榍昂髢牲c(diǎn)向量差,如下圖綠色線為中心軸線,A點(diǎn)為橫截面所在點(diǎn),橫截面的法線方法為向量
A
D
?
\vec{AD}
AD既向量
C
B
?
\vec{CB}
CB的單位向量;終點(diǎn)和起點(diǎn)的法線方向?yàn)樽陨砗颓包c(diǎn)或者后一點(diǎn)的向量。
代碼源碼
3.1.1 橫截圓的創(chuàng)建
#region 橫切圓創(chuàng)建
/// <summary>
/// 得到管線橫切圓
/// </summary>
/// <param name="Count">段數(shù)</param>
/// <param name="R">半徑</param>
/// <returns></returns>
Vector3[] CircularSection(int Count, float R)
{
Vector3[] vector3s = new Vector3[Count];
float angle = 360 / Count;
Vector3 vector3 = new Vector3(R, 0, 0);
for (int i = 0; i < Count; i++)
{
//根據(jù)角度得到圓的分布點(diǎn)
vector3s[i] = vector3.ToAngle(angle * i, Vector3.zero, Vector3.forward);
}
return vector3s;
}
#endregion
? vector3旋轉(zhuǎn)擴(kuò)展方法
/// <summary>
/// 角度旋轉(zhuǎn)
/// </summary>
/// <param name="vector3"></param>
/// <param name="angle">旋轉(zhuǎn)角度</param>
/// <param name="center">旋轉(zhuǎn)中心點(diǎn)</param>
/// <param name="direction">旋轉(zhuǎn)軸</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;
}
3.1.2 中心線的確立
class LinePoint
{
Vector3 location;
Vector3 direction;
public Vector3 Location { get => location; set => location = value; }
public Vector3 Direction { get => direction; set => direction = value; }
}
/// <summary>
/// 中心線的確立
/// </summary>
/// <param name="createPoint">曲線點(diǎn)</param>
/// <returns></returns>
List<LinePoint> SetLinePoint(Vector3[] createPoint)
{
List<LinePoint> pipePoints = new List<LinePoint>();
int length = createPoint.Length;
for (int i = 0; i < length; i++)
{
if (i == 0)
{
Vector3 tangent = (createPoint[i + 1] - createPoint[i]).normalized;//法線
AddPipePoints(createPoint[i], tangent, ref pipePoints);
}
else if (i == length - 1)
{
Vector3 tangent = (createPoint[i] - createPoint[i - 1]).normalized;//法線
AddPipePoints(createPoint[i], tangent, ref pipePoints);
}
else
{
Vector3 tangent = (createPoint[i+1] - createPoint[i - 1]).normalized;//法線
AddPipePoints(createPoint[i], tangent, ref pipePoints);
}
}
return pipePoints;
}
/// <summary>
/// 增加中心軸線點(diǎn)
/// </summary>
/// <param name="location">位置</param>
/// <param name="direction">法線</param>
void AddPipePoints(Vector3 location, Vector3 direction, ref List<LinePoint> pipePoints)
{
LinePoint pipePoint = new LinePoint();
pipePoint.Location = location;
pipePoint.Direction = direction;
pipePoints.Add(pipePoint);
}
3.1.3網(wǎng)格創(chuàng)建
/// <summary>
/// 立體網(wǎng)格創(chuàng)建
/// </summary>
/// <param name="createPoint">創(chuàng)建的點(diǎn)數(shù)據(jù)</param>
/// <param name="circularCount">圓的段數(shù)</param>
/// <param name="circularR">圓的半徑</param>
/// <returns></returns>
public Mesh CreateLine3D(Vector3[] createPoint, int circularCount, float circularR)
{
//截面圓
Vector3[] circul = CircularSection(circularCount, circularR);
//中心線
List<LinePoint> centreLine = SetLinePoint(createPoint);
//網(wǎng)格點(diǎn)數(shù)據(jù)
Vector3[] meshPoint = CreateMeshPoint(centreLine, circul);
float uvX = Vector3.Distance(circul[0], circul[1]);
//返回網(wǎng)格
return CreatMesh(centreLine, meshPoint, circul.Length, uvX);
}
/// <summary>
/// 創(chuàng)建網(wǎng)格點(diǎn)數(shù)據(jù)
/// </summary>
/// <param name="linePoint"></param>
/// <param name="circular"></param>
/// <returns></returns>
Vector3[] CreateMeshPoint(List<LinePoint> linePoint, Vector3[] circular)
{
int length = linePoint.Count;
int circularCount = circular.Length;
Vector3[] meshPoint = new Vector3[length * circularCount];
for (int i = 0; i < length; i++)
{
for (int j = 0; j < circularCount; j++)
{
meshPoint[(i * circularCount) + j] = circular[j].FromToMoveRotation(linePoint[i].Location, linePoint[i].Direction);
}
}
return meshPoint;
}
/// <summary>
/// 網(wǎng)格創(chuàng)建
/// </summary>
/// <param name="linePoints">線的軸心線組</param>
/// <param name="meshPoint">網(wǎng)格點(diǎn)</param>
/// <param name="count">段數(shù)</param>
/// <param name="uvX">uv寬度</param>
/// <returns></returns>
Mesh CreatMesh(List<LinePoint> linePoints, Vector3[] meshPoint, int count, float 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;
}
/// <param name="length">線段段數(shù)</param>
/// <param name="count">橫截面段數(shù)(也就是圓的段數(shù))</param>
/// <returns></returns>
int[] GetTriangles(int length, int count)
{
int[] triangles = new int[(count * (length - 1)) * 6];
int k = 0;
if (count == 1)
{
for (int i = 0; i < length-1; i++)
{
int a = i * 2;
triangles[k] = a;
triangles[k + 1] = a + 1;
triangles[k + 2] = a + 3;
triangles[k + 3] = a;
triangles[k + 4] = a + 3;
triangles[k + 5] = a + 2;
k += 6;
}
}
else
{
for (int i = 0; i < length - 1; i++)
{
for (int j = 0; j < count; j++)
{
if (j == count - 1)
{
// Debug.Log("k=" + k);
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;
}
/// <summary>
/// 創(chuàng)建uv
/// </summary>
/// <param name="linePoints"></param>
/// <param name="count"></param>
/// <param name="uvX"></param>
/// <returns></returns>
Vector2[] GetUV(List<LinePoint> linePoints,int count, float uvX)
{
int length = linePoints.Count;
if (count == 1) { count = 2; }
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;
}
3.2貝塞爾曲線的建立方法
源碼
/// <summary>
/// 獲取繪制點(diǎn)
/// </summary>
/// <param name="controlPoints"></param>
/// <param name="segmentsPerCurve"></param>
/// <returns></returns>
public List<Vector3> GetDrawingPoints(List<Vector3> controlPoints, int segmentsPerCurve)
{
List<Vector3> points = new List<Vector3>();
// 下一段的起始點(diǎn)和上段終點(diǎn)是一個(gè),所以是 i+=3
for (int i = 0; i <= controlPoints.Count - 4; i += 3)
{
var p0 = controlPoints[i];
var p1 = controlPoints[i + 1];
var p2 = controlPoints[i + 2];
var p3 = controlPoints[i + 3];
float dis = Vector3.Distance(p0, p3);
int count = Mathf.CeilToInt(segmentsPerCurve * dis);
if (count < segmentsPerCurve)
{
count = segmentsPerCurve;
}
for (int j = 0; j <= count; j++)
{
var t = j / (float)count;
points.Add(CalculateBezierPoint(t, p0, p1, p2, p3));
}
}
return points;
}
// 三階公式
Vector3 CalculateBezierPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3)
{
Vector3 result;
Vector3 p0p1 = (1 - t) * p0 + t * p1;
Vector3 p1p2 = (1 - t) * p1 + t * p2;
Vector3 p2p3 = (1 - t) * p2 + t * p3;
Vector3 p0p1p2 = (1 - t) * p0p1 + t * p1p2;
Vector3 p1p2p3 = (1 - t) * p1p2 + t * p2p3;
result = (1 - t) * p0p1p2 + t * p1p2p3;
return result;
}
3.3貝塞爾曲線應(yīng)用
基于上述方法實(shí)現(xiàn)了貝塞爾創(chuàng)建,保存,讀取,在編輯功能。
案例下載地址
創(chuàng)建曲線
保存曲線
保存方法有兩種分別是,長(zhǎng)期保存和暫時(shí)保存。長(zhǎng)期保存是將保存數(shù)據(jù)寫入本地文件,項(xiàng)目重啟也可以讀?。粫簳r(shí)保存是在項(xiàng)目運(yùn)行期間保存數(shù)據(jù),重啟后丟失。demo使用暫時(shí)保存的方法
讀取再編輯
讀取曲線后可以繼續(xù)編輯當(dāng)前曲線
曲線瀏覽文章來源:http://www.zghlxwxcb.cn/news/detail-760671.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-760671.html
到了這里,關(guān)于【Unity】運(yùn)行時(shí)創(chuàng)建曲線(貝塞爾的運(yùn)用)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!