You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何检测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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 09:19:32