Unity空间画任意管道
发表于2017-10-18
三维空间画管道主要是用到空间圆参数方程来实现。
在拐角的处理方面,暂时只是前后管道预留出一个半径的长度,用贝塞尔曲线进行十均分然后画mesh连接,由于圆的空间参数方程的单位向量的a与b的值是圆的随机值,是通过坐标向量求得,所以可能会出现画圆的时候起点不统一的情况。
在前后管道重新生成的过程中,直管道部分暂时是两个重叠,尾部缺一个半径的距离,是为了避免管道有裂缝。(这不是重点,重点是参考空间圆的参数方程,代码中有必要注释)
using UnityEngine; using System.Collections; using System.Collections.Generic; public class drawPipe : MonoBehaviour { //第一个点和下一个点 Vector3 firstPoint = Vector3.zero; Vector3 nextPoint = Vector3.zero; //管道半径 public float circleR = 0.5f; //管道材质球 public Material lineMaterial; //管道方向法线向量 Vector3 n,n1,n2; //法向量单位向量 Vector3 n0,n10, n20; //坐标向量 Vector3 i1 = new Vector3(0, 1, 0); Vector3 i2 = new Vector3(1, 0, 0); //单位向量 Vector3 a, al; Vector3 b, bl; //第一个与下一个圆的坐标 Vector3 circleXYZFirst; Vector3 circleXYZNext; //123三点 Vector3[] points = new Vector3[3]; //拐角三点的前后两点 Vector3 cornerPointF; Vector3 cornerPointB; //第一次画管道的最后点的预留点 Vector3 secondPF; //拐点后用来重复续接的靠近尾部的点。 Vector3 cornerPointB_B; //用于下次接着画的起点 Vector3 cornerPointBStart = Vector3.zero; //拐点密度 float density = 0.1f;//绘制的0-1的间隙 越小曲线越接近曲线 //贝塞尔曲线生成拐点坐标列表 List<Vector3> cornerPoints = new List<Vector3>(); Vector3[] _Vertices = new Vector3[16]; Vector3[] _VerticesAll = new Vector3[96]; List<Vector3> _VerticesAllList = new List<Vector3>(); GameObject pointItem; // Use this for initialization void Start () { points = new Vector3[3] { Vector3.zero, Vector3.zero, Vector3.zero }; } // Update is called once per frame void Update () { DrawPipeA(); } private void DrawPipeA() { if (Input.GetMouseButtonDown(0)) { RaycastHit hit; Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); if(Physics.Raycast(ray,out hit) && hit.collider.tag == "plane") { if(firstPoint == Vector3.zero) { firstPoint = hit.point; points[0] = firstPoint; pointItem = (GameObject)Instantiate(Resources.Load("Prefabs/point"), points[0], transform.rotation); } else if(nextPoint == Vector3.zero) { nextPoint = hit.point; points[1] = nextPoint; //画管道 DrawPipeB(points[0], points[1]); pointItem = (GameObject)Instantiate(Resources.Load("Prefabs/point"), points[1], transform.rotation); } else { if(points[2] == Vector3.zero) { nextPoint = hit.point; points[2] = nextPoint; //画带拐角的管道 DrawPipeB(points[0], points[1], points[2]); pointItem = (GameObject)Instantiate(Resources.Load("Prefabs/point"), points[2], transform.rotation); } else { points[0] = points[1]; points[1] = points[2]; points[2] = hit.point; //画带拐角的管道 DrawPipeB(points[0], points[1], points[2]); pointItem = (GameObject)Instantiate(Resources.Load("Prefabs/point"), points[2], transform.rotation); } } } } } //画管道,创建mesh private void DrawPipeB(Vector3 firstP,Vector3 secondP) { n = secondP - firstP; n0 = n / Mathf.Sqrt(n.x * n.x n.y * n.y n.z * n.z); secondPF = secondP - n0 * circleR; GameObject _Line = new GameObject("MeshLine"); _Line.AddComponent<MeshFilter>(); _Line.AddComponent<MeshRenderer>(); if (lineMaterial == null) Debug.Log("The line material is empty! 线段未添加材质球!"); else _Line.GetComponent<MeshRenderer>().material = lineMaterial; al = Vector3.Cross(n, i1);//叉乘 if (al == Vector3.zero) { al = Vector3.Cross(n, i2); } a = Vector3.Normalize(al);//单位化向量 bl = Vector3.Cross(n, a); b = Vector3.Normalize(bl); for (float angle = 0, x = 0; angle < (2 * Mathf.PI - Mathf.PI / 4); angle = angle (Mathf.PI / 4), x ) { circleXYZFirst.x = firstP.x circleR * Mathf.Cos(angle) * a.x circleR * Mathf.Sin(angle) * b.x;//参数方程公式 circleXYZFirst.y = firstP.y circleR * Mathf.Cos(angle) * a.y circleR * Mathf.Sin(angle) * b.y; circleXYZFirst.z = firstP.z circleR * Mathf.Cos(angle) * a.z circleR * Mathf.Sin(angle) * b.z; _Vertices[(int)x] = circleXYZFirst; } for (float angle = 0, y = 8; angle < (2 * Mathf.PI - Mathf.PI / 4); angle = angle (Mathf.PI / 4), y ) { circleXYZNext.x = secondPF.x circleR * Mathf.Cos(angle) * a.x circleR * Mathf.Sin(angle) * b.x;//参数方程公式 circleXYZNext.y = secondPF.y circleR * Mathf.Cos(angle) * a.y circleR * Mathf.Sin(angle) * b.y; circleXYZNext.z = secondPF.z circleR * Mathf.Cos(angle) * a.z circleR * Mathf.Sin(angle) * b.z; _Vertices[(int)y] = circleXYZNext; } //生成所有面 int[] _Triangles = new int[48] { //顶点0,1,9按顺序生成一个面,面朝向顺时针方向 0,1,8, 1,9,8, 1,2,9, 2,10,9, 2,3,10, 3,11,10, 3,4,11, 4,12,11, 4,5,12, 5,13,12, 5,6,13, 6,14,13, 6,7,14, 7,15,14, 7,0,15, 0,8,15 }; Mesh _mesh = new Mesh(); _mesh.Clear(); _mesh.vertices = _Vertices; _mesh.triangles = _Triangles; _mesh.RecalculateNormals(); _Line.GetComponent<MeshFilter>().mesh = _mesh; } //画带拐角的管道,创建mesh private void DrawPipeB(Vector3 firstP, Vector3 secondP,Vector3 thirdP) { cornerPoints.Clear(); _VerticesAllList.Clear(); GameObject _Line = new GameObject("MeshLine"); _Line.AddComponent<MeshFilter>(); _Line.AddComponent<MeshRenderer>(); if (lineMaterial == null) Debug.Log("The line material is empty! 线段未添加材质球!"); else _Line.GetComponent<MeshRenderer>().material = lineMaterial; n1 = secondP - firstP; n2 = thirdP - secondP; n10 = n1 / Mathf.Sqrt(n1.x * n1.x n1.y * n1.y n1.z * n1.z); cornerPointF = secondP - n10 * circleR; n20 = n2 / Mathf.Sqrt(n2.x * n2.x n2.y * n2.y n2.z * n2.z); cornerPointB = secondP n20 * circleR; cornerPointB_B = thirdP - n20 * circleR; //处理第一个点 if (cornerPointBStart == Vector3.zero) { cornerPoints.Add(firstP); } else { cornerPoints.Add(cornerPointBStart); } //给第一个点赋值,留作下次用 cornerPointBStart = cornerPointB; for (float i = 0; i < 1; i = density) { Vector3 cornerPot = CornerPoint(i density, cornerPointF, cornerPointB, secondP); cornerPoints.Add(cornerPot); //Instantiate(Resources.Load("Prefabs/point11"), cornerPot, transform.rotation); } cornerPoints.Add(cornerPointB_B); for (int i = 1; i < cornerPoints.Count; i ) { n = cornerPoints[i] - cornerPoints[i - 1]; //GameObject _Line = new GameObject("MeshLine" i); //_Line.AddComponent<MeshFilter>(); //_Line.AddComponent<MeshRenderer>(); //if (lineMaterial == null) Debug.Log("The line material is empty! 线段未添加材质球!"); //else _Line.GetComponent<MeshRenderer>().material = lineMaterial; al = Vector3.Cross(n, i1);//叉乘 if (al == Vector3.zero) { al = Vector3.Cross(n, i2); } a = Vector3.Normalize(al);//单位化向量 bl = Vector3.Cross(n, a); b = Vector3.Normalize(bl); if(i == 1) { for (float angle = 0, x = 0; angle < (2 * Mathf.PI - Mathf.PI / 4); angle = angle (Mathf.PI / 4), x ) { circleXYZFirst.x = cornerPoints[i - 1].x circleR * Mathf.Cos(angle) * a.x circleR * Mathf.Sin(angle) * b.x;//参数方程公式 circleXYZFirst.y = cornerPoints[i - 1].y circleR * Mathf.Cos(angle) * a.y circleR * Mathf.Sin(angle) * b.y; circleXYZFirst.z = cornerPoints[i - 1].z circleR * Mathf.Cos(angle) * a.z circleR * Mathf.Sin(angle) * b.z; _Vertices[(int)x] = circleXYZFirst; _VerticesAllList.Add(_Vertices[(int)x]);//第一个圈点存入list } } else { for (float angle = 0, x = 0; angle < (2 * Mathf.PI - Mathf.PI / 4); angle = angle (Mathf.PI / 4), x ) { _Vertices[(int)x] = _Vertices[(int)x 8]; //_VerticesAllList.Add(_Vertices[(int)x]);//拐角点存入list } } for (float angle = 0, y = 8; angle < (2 * Mathf.PI - Mathf.PI / 4); angle = angle (Mathf.PI / 4), y ) { circleXYZNext.x = cornerPoints[i].x circleR * Mathf.Cos(angle) * a.x circleR * Mathf.Sin(angle) * b.x;//参数方程公式 circleXYZNext.y = cornerPoints[i].y circleR * Mathf.Cos(angle) * a.y circleR * Mathf.Sin(angle) * b.y; circleXYZNext.z = cornerPoints[i].z circleR * Mathf.Cos(angle) * a.z circleR * Mathf.Sin(angle) * b.z; _Vertices[(int)y] = circleXYZNext; _VerticesAllList.Add(_Vertices[(int)y]);//除了第一圈点的剩下点存入list } } for(int i = 0;i < _VerticesAllList.Count; i ) { _VerticesAll[i] = _VerticesAllList[i]; } //生成所有面 int[] _Triangles = new int[48] { //顶点0,1,9按顺序生成一个面,面朝向顺时针方向 0,1,8, 1,9,8, 1,2,9, 2,10,9, 2,3,10, 3,11,10, 3,4,11, 4,12,11, 4,5,12, 5,13,12, 5,6,13, 6,14,13, 6,7,14, 7,15,14, 7,0,15, 0,8,15, }; int[] _TrianglesAll = new int[528]; for(int i = 0;i < 48; i ) { _TrianglesAll[i] = _Triangles[i]; _TrianglesAll[i 48] = _Triangles[i] 8; _TrianglesAll[i 48 * 2] = _Triangles[i] 8 * 2; _TrianglesAll[i 48 * 3] = _Triangles[i] 8 * 3; _TrianglesAll[i 48 * 4] = _Triangles[i] 8 * 4; _TrianglesAll[i 48 * 5] = _Triangles[i] 8 * 5; _TrianglesAll[i 48 * 6] = _Triangles[i] 8 * 6; _TrianglesAll[i 48 * 7] = _Triangles[i] 8 * 7; _TrianglesAll[i 48 * 8] = _Triangles[i] 8 * 8; _TrianglesAll[i 48 * 9] = _Triangles[i] 8 * 9; _TrianglesAll[i 48 * 10] = _Triangles[i] 8 * 10; } Mesh _mesh = new Mesh(); _mesh.Clear(); _mesh.vertices = _VerticesAll; _mesh.triangles = _TrianglesAll; _mesh.RecalculateNormals(); _Line.GetComponent<MeshFilter>().mesh = _mesh; } //贝塞尔曲线计算拐角 private Vector3 CornerPoint(float k, Vector3 firstPot, Vector3 endPot, Vector3 midPot) { Vector3 a; a.x = k * k * (endPot.x - 2 * midPot.x firstPot.x) firstPot.x 2 * k * (midPot.x - firstPot.x);//公式为B(t)=(1-t)^2*v0 2*t*(1-t)*a0 t*t*v1 其中v0为起点 v1为终点 a为中间点 a.y = k * k * (endPot.y - 2 * midPot.y firstPot.y) firstPot.y 2 * k * (midPot.y - firstPot.y); a.z = k * k * (endPot.z - 2 * midPot.z firstPot.z) firstPot.z 2 * k * (midPot.z - firstPot.z); return a; } }