C#求解Unity3D中z=c直线与Mesh四边形的交点问题
在Unity中寻找z=c平面内直线与Mesh四边形的交点
嘿,这个问题我之前做Unity项目时刚好碰到过!看你描述的是直线在z=c形式的平面上(应该是指平行于XY平面的固定Z值平面内的直线吧?),目标是找到它和Mesh四边形的交点,咱们一步步来解决:
核心思路
Unity里的Mesh四边形本质是由两个三角面组成的,但咱们不用拆三角面,直接利用平面相交的思路更高效:先找z=c平面和四边形的交线段,再求这条线段和你的目标直线的交点,最后验证这个点是否在四边形范围内。
具体步骤(附C#代码片段)
1. 准备工作:获取四边形的世界空间顶点
首先把Mesh的局部顶点转换为世界坐标(毕竟你的直线大概率是在世界空间里定义的):
using System.Linq; // 后面提取顶点会用到 Mesh quadMesh = yourQuadObject.GetComponent<MeshFilter>().mesh; Transform quadTransform = yourQuadObject.transform; Vector3[] worldVertices; // 情况1:已知Mesh前4个顶点是四边形的四个顶点 worldVertices = new Vector3[4]; for (int i = 0; i < 4; i++) { worldVertices[i] = quadTransform.TransformPoint(quadMesh.vertices[i]); } // 情况2:不确定顶点顺序,提取Mesh中唯一的4个顶点(适配任意四边形Mesh) // HashSet<int> uniqueIndices = new HashSet<int>(); // foreach (int idx in quadMesh.triangles) // { // uniqueIndices.Add(idx); // if (uniqueIndices.Count == 4) break; // } // worldVertices = uniqueIndices.Select(idx => quadTransform.TransformPoint(quadMesh.vertices[idx])).ToArray();
2. 快速过滤无交点情况
先用mesh.bounds或者顶点Z值范围做初步判断,如果四边形完全在z=c平面的一侧,直接返回空结果:
float targetZ = 5f; // 你的固定Z值c float minZ = Mathf.Min(worldVertices[0].z, worldVertices[1].z, worldVertices[2].z, worldVertices[3].z); float maxZ = Mathf.Max(worldVertices[0].z, worldVertices[1].z, worldVertices[2].z, worldVertices[3].z); // 也可以用mesh.bounds做更粗略的判断:if (!quadMesh.bounds.Contains(new Vector3(0,0,targetZ))) if (targetZ < minZ || targetZ > maxZ) { Debug.Log("无交点:四边形不在z=c平面的范围内"); return null; }
3. 计算z=c平面与四边形四条边的交点
遍历四边形的四条边,找出和z=c平面相交的边,通过线性插值计算交点(这些交点会组成一条线段):
List<Vector3> planeIntersections = new List<Vector3>(); // 定义四边形的四条边(顶点顺序0-1,1-2,2-3,3-0) int[,] edges = new int[,] { {0,1}, {1,2}, {2,3}, {3,0} }; for (int i = 0; i < 4; i++) { Vector3 v1 = worldVertices[edges[i,0]]; Vector3 v2 = worldVertices[edges[i,1]]; // 判断这条边是否跨z=c平面(包含顶点刚好在z=c上的情况) if ((v1.z - targetZ) * (v2.z - targetZ) <= 0) { // 避免除以0(两个顶点都在z=c上的情况) if (Mathf.Abs(v2.z - v1.z) < 0.001f) { planeIntersections.Add(v1); continue; } // 线性插值计算交点 float t = (targetZ - v1.z) / (v2.z - v1.z); Vector3 intersection = Vector3.Lerp(v1, v2, t); planeIntersections.Add(intersection); } } // 去重(防止两个相邻顶点都在z=c上导致重复点) planeIntersections = planeIntersections.Distinct(new Vector3Comparer()).ToList(); // 辅助类:用于Vector3去重 public class Vector3Comparer : IEqualityComparer<Vector3> { public bool Equals(Vector3 a, Vector3 b) => Vector3.Distance(a, b) < 0.001f; public int GetHashCode(Vector3 v) => v.x.GetHashCode() ^ v.y.GetHashCode() ^ v.z.GetHashCode(); }
4. 求目标直线与交线段的交点
把目标直线和交线段都转换为XY平面的二维坐标(因为Z值固定为c),用二维直线相交算法计算交点:
// 假设你的目标直线由两个点lineP1、lineP2定义(Z值都是targetZ) Vector2 lineA = new Vector2(lineP1.x, lineP1.y); Vector2 lineB = new Vector2(lineP2.x, lineP2.y); // 交线段的两个端点 if (planeIntersections.Count != 2) { Debug.Log("特殊情况:四边形完全在z=c平面上,直接用直线与四边形的二维交点即可"); // 这种情况可以直接用二维直线与四边形的交点算法,这里就不展开了 return null; } Vector2 segA = new Vector2(planeIntersections[0].x, planeIntersections[0].y); Vector2 segB = new Vector2(planeIntersections[1].x, planeIntersections[1].y); // 计算二维直线与线段的交点 if (GetLineSegmentIntersection(lineA, lineB, segA, segB, out Vector2 intersection2D)) { Vector3 finalIntersection = new Vector3(intersection2D.x, intersection2D.y, targetZ); Debug.Log("找到交点:" + finalIntersection); return finalIntersection; } else { Debug.Log("直线与四边形的交线段无交点"); return null; }
5. 二维直线与线段相交的辅助函数
这是核心的几何判断算法,用来验证并计算交点:
bool GetLineSegmentIntersection(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, out Vector2 intersection) { intersection = Vector2.zero; float denominator = (p2.x - p1.x) * (p4.y - p3.y) - (p2.y - p1.y) * (p4.x - p3.x); if (Mathf.Abs(denominator) < 0.001f) return false; // 直线平行或重合 float t = ((p3.x - p1.x) * (p4.y - p3.y) - (p3.y - p1.y) * (p4.x - p3.x)) / denominator; float u = -((p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x)) / denominator; // 判断交点是否在线段范围内 if (t >= 0 && t <= 1 && u >= 0 && u <= 1) { intersection = p1 + t * (p2 - p1); return true; } return false; }
额外注意事项
- 如果你的四边形是空间四边形(非平面),
z=c平面和它的交线可能是两条线段,这时候需要分别计算目标直线和两条线段的交点。 - 代码里的
0.001f是精度阈值,可以根据你的项目需求调整。 - 如果需要频繁计算交点,可以把顶点缓存起来,避免重复转换世界坐标。
内容的提问来源于stack exchange,提问作者Dhoni




