如何检测3D物体是否在指定2D边界内并触发消息,解决Rect()使用难题?
解决3D物体在Y轴无限延伸多边形内的触发问题
嘿,我完全懂你遇到的困扰——Unity自带的Rect确实只能靠左上角坐标+宽高来定义,没法直接用四个顶点来匹配你的循环拼接多边形。不过别担心,咱们换个思路就能搞定这个需求,步骤如下:
1. 把3D问题转成2D问题
因为你的多边形是Y轴无限延伸的,所以我们只需要把3D物体的位置投影到X-Z平面(直接忽略Y坐标就行),然后判断这个投影点是否在2D多边形内部就可以了。
2. 自定义可通过四个顶点定义的矩形/多边形
既然Rect不好用,我们可以直接用Vector2列表来存储多边形的所有顶点——尤其是你说的那种循环拼接、相邻多边形共享两个顶点的情况,只要把所有顶点按顺时针或逆时针的连续顺序放进列表,最后让最后一个顶点和第一个顶点闭合就行。
如果需要单独处理每个矩形,也可以写个简单的自定义类:
public class CustomRect { public Vector2[] vertices; // 按顺序存储四个顶点,比如A→B→C→D public CustomRect(Vector2 a, Vector2 b, Vector2 c, Vector2 d) { vertices = new Vector2[] { a, b, c, d }; } }
3. 用射线法判断点是否在多边形内
这是判断点是否在闭合多边形内的通用方法,逻辑很简单:从待检测点向右发射一条水平射线,统计它和多边形边的交点数量——如果是奇数,说明点在内部;偶数则在外部。
直接上实现代码:
// 判断2D投影点是否在闭合多边形内部 private bool IsPointInPolygon(Vector2 point, List<Vector2> polygonVertices) { int intersectionCount = 0; int vertexCount = polygonVertices.Count; for (int i = 0; i < vertexCount; i++) { Vector2 currentVertex = polygonVertices[i]; Vector2 nextVertex = polygonVertices[(i + 1) % vertexCount]; // 循环到下一个顶点,最后一个连回第一个 if (RayIntersectsEdge(point, currentVertex, nextVertex)) { intersectionCount++; } } // 奇数交点=在内部 return intersectionCount % 2 == 1; } // 辅助方法:判断水平射线是否与多边形的某条边相交 private bool RayIntersectsEdge(Vector2 point, Vector2 v1, Vector2 v2) { // 先把边的两个顶点按y坐标从小到大排序,方便判断 if (v1.y > v2.y) { (v1, v2) = (v2, v1); } // 如果点的y坐标不在边的y范围里,直接不相交 if (point.y < v1.y || point.y > v2.y) { return false; } // 处理水平边的特殊情况(避免除以0) if (Mathf.Approximately(v1.y, v2.y)) { // 点的y和边的y相等,且x在边的范围内,也算在区域内 return point.x >= Mathf.Min(v1.x, v2.x) && point.x <= Mathf.Max(v1.x, v2.x); } // 计算边的斜率,以及射线与边的交点x坐标 float edgeSlope = (v2.x - v1.x) / (v2.y - v1.y); float intersectX = v1.x + edgeSlope * (point.y - v1.y); // 交点在点的右侧,才算相交 return intersectX > point.x; }
4. 完整的检测逻辑
把3D物体的位置投影后,调用上面的方法判断,触发消息:
using UnityEngine; using System.Collections.Generic; public class AreaTrigger : MonoBehaviour { // 在Inspector里按顺序添加所有多边形顶点(闭合的) public List<Vector2> polygonVertices = new List<Vector2>(); void Update() { // 把3D物体的位置投影到X-Z平面 Vector3 objectWorldPos = transform.position; Vector2 projectedPos = new Vector2(objectWorldPos.x, objectWorldPos.z); if (IsPointInPolygon(projectedPos, polygonVertices)) { // 这里替换成你要触发的消息逻辑 Debug.Log("物体进入目标区域!"); // 比如发送事件:EventManager.TriggerEvent("ObjectInArea"); } } // 把上面的IsPointInPolygon和RayIntersectsEdge方法放在这里 private bool IsPointInPolygon(Vector2 point, List<Vector2> polygonVertices) { // 实现代码同上 } private bool RayIntersectsEdge(Vector2 point, Vector2 v1, Vector2 v2) { // 实现代码同上 } // 可选:在编辑器里绘制多边形Gizmos,方便调试 void OnDrawGizmos() { Gizmos.color = Color.cyan; for (int i = 0; i < polygonVertices.Count; i++) { Vector3 v1 = new Vector3(polygonVertices[i].x, 0, polygonVertices[i].y); Vector3 v2 = new Vector3(polygonVertices[(i+1)%polygonVertices.Count].x, 0, polygonVertices[(i+1)%polygonVertices.Count].y); Gizmos.DrawLine(v1, v2); } } }
额外小提示
- 如果你是用多个矩形拼接成的大区域,一定要保证顶点顺序是连续的顺时针/逆时针,不然射线法会判断错误。
- 要是你需要判断物体是否在任意一个单独的矩形内,只需要遍历每个CustomRect,把每个矩形的四个顶点作为单独的多边形列表,逐个调用
IsPointInPolygon就行,只要有一个返回true就触发消息。
内容的提问来源于stack exchange,提问作者Denver Thomas
相关产品推荐
相关产品推荐

