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;
}
}
