Triangles

This is a simple problem. Given two triangles A and B, you should determine they are intersect, contain or disjoint. (Public edge or point are treated as intersect.)

Input

First line contains an integer T (1 ≤ T ≤ 10), represents there are T test cases.

For each test case: X1 Y1 X2 Y2 X3 Y3 X4 Y4 X5 Y5 X6 Y6. All the coordinate are integer. (X1,Y1) , (X2,Y2), (X3,Y3) forms triangles A ; (X4,Y4) , (X5,Y5), (X6,Y6) forms triangles B.

-10000<=All the coordinate <=10000

Output

For each test case, output “intersect”, “contain” or “disjoint”.

Sample Input
2
0 0 0 1 1 0 10 10 9 9 9 10
0 0 1 1 1 0 0 0 1 1 0 1
Sample Output
disjoint 
intersect 

思路:先判断是否相交,这里是通过  判断任意两条线段的是否存在交点来确定是否相交的;以为两个三角形相交的话,只要判断存在两条线段相交即可;

包含的话是通过面积有判断的,  因为如果是包含关系的话,就可以由   被包含的三角形的三个顶点   来计算另一个三角形(s2)的面积,三个顶点全部计算一边,如果都和另一个三角形(s2)相等的话,就是包含关系;

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
struct node
{
    double x,y;
} f[6];
double s0;
node jiaodian(node x1,node x2,node x3,node x4)//求任意两线段的交点(当然这两条线段必须是属于不同的三角形)
{
    double k1,k2;
    if(x1.x==x2.x)
        k1=0;
    else
        k1=(x1.y-x2.y)/(x1.x-x2.x);
    if(x3.x==x4.x)
        k2=0;
    else
        k2=(x3.y-x4.y)/(x3.x-x4.x);
    node dian;
    if(k1==k2)
    {
        dian.x=99999;
        dian.y=99999;
        return dian;
    }
    double a=x1.y-k1*x1.x;
    double b=x3.y-k2*x3.x;
    dian.x=(b-a)/(k1-k2);
    dian.y=k1*dian.x+a;
    return dian;
}
int A(node x1,node x2,node x3,node x4)//由三点求三角形面积
{
    double s1,s2,s3;
    s1=fabs((x1.x*x2.y+x2.x*x4.y+x4.x*x1.y-x1.y*x2.x-x2.y*x4.x-x4.y*x1.x)/2.0);
    s2=fabs((x1.x*x3.y+x3.x*x4.y+x4.x*x1.y-x1.y*x3.x-x3.y*x4.x-x4.y*x1.x)/2.0);
    s3=fabs((x3.x*x2.y+x2.x*x4.y+x4.x*x3.y-x3.y*x2.x-x2.y*x4.x-x4.y*x3.x)/2.0);
    if(fabs(s1+s2+s3-s0)<=0.000001)
        return 1;
    else
        return 0;
}

int main()
{
    int n;
    scanf("%d",&n);
    while(n--)
    {
        node q;
        int xj=0,xl=0,bh=0;
        for(int i=0; i<6; i++)
            scanf("%lf %lf",&f[i].x,&f[i].y);
        for(int i=0; i<2; i++)
        {
            for(int l=i+1; l<3; l++)
            {
                for(int j=3; j<5; j++)
                {
                    for(int h=j+1; h<6; h++)
                    {
                        q=jiaodian(f[i],f[l],f[j],f[h]);
                        if(q.x>=max(min(f[i].x,f[l].x),min(f[j].x,f[h].x))&&q.x<=min(max(f[i].x,f[l].x),max(f[j].x,f[h].x))&&
                                (q.y>=max(min(f[i].y,f[l].y),min(f[j].y,f[h].y))&&q.y<=min(max(f[i].y,f[l].y),max(f[j].y,f[h].y))))
                        {
                            xj=1;
                            break;
                        }
                    }
                    if(xj)
                        break;
                }
                if(xj)
                    break;
            }
            if(xj)
                break;
        }
        if(xj)
        {
            printf("intersect\n");
            continue;
        }
        int ans=0;
        s0=fabs((f[0].x*f[1].y+f[1].x*f[2].y+f[2].x*f[0].y-f[0].y*f[1].x-f[1].y*f[2].x-f[2].y*f[0].x)/2.0);
        for(int i=3; i<6; i++)
        {
            if(A(f[0],f[1],f[2],f[i]))
            {
                ans++;
            }
        }
        if(ans==3)
            bh=1;
        if(bh)
            printf("contain\n");
        else
            printf("disjoint\n");
    }
    return 0;
}


// Hemisphere.cs using System.Collections; using System.Collections.Generic; using UnityEngine; using System.Linq; [System.Serializable] public struct RADAR { [Header("雷达参数")] public float P_t; // 发射功率(W) public float G; // 天线增益 public float σ; // 目标截面积(m²) public float S_min; // 最小可检测信号功率(W) public float f; // 频率(Hz) public float c; // 光速 public RADAR(float pT, float g, float sigma, float sMin, float frequency, float speedOfLight) { P_t = pT; G = g; σ = sigma; S_min = sMin; f = frequency; c = speedOfLight; } } public class Hemisphere : MonoBehaviour { [Header("半球设置")] [SerializeField] private int longitudeSegments = 20; [SerializeField] private int latitudeSegments = 20; public float radius; [Header("扇形设置")] [SerializeField] public float sectorAngle = Mathf.PI / 2; //扇形高度(弧度) [Range(3, 30)] [SerializeField] private int sectorSegments = 8; [SerializeField] public float rotateSpeed = 1f; [SerializeField] [Range(0.01f, 0.5f)] private float sectorThickness = 0.1f; [SerializeField] public float ScallopingAngleRadian = 1f; // 扇形宽度(弧度) [SerializeField] public float initialAngleOffsetAfter = Mathf.PI / 18f; // 第二个扇形从Z轴向左偏转的角度 public float currentRotation = 0f; private readonly float initialAngleOffset = Mathf.PI / 2f; // 从Z轴正方向开始 // 扫描完成状态标识 public bool IsSweepCompleted { get; private set; } [Header("雷达参数")] [SerializeField] public RADAR radarParams; [Header("渲染设置")] [SerializeField] private Material hemisphereMaterial; [SerializeField] private Material sectorMaterial; [SerializeField] private Material sectorMaterial2; // 第二个扇形的材质,可选 [SerializeField] private Material sideMaterial; // 侧面材质 [Header("碰撞体优化")] [SerializeField] private int collisionMeshMaxVertices = 16; // 组件引用 - 第一个扇形 private MeshFilter hemisphereMeshFilter; private MeshRenderer hemisphereMeshRenderer; private MeshCollider hemisphereCollider; private GameObject sectorObject; private MeshFilter sectorMeshFilter; private MeshRenderer sectorMeshRenderer; private MeshCollider sectorCollider; // 组件引用 - 第二个扇形 private GameObject sectorObject2; private MeshFilter sectorMeshFilter2; private MeshRenderer sectorMeshRenderer2; private MeshCollider sectorCollider2; // 组件引用 - 侧面连接 private GameObject sideObject; private MeshFilter sideMeshFilter; private MeshRenderer sideMeshRenderer; private MeshCollider sideCollider; private GameObject zxSectorObject1; private MeshFilter zxSectorMeshFilter1; private MeshRenderer zxSectorMeshRenderer1; private MeshCollider zxSectorCollider1; private GameObject zxSectorObject2; private MeshFilter zxSectorMeshFilter2; private MeshRenderer zxSectorMeshRenderer2; private MeshCollider zxSectorCollider2; [Header("Z-X平面扇形Y轴偏移")] [SerializeField] private float zxSector2YOffset = 1.0f; // Z-X平面扇形简化碰撞网格(优化性能) private Mesh simplifiedZxSectorMesh1; private Mesh simplifiedZxSectorMesh2; // 跟踪目标信息类 public class TrackedObjectInfo { public float lastDetectedAngle; // 最后检测到的角度 public Vector3 lastLocalPosition; // 最后检测到的本地位置 public bool detectedInCurrentSweep = false; // 当前扫描是否被检测到 public int missedSweeps = 0; //未检测到的扫描周期数 } // 跟踪所有目标的字典 public Dictionary<GameObject, TrackedObjectInfo> trackedObjects = new Dictionary<GameObject, TrackedObjectInfo>(); private float previousRotation = 0f; // 上一帧的旋转角度,用于判断是否完成一圈 private Mesh simplifiedSectorMesh; private Mesh simplifiedSectorMesh2; private Mesh simplifiedSideMesh; void Start() { // 初始化雷达默认参数 if (radarParams.c == 0) { radarParams = new RADAR(1.0f, 10.0f, 0.01f, 6.3e-9f, 2.4e9f, 3e8f); } // 限制参数范围 sectorSegments = Mathf.Max(3, sectorSegments); collisionMeshMaxVertices = Mathf.Max(3, collisionMeshMaxVertices); sectorAngle = Mathf.Max(0.01f, sectorAngle); initialAngleOffsetAfter = Mathf.Max(0.01f, initialAngleOffsetAfter); // 初始化组件 InitHemisphereComponents(); CreateSector(); CreateSector2(); // 创建第二个扇形 CreateSideObject(); // 创建侧面连接对象 //CreateZxSector1(); CreateZxSector2(); UpdateSector(); UpdateSector2(); // 更新第二个扇形 UpdateSideMesh(); // 更新侧面网格 // 初始化扫描状态 IsSweepCompleted = false; } void Update() { sectorAngle = Mathf.Max(0.01f, sectorAngle); initialAngleOffsetAfter = Mathf.Max(0.01f, initialAngleOffsetAfter); // 更新旋转和网格 previousRotation = currentRotation; currentRotation = currentRotation - rotateSpeed * Time.deltaTime; currentRotation = Mathf.Repeat(currentRotation, 2 * Mathf.PI); UpdateHemisphere(); UpdateSector(); UpdateSector2(); // 更新第二个扇形 UpdateSideMesh(); // 更新侧面网格 //UpdateZxSector1(); UpdateZxSector2(); // 检测是否完成一圈扫描 IsSweepCompleted = currentRotation > previousRotation; if (IsSweepCompleted) { CleanupUntrackedObjects(); } } /// <summary> /// 创建第一个Z-X平面扇形(初始化GameObject与组件) /// </summary> public void CreateZxSector1() { // 1. 创建扇形GameObject,设置父物体(跟随雷达旋转) zxSectorObject1 = new GameObject("ZxPlaneSector1"); zxSectorObject1.transform.SetParent(transform); zxSectorObject1.transform.localPosition = Vector3.zero; // 与雷达中心对齐 // 2. 添加MeshFilter(存储网格)、MeshRenderer(渲染)、MeshCollider(碰撞) zxSectorMeshFilter1 = zxSectorObject1.AddComponent<MeshFilter>(); zxSectorMeshRenderer1 = zxSectorObject1.AddComponent<MeshRenderer>(); zxSectorCollider1 = zxSectorObject1.AddComponent<MeshCollider>(); // 碰撞体设置:凸包(支持触发器)、启用触发器(用于目标检测) zxSectorCollider1.convex = true; zxSectorCollider1.isTrigger = true; // 3. 赋值材质(优先使用原扇形材质,无则提示) if (sectorMaterial != null) zxSectorMeshRenderer1.material = sectorMaterial; else Debug.LogWarning("Z-X平面扇形1:请指定sectorMaterial材质"); } /// <summary> /// 创建第二个Z-X平面扇形(逻辑与第一个一致,仅角度偏移不同) /// </summary> public void CreateZxSector2() { zxSectorObject2 = new GameObject("ZxPlaneSector2"); zxSectorObject2.transform.SetParent(transform); zxSectorObject2.transform.localPosition = Vector3.zero; zxSectorMeshFilter2 = zxSectorObject2.AddComponent<MeshFilter>(); zxSectorMeshRenderer2 = zxSectorObject2.AddComponent<MeshRenderer>(); zxSectorCollider2 = zxSectorObject2.AddComponent<MeshCollider>(); zxSectorCollider2.convex = true; zxSectorCollider2.isTrigger = true; // 材质:优先使用sectorMaterial2,无则复用sectorMaterial if (sectorMaterial2 != null) zxSectorMeshRenderer2.material = sectorMaterial2; else if (sectorMaterial != null) zxSectorMeshRenderer2.material = sectorMaterial; else Debug.LogWarning("Z-X平面扇形2:请指定sectorMaterial或sectorMaterial2材质"); } /// <summary> /// 更新第一个Z-X平面扇形(旋转、半径、碰撞体) /// </summary> private void UpdateZxSector1() { if (radius < 0.1f) return; // 关键修改:在原有旋转角度基础上,增加向右旋转的偏移(+initialAngleOffsetAfter) // 向右旋转在数学上表现为角度值增加(Y轴正方向旋转) float adjustedRotation = currentRotation + initialAngleOffset - initialAngleOffsetAfter; Mesh displayMesh = GenerateZxPlaneSectorMesh(sectorSegments, radius, adjustedRotation); zxSectorMeshFilter1.mesh = displayMesh; Mesh cleanedMesh = CleanMeshVertices(displayMesh); simplifiedZxSectorMesh1 = SimplifyMesh(cleanedMesh, collisionMeshMaxVertices); Mesh finalCollisionMesh = IsValidConvexMesh(simplifiedZxSectorMesh1) ? simplifiedZxSectorMesh1 : CreateZxFallbackCollisionMesh(radius, adjustedRotation); try { zxSectorCollider1.sharedMesh = finalCollisionMesh; } catch (System.Exception e) { Debug.LogWarning($"Z-X扇形1碰撞体设置失败: {e.Message},使用安全备用网格"); zxSectorCollider1.sharedMesh = CreateZxSafeFallbackMesh(radius, adjustedRotation); } } /// <summary> /// 更新第二个Z-X平面扇形(仅角度偏移不同,其余逻辑与第一个一致) /// </summary> private void UpdateZxSector2() { if (radius < 0.1f) return; float adjustedRotation = currentRotation + initialAngleOffset - initialAngleOffsetAfter; // 使用带Y轴偏移的网格生成函数 Mesh displayMesh = GenerateZxPlaneSector2Mesh(sectorSegments, radius, adjustedRotation); zxSectorMeshFilter2.mesh = displayMesh; Mesh cleanedMesh = CleanMeshVertices(displayMesh); simplifiedZxSectorMesh2 = SimplifyMesh(cleanedMesh, collisionMeshMaxVertices); // 使用带偏移的备用碰撞网格 Mesh finalCollisionMesh = IsValidConvexMesh(simplifiedZxSectorMesh2) ? simplifiedZxSectorMesh2 : CreateZxSector2FallbackCollisionMesh(radius, adjustedRotation); try { zxSectorCollider2.sharedMesh = finalCollisionMesh; } catch (System.Exception e) { Debug.LogWarning($"带偏移的Z-X扇形2碰撞体设置失败: {e.Message},使用安全备用网格"); // 安全备用网格也需要添加Y轴偏移 zxSectorCollider2.sharedMesh = CreateZxSector2SafeFallbackMesh(radius, adjustedRotation); } } private Mesh GenerateZxPlaneSector2Mesh(int segments, float r, float rotationAngle) { Mesh mesh = new Mesh(); List<Vector3> vertices = new List<Vector3>(); List<int> triangles = new List<int>(); float innerRadius = Mathf.Max(0.1f, r * (1 - sectorThickness)); // 关键修改:使用initialAngleOffsetAfter作为扇形角度 float angleRange = initialAngleOffsetAfter; // 外边缘顶点(带Y轴偏移) for (int i = 0; i <= segments; i++) { float angle = Mathf.Lerp(0, angleRange, (float)i / segments); Vector3 baseVertex = new Vector3( r * Mathf.Cos(angle), // Z坐标 zxSector2YOffset, // Y轴偏移 r * Mathf.Sin(angle) // X坐标 ); vertices.Add(RotateAroundYAxis(baseVertex, rotationAngle)); } int outerVertexCount = segments + 1; // 内边缘顶点(带Y轴偏移) for (int i = 0; i <= segments; i++) { float angle = Mathf.Lerp(0, angleRange, (float)i / segments); Vector3 baseVertex = new Vector3( innerRadius * Mathf.Cos(angle), zxSector2YOffset, innerRadius * Mathf.Sin(angle) ); vertices.Add(RotateAroundYAxis(baseVertex, rotationAngle)); } // 中心点(带Y轴偏移) int centerVertexIndex = outerVertexCount * 2; Vector3 centerWithOffset = new Vector3(0, zxSector2YOffset, 0); vertices.Add(RotateAroundYAxis(centerWithOffset, rotationAngle)); // 生成三角形 for (int i = 0; i < segments; i++) { triangles.AddRange(new[] { centerVertexIndex, i, i + 1 }); int innerCurrent = outerVertexCount + i; int innerNext = outerVertexCount + i + 1; triangles.AddRange(new[] { centerVertexIndex, innerNext, innerCurrent }); triangles.AddRange(new[] { i, innerCurrent, i + 1 }); triangles.AddRange(new[] { i + 1, innerCurrent, innerNext }); } mesh.vertices = vertices.ToArray(); mesh.triangles = triangles.ToArray(); mesh.RecalculateNormals(); mesh.RecalculateBounds(); return mesh; } // 3. 为偏移扇形创建专用备用碰撞网格 private Mesh CreateZxSector2FallbackCollisionMesh(float r, float rotationAngle) { Mesh mesh = new Mesh(); List<Vector3> vertices = new List<Vector3>(); List<int> triangles = new List<int>(); float innerRadius = Mathf.Max(0.1f, r * (1 - sectorThickness)); int segments = 6; // 使用initialAngleOffsetAfter作为角度范围 float angleRange = initialAngleOffsetAfter; float angleStep = angleRange / (segments - 1); // 外边缘顶点(带Y轴偏移) for (int i = 0; i < segments; i++) { float angle = i * angleStep; Vector3 baseVertex = new Vector3( r * Mathf.Cos(angle), zxSector2YOffset, // Y轴偏移 r * Mathf.Sin(angle) ); vertices.Add(RotateAroundYAxis(baseVertex, rotationAngle)); } // 内边缘顶点(带Y轴偏移) int innerOffset = segments; for (int i = 0; i < segments; i++) { float angle = i * angleStep; Vector3 baseVertex = new Vector3( innerRadius * Mathf.Cos(angle), zxSector2YOffset, innerRadius * Mathf.Sin(angle) ); vertices.Add(RotateAroundYAxis(baseVertex, rotationAngle)); } // 生成三角形 for (int i = 0; i < segments - 1; i++) { triangles.AddRange(new[] { 0, i, i + 1 }); triangles.AddRange(new[] { innerOffset, innerOffset + i + 1, innerOffset + i }); triangles.AddRange(new[] { i, innerOffset + i, i + 1 }); triangles.AddRange(new[] { i + 1, innerOffset + i, innerOffset + i + 1 }); } mesh.vertices = vertices.ToArray(); mesh.triangles = triangles.ToArray(); mesh.RecalculateNormals(); return mesh; } // 为偏移扇形创建安全备用网格 private Mesh CreateZxSector2SafeFallbackMesh(float r, float rotationAngle) { Mesh mesh = new Mesh(); List<Vector3> vertices = new List<Vector3>(); float innerRadius = Mathf.Max(0.1f, r * (1 - sectorThickness)); // 带Y轴偏移的核心顶点 Vector3 centerWithOffset = new Vector3(0, zxSector2YOffset, 0); vertices.Add(RotateAroundYAxis(centerWithOffset, rotationAngle)); // 中心 vertices.Add(RotateAroundYAxis(new Vector3(r, zxSector2YOffset, 0), rotationAngle)); // 外点1 vertices.Add(RotateAroundYAxis(new Vector3( r * Mathf.Cos(sectorAngle), zxSector2YOffset, r * Mathf.Sin(sectorAngle) ), rotationAngle)); // 外点2 vertices.Add(RotateAroundYAxis(new Vector3(innerRadius, zxSector2YOffset, 0), rotationAngle)); // 内点1 vertices.Add(RotateAroundYAxis(new Vector3( innerRadius * Mathf.Cos(sectorAngle), zxSector2YOffset, innerRadius * Mathf.Sin(sectorAngle) ), rotationAngle)); // 内点2 // 三角形组合 int[] triangles = { 0, 1, 2, // 外扇面 0, 3, 4, // 内扇面 1, 3, 4, // 侧面1 1, 4, 2 // 侧面2 }; mesh.vertices = vertices.ToArray(); mesh.triangles = triangles; mesh.RecalculateNormals(); return mesh; } private Mesh GenerateZxPlaneSectorMesh(int segments, float r, float rotationAngle) { Mesh mesh = new Mesh(); List<Vector3> vertices = new List<Vector3>(); List<int> triangles = new List<int>(); float innerRadius = Mathf.Max(0.1f, r * (1 - sectorThickness)); // 关键修改:使用initialAngleOffsetAfter作为扇形角度,而非sectorAngle float angleRange = initialAngleOffsetAfter; // 外边缘顶点 for (int i = 0; i <= segments; i++) { // 角度范围从0到initialAngleOffsetAfter float angle = Mathf.Lerp(0, angleRange, (float)i / segments); Vector3 baseVertex = new Vector3( r * Mathf.Cos(angle), // Z坐标 0, // Y坐标 r * Mathf.Sin(angle) // X坐标 ); vertices.Add(RotateAroundYAxis(baseVertex, rotationAngle)); } int outerVertexCount = segments + 1; // 内边缘顶点 for (int i = 0; i <= segments; i++) { float angle = Mathf.Lerp(0, angleRange, (float)i / segments); Vector3 baseVertex = new Vector3( innerRadius * Mathf.Cos(angle), 0, innerRadius * Mathf.Sin(angle) ); vertices.Add(RotateAroundYAxis(baseVertex, rotationAngle)); } // 中心点 int centerVertexIndex = outerVertexCount * 2; vertices.Add(RotateAroundYAxis(Vector3.zero, rotationAngle)); // 生成三角形 for (int i = 0; i < segments; i++) { triangles.AddRange(new[] { centerVertexIndex, i, i + 1 }); int innerCurrent = outerVertexCount + i; int innerNext = outerVertexCount + i + 1; triangles.AddRange(new[] { centerVertexIndex, innerNext, innerCurrent }); triangles.AddRange(new[] { i, innerCurrent, i + 1 }); triangles.AddRange(new[] { i + 1, innerCurrent, innerNext }); } mesh.vertices = vertices.ToArray(); mesh.triangles = triangles.ToArray(); mesh.RecalculateNormals(); mesh.RecalculateBounds(); return mesh; } /// <summary> /// Z-X平面扇形备用碰撞网格(6段简化扇形) /// </summary> private Mesh CreateZxFallbackCollisionMesh(float r, float rotationAngle) { Mesh mesh = new Mesh(); List<Vector3> vertices = new List<Vector3>(); List<int> triangles = new List<int>(); float innerRadius = Mathf.Max(0.1f, r * (1 - sectorThickness)); int segments = 6; // 使用initialAngleOffsetAfter作为角度范围 float angleRange = initialAngleOffsetAfter; float angleStep = angleRange / (segments - 1); // 外边缘顶点 for (int i = 0; i < segments; i++) { float angle = i * angleStep; Vector3 baseVertex = new Vector3( r * Mathf.Cos(angle), 0, r * Mathf.Sin(angle) ); vertices.Add(RotateAroundYAxis(baseVertex, rotationAngle)); } // 内边缘顶点 int innerOffset = segments; for (int i = 0; i < segments; i++) { float angle = i * angleStep; Vector3 baseVertex = new Vector3( innerRadius * Mathf.Cos(angle), 0, innerRadius * Mathf.Sin(angle) ); vertices.Add(RotateAroundYAxis(baseVertex, rotationAngle)); } // 生成三角形 for (int i = 0; i < segments - 1; i++) { triangles.AddRange(new[] { 0, i, i + 1 }); triangles.AddRange(new[] { innerOffset, innerOffset + i + 1, innerOffset + i }); triangles.AddRange(new[] { i, innerOffset + i, i + 1 }); triangles.AddRange(new[] { i + 1, innerOffset + i, innerOffset + i + 1 }); } mesh.vertices = vertices.ToArray(); mesh.triangles = triangles.ToArray(); mesh.RecalculateNormals(); return mesh; } /// <summary> /// Z-X平面扇形安全备用网格(四面体,极端容错) /// </summary> private Mesh CreateZxSafeFallbackMesh(float r, float rotationAngle) { Mesh mesh = new Mesh(); List<Vector3> vertices = new List<Vector3>(); float innerRadius = Mathf.Max(0.1f, r * (1 - sectorThickness)); // 使用initialAngleOffsetAfter作为角度范围(保持之前的修改) float angleRange = initialAngleOffsetAfter; // 核心顶点(应用了向右旋转偏移) vertices.Add(RotateAroundYAxis(Vector3.zero, rotationAngle)); // 中心 // 外点1(0角度位置) vertices.Add(RotateAroundYAxis(new Vector3(r, 0, 0), rotationAngle)); // 外点2(angleRange角度位置) vertices.Add(RotateAroundYAxis(new Vector3( r * Mathf.Cos(angleRange), 0, r * Mathf.Sin(angleRange) ), rotationAngle)); // 内点1(0角度位置) vertices.Add(RotateAroundYAxis(new Vector3(innerRadius, 0, 0), rotationAngle)); // 内点2(angleRange角度位置) vertices.Add(RotateAroundYAxis(new Vector3( innerRadius * Mathf.Cos(angleRange), 0, innerRadius * Mathf.Sin(angleRange) ), rotationAngle)); // 三角形组合 int[] triangles = { 0, 1, 2, // 外扇面 0, 3, 4, // 内扇面 1, 3, 4, // 侧面1 1, 4, 2 // 侧面2 }; mesh.vertices = vertices.ToArray(); mesh.triangles = triangles; mesh.RecalculateNormals(); return mesh; } private Vector3 RotateAroundXAxis(Vector3 point, float angle) { float cosAngle = Mathf.Cos(angle); float sinAngle = Mathf.Sin(angle); // 应用X轴旋转矩阵 return new Vector3( point.x, // X坐标不变 point.y * cosAngle - point.z * sinAngle, // Y坐标变换 point.y * sinAngle + point.z * cosAngle // Z坐标变换 ); } private void CleanupUntrackedObjects() { List<GameObject> toRemove = new List<GameObject>(); foreach (var it in trackedObjects) { if (!it.Value.detectedInCurrentSweep) { it.Value.missedSweeps++; if (it.Value.missedSweeps >= 1) { toRemove.Add(it.Key); } } else { it.Value.missedSweeps = 0; } it.Value.detectedInCurrentSweep = false; } foreach (var obj in toRemove) { trackedObjects.Remove(obj); } } // 组件初始化 private void InitHemisphereComponents() { hemisphereMeshFilter = GetComponent<MeshFilter>() ?? gameObject.AddComponent<MeshFilter>(); hemisphereMeshRenderer = GetComponent<MeshRenderer>() ?? gameObject.AddComponent<MeshRenderer>(); hemisphereCollider = GetComponent<MeshCollider>() ?? gameObject.AddComponent<MeshCollider>(); if (hemisphereMaterial != null) hemisphereMeshRenderer.material = hemisphereMaterial; else Debug.LogWarning("请指定半球材质"); } private void CreateSector() { sectorObject = new GameObject("RotatingSector"); sectorObject.transform.SetParent(transform); sectorObject.transform.localPosition = Vector3.zero; sectorMeshFilter = sectorObject.AddComponent<MeshFilter>(); sectorMeshRenderer = sectorObject.AddComponent<MeshRenderer>(); sectorCollider = sectorObject.AddComponent<MeshCollider>(); sectorCollider.convex = true; sectorCollider.isTrigger = true; if (sectorMaterial != null) sectorMeshRenderer.material = sectorMaterial; else Debug.LogWarning("请指定扇形材质"); } // 创建第二个扇形 private void CreateSector2() { sectorObject2 = new GameObject("RotatingSector2"); sectorObject2.transform.SetParent(transform); sectorObject2.transform.localPosition = Vector3.zero; sectorMeshFilter2 = sectorObject2.AddComponent<MeshFilter>(); sectorMeshRenderer2 = sectorObject2.AddComponent<MeshRenderer>(); sectorCollider2 = sectorObject2.AddComponent<MeshCollider>(); sectorCollider2.convex = true; sectorCollider2.isTrigger = true; // 使用指定的第二个材质或默认使用第一个材质 if (sectorMaterial2 != null) sectorMeshRenderer2.material = sectorMaterial2; else if (sectorMaterial != null) sectorMeshRenderer2.material = sectorMaterial; else Debug.LogWarning("请指定扇形材质"); } // 创建侧面连接对象 private void CreateSideObject() { sideObject = new GameObject("SideConnection"); sideObject.transform.SetParent(transform); sideObject.transform.localPosition = Vector3.zero; sideMeshFilter = sideObject.AddComponent<MeshFilter>(); sideMeshRenderer = sideObject.AddComponent<MeshRenderer>(); sideCollider = sideObject.AddComponent<MeshCollider>(); sideCollider.convex = true; sideCollider.isTrigger = true; if (sideMaterial != null) sideMeshRenderer.material = sideMaterial; else if (sectorMaterial != null) sideMeshRenderer.material = sectorMaterial; else Debug.LogWarning("请指定侧面材质"); } public void UpdateHemisphere() { radius = Mathf.Max(0.1f, RadarScanningRadius(radarParams)); Mesh mesh = GenerateHemisphereMesh(longitudeSegments, latitudeSegments, radius); hemisphereCollider.sharedMesh = mesh; hemisphereMeshFilter.mesh = mesh; } public void UpdateSector() { if (radius < 0.1f) return; float adjustedRotation = currentRotation + initialAngleOffset; Mesh displayMesh = GenerateSectorMesh(sectorSegments, radius, adjustedRotation); sectorMeshFilter.mesh = displayMesh; // 处理碰撞体网格 Mesh cleanedMesh = CleanMeshVertices(displayMesh); simplifiedSectorMesh = SimplifyMesh(cleanedMesh, collisionMeshMaxVertices); Mesh finalCollisionMesh = IsValidConvexMesh(simplifiedSectorMesh) ? simplifiedSectorMesh : CreateFallbackCollisionMesh(radius, adjustedRotation); try { sectorCollider.sharedMesh = finalCollisionMesh; } catch (System.Exception e) { Debug.LogWarning($"碰撞体设置失败: {e.Message},使用备用网格"); sectorCollider.sharedMesh = CreateSafeFallbackMesh(radius, adjustedRotation); } } // 更新第二个扇形 private void UpdateSector2() { if (radius < 0.1f) return; // 第二个扇形从Z轴向左偏转initialAngleOffsetAfter弧度 float adjustedRotation = currentRotation + initialAngleOffset - initialAngleOffsetAfter; Mesh displayMesh = GenerateSectorMesh(sectorSegments, radius, adjustedRotation); sectorMeshFilter2.mesh = displayMesh; // 处理碰撞体网格 Mesh cleanedMesh = CleanMeshVertices(displayMesh); simplifiedSectorMesh2 = SimplifyMesh(cleanedMesh, collisionMeshMaxVertices); Mesh finalCollisionMesh = IsValidConvexMesh(simplifiedSectorMesh2) ? simplifiedSectorMesh2 : CreateFallbackCollisionMesh(radius, adjustedRotation); try { sectorCollider2.sharedMesh = finalCollisionMesh; } catch (System.Exception e) { Debug.LogWarning($"第二个扇形碰撞体设置失败: {e.Message},使用备用网格"); sectorCollider2.sharedMesh = CreateSafeFallbackMesh(radius, adjustedRotation); } } // 更新侧面连接网格 private void UpdateSideMesh() { if (radius < 0.1f) return; float adjustedRotation1 = currentRotation + initialAngleOffset; float adjustedRotation2 = currentRotation + initialAngleOffset - initialAngleOffsetAfter; Mesh sideMesh = GenerateSideConnectionMesh(sectorSegments, radius, adjustedRotation1, adjustedRotation2); sideMeshFilter.mesh = sideMesh; // 处理碰撞体网格 Mesh cleanedMesh = CleanMeshVertices(sideMesh); simplifiedSideMesh = SimplifySideMesh(cleanedMesh, collisionMeshMaxVertices); Mesh finalCollisionMesh = IsValidConvexMesh(simplifiedSideMesh) ? simplifiedSideMesh : CreateSideFallbackCollisionMesh(radius, adjustedRotation1, adjustedRotation2); try { sideCollider.sharedMesh = finalCollisionMesh; } catch (System.Exception e) { Debug.LogWarning($"侧面碰撞体设置失败: {e.Message},使用备用网格"); sideCollider.sharedMesh = CreateSideSafeFallbackMesh(radius, adjustedRotation1, adjustedRotation2); } } private Mesh GenerateHemisphereMesh(int lonSeg, int latSeg, float r) { Mesh mesh = new Mesh(); List<Vector3> vertices = new List<Vector3>(); List<int> triangles = new List<int>(); // 生成顶点 for (int i = 0; i <= lonSeg; i++) { float lonAngle = Mathf.Lerp(0, 2 * Mathf.PI, (float)i / lonSeg); for (int j = 0; j <= latSeg; j++) { float latAngle = Mathf.PI / 2 * (1 - (float)j / latSeg); vertices.Add(new Vector3( r * Mathf.Sin(latAngle) * Mathf.Cos(lonAngle), r * Mathf.Cos(latAngle), r * Mathf.Sin(latAngle) * Mathf.Sin(lonAngle) )); } } // 生成三角形 for (int i = 0; i < lonSeg; i++) { for (int j = 0; j < latSeg; j++) { int topLeft = i * (latSeg + 1) + j; int topRight = (i + 1) * (latSeg + 1) + j; int bottomLeft = i * (latSeg + 1) + (j + 1); int bottomRight = (i + 1) * (latSeg + 1) + (j + 1); triangles.AddRange(new[] { topLeft, bottomLeft, topRight, topRight, bottomLeft, bottomRight }); } } mesh.vertices = vertices.ToArray(); mesh.triangles = triangles.ToArray(); mesh.RecalculateNormals(); return mesh; } private Mesh GenerateSectorMesh(int segments, float r, float rotationAngle) { Mesh mesh = new Mesh(); List<Vector3> vertices = new List<Vector3>(); List<int> triangles = new List<int>(); float innerRadius = Mathf.Max(0.1f, r * (1 - sectorThickness)); // 外边缘顶点 for (int i = 0; i <= segments; i++) { float angle = Mathf.Lerp(0, sectorAngle, (float)i / segments); vertices.Add(RotateAroundYAxis(new Vector3(r * Mathf.Cos(angle), r * Mathf.Sin(angle), 0), rotationAngle)); } int outerCount = segments + 1; // 内边缘顶点 for (int i = 0; i <= segments; i++) { float angle = Mathf.Lerp(0, sectorAngle, (float)i / segments); vertices.Add(RotateAroundYAxis(new Vector3(innerRadius * Mathf.Cos(angle), innerRadius * Mathf.Sin(angle), 0), rotationAngle)); } // 中心点 int originIndex = outerCount * 2; vertices.Add(RotateAroundYAxis(Vector3.zero, rotationAngle)); // 生成三角形 for (int i = 0; i < segments; i++) { // 外扇形面 triangles.AddRange(new[] { originIndex, i, i + 1 }); // 内扇形面 triangles.AddRange(new[] { originIndex, outerCount + i + 1, outerCount + i }); // 侧面 triangles.AddRange(new[] { i, outerCount + i, i + 1 }); triangles.AddRange(new[] { i + 1, outerCount + i, outerCount + i + 1 }); } mesh.vertices = vertices.ToArray(); mesh.triangles = triangles.ToArray(); mesh.RecalculateNormals(); mesh.RecalculateBounds(); return mesh; } // 生成连接两个扇形的侧面网格 private Mesh GenerateSideConnectionMesh(int segments, float r, float rotationAngle1, float rotationAngle2) { Mesh mesh = new Mesh(); List<Vector3> vertices = new List<Vector3>(); List<int> triangles = new List<int>(); float innerRadius = Mathf.Max(0.1f, r * (1 - sectorThickness)); // 获取第一个扇形的顶点 List<Vector3> sector1Outer = new List<Vector3>(); List<Vector3> sector1Inner = new List<Vector3>(); for (int i = 0; i <= segments; i++) { float angle = Mathf.Lerp(0, sectorAngle, (float)i / segments); sector1Outer.Add(RotateAroundYAxis(new Vector3(r * Mathf.Cos(angle), r * Mathf.Sin(angle), 0), rotationAngle1)); sector1Inner.Add(RotateAroundYAxis(new Vector3(innerRadius * Mathf.Cos(angle), innerRadius * Mathf.Sin(angle), 0), rotationAngle1)); } // 获取第二个扇形的顶点 List<Vector3> sector2Outer = new List<Vector3>(); List<Vector3> sector2Inner = new List<Vector3>(); for (int i = 0; i <= segments; i++) { float angle = Mathf.Lerp(0, sectorAngle, (float)i / segments); sector2Outer.Add(RotateAroundYAxis(new Vector3(r * Mathf.Cos(angle), r * Mathf.Sin(angle), 0), rotationAngle2)); sector2Inner.Add(RotateAroundYAxis(new Vector3(innerRadius * Mathf.Cos(angle), innerRadius * Mathf.Sin(angle), 0), rotationAngle2)); } // 添加顶点 vertices.AddRange(sector1Outer); vertices.AddRange(sector1Inner); vertices.AddRange(sector2Outer); vertices.AddRange(sector2Inner); int sector1OuterStart = 0; int sector1InnerStart = segments + 1; int sector2OuterStart = (segments + 1) * 2; int sector2InnerStart = (segments + 1) * 3; // 生成连接两个扇形外侧的三角形 for (int i = 0; i < segments; i++) { triangles.Add(sector1OuterStart + i); triangles.Add(sector2OuterStart + i); triangles.Add(sector1OuterStart + i + 1); triangles.Add(sector1OuterStart + i + 1); triangles.Add(sector2OuterStart + i); triangles.Add(sector2OuterStart + i + 1); } // 生成连接两个扇形内侧的三角形 for (int i = 0; i < segments; i++) { triangles.Add(sector1InnerStart + i); triangles.Add(sector1InnerStart + i + 1); triangles.Add(sector2InnerStart + i); triangles.Add(sector1InnerStart + i + 1); triangles.Add(sector2InnerStart + i + 1); triangles.Add(sector2InnerStart + i); } // 生成连接两个扇形端面的三角形 triangles.Add(sector1OuterStart); triangles.Add(sector1InnerStart); triangles.Add(sector2OuterStart); triangles.Add(sector1InnerStart); triangles.Add(sector2InnerStart); triangles.Add(sector2OuterStart); triangles.Add(sector1OuterStart + segments); triangles.Add(sector2OuterStart + segments); triangles.Add(sector1InnerStart + segments); triangles.Add(sector1InnerStart + segments); triangles.Add(sector2OuterStart + segments); triangles.Add(sector2InnerStart + segments); mesh.vertices = vertices.ToArray(); mesh.triangles = triangles.ToArray(); mesh.RecalculateNormals(); mesh.RecalculateBounds(); return mesh; } private Mesh CleanMeshVertices(Mesh originalMesh) { Mesh cleanedMesh = new Mesh(); List<Vector3> vertices = new List<Vector3>(); Dictionary<Vector3, int> vertexMap = new Dictionary<Vector3, int>(); List<int> newTriangles = new List<int>(); // 顶点去重 foreach (Vector3 v in originalMesh.vertices) { Vector3 rounded = new Vector3( Mathf.Round(v.x * 10000) / 10000, Mathf.Round(v.y * 10000) / 10000, Mathf.Round(v.z * 10000) / 10000 ); if (!vertexMap.ContainsKey(rounded)) { vertexMap[rounded] = vertices.Count; vertices.Add(rounded); } } // 过滤无效三角形 int[] originalTris = originalMesh.triangles; for (int i = 0; i < originalTris.Length; i += 3) { Vector3 p0 = originalMesh.vertices[originalTris[i]]; Vector3 p1 = originalMesh.vertices[originalTris[i + 1]]; Vector3 p2 = originalMesh.vertices[originalTris[i + 2]]; // 过滤面积过小的三角形 if (Vector3.Cross(p1 - p0, p2 - p0).magnitude < 0.0001f) continue; // 映射新顶点索引 if (TryGetMappedIndex(vertexMap, p0, out int n0) && TryGetMappedIndex(vertexMap, p1, out int n1) && TryGetMappedIndex(vertexMap, p2, out int n2)) { newTriangles.AddRange(new[] { n0, n1, n2 }); } } cleanedMesh.vertices = vertices.ToArray(); cleanedMesh.triangles = newTriangles.ToArray(); cleanedMesh.RecalculateNormals(); cleanedMesh.RecalculateBounds(); return cleanedMesh; } private Mesh SimplifyMesh(Mesh originalMesh, int maxVertices) { maxVertices = Mathf.Clamp(maxVertices, 3, 30); if (originalMesh.vertexCount <= maxVertices) return originalMesh; Mesh simplified = new Mesh(); List<Vector3> vertices = new List<Vector3>(); List<int> triangles = new List<int>(); float totalAngle = sectorAngle; float angleStep = totalAngle / (maxVertices - 1); int halfCount = originalMesh.vertexCount / 2; // 采样外边缘顶点 for (int i = 0; i < maxVertices / 2; i++) { int index = FindClosestVertexByAngle(originalMesh.vertices, 0, halfCount, i * angleStep, currentRotation + initialAngleOffset); vertices.Add(originalMesh.vertices[index]); int inde1x = FindClosestVertexByAngle(originalMesh.vertices, 0, halfCount, i * angleStep, currentRotation + initialAngleOffsetAfter); vertices.Add(originalMesh.vertices[inde1x]); } // 采样内边缘顶点 for (int i = 0; i < maxVertices - vertices.Count; i++) { int index = FindClosestVertexByAngle(originalMesh.vertices, halfCount, originalMesh.vertexCount, i * angleStep, currentRotation + initialAngleOffset); vertices.Add(originalMesh.vertices[index]); int inde1x = FindClosestVertexByAngle(originalMesh.vertices, halfCount, originalMesh.vertexCount, i * angleStep, currentRotation + initialAngleOffsetAfter); vertices.Add(originalMesh.vertices[inde1x]); } // 确保顶点数量不超标 if (vertices.Count > maxVertices) vertices.RemoveRange(maxVertices, vertices.Count - maxVertices); // 按角度排序 vertices.Sort((a, b) => Mathf.Atan2(a.z, a.x).CompareTo(Mathf.Atan2(b.z, b.x)) ); GenerateConvexTriangles(vertices, triangles); simplified.vertices = vertices.ToArray(); simplified.triangles = triangles.ToArray(); simplified.RecalculateNormals(); simplified.RecalculateBounds(); return simplified; } // 简化侧面网格 private Mesh SimplifySideMesh(Mesh originalMesh, int maxVertices) { maxVertices = Mathf.Clamp(maxVertices, 6, 30); if (originalMesh.vertexCount <= maxVertices) return originalMesh; // 对于侧面网格,我们只需要保留关键顶点 Mesh simplified = new Mesh(); List<Vector3> vertices = new List<Vector3>(); // 获取原始网格的边界点 Bounds bounds = originalMesh.bounds; vertices.Add(bounds.min); vertices.Add(new Vector3(bounds.min.x, bounds.min.y, bounds.max.z)); vertices.Add(new Vector3(bounds.max.x, bounds.min.y, bounds.min.z)); vertices.Add(bounds.max); vertices.Add(new Vector3(bounds.min.x, bounds.max.y, bounds.min.z)); vertices.Add(new Vector3(bounds.max.x, bounds.max.y, bounds.min.z)); vertices.Add(new Vector3(bounds.min.x, bounds.max.y, bounds.max.z)); vertices.Add(bounds.max); // 生成凸包三角形 List<int> triangles = new List<int>(); GenerateConvexTriangles(vertices, triangles); simplified.vertices = vertices.ToArray(); simplified.triangles = triangles.ToArray(); simplified.RecalculateNormals(); simplified.RecalculateBounds(); return simplified; } private bool IsValidConvexMesh(Mesh mesh) { if (!IsValidCollisionMesh(mesh)) return false; // 检查法向量一致性 Vector3[] normals = mesh.normals; if (normals.Length == 0) return false; Vector3 firstNormal = normals[0]; foreach (Vector3 normal in normals) { if (Vector3.Dot(firstNormal, normal) < 0.9f) return false; } return true; } private bool IsValidCollisionMesh(Mesh mesh) { if (mesh == null || mesh.vertexCount < 3 || mesh.triangles.Length < 3) return false; // 检查至少有3个不同顶点 HashSet<Vector3> uniqueVertices = new HashSet<Vector3>(); foreach (Vector3 v in mesh.vertices) { uniqueVertices.Add(v); if (uniqueVertices.Count >= 3) break; } return uniqueVertices.Count >= 3; } private Mesh CreateFallbackCollisionMesh(float r, float rotationAngle) { Mesh mesh = new Mesh(); List<Vector3> vertices = new List<Vector3>(); List<int> triangles = new List<int>(); float innerRadius = Mathf.Max(0.1f, r * (1 - sectorThickness)); int segments = 6; float VerticalAngleStep = sectorAngle / (segments - 1);//竖直角度步长 // 外边缘顶点 for (int i = 0; i < segments; i++) { float angle = i * VerticalAngleStep; vertices.Add(RotateAroundYAxis(new Vector3(r * Mathf.Cos(angle), 0, r * Mathf.Sin(angle)), rotationAngle)); } // 内边缘顶点 int innerOffset = segments; for (int i = 0; i < segments; i++) { float angle = i * VerticalAngleStep; vertices.Add(RotateAroundYAxis(new Vector3(innerRadius * Mathf.Cos(angle), 0, innerRadius * Mathf.Sin(angle)), rotationAngle)); } // 生成三角形 for (int i = 0; i < segments - 1; i++) { triangles.AddRange(new[] { 0, i, i + 1 }); // 外扇面 triangles.AddRange(new[] { innerOffset, innerOffset + i + 1, innerOffset + i }); // 内扇面 triangles.AddRange(new[] { i, innerOffset + i, i + 1 }); // 侧面1 triangles.AddRange(new[] { i + 1, innerOffset + i, innerOffset + i + 1 }); // 侧面2 } mesh.vertices = vertices.ToArray(); mesh.triangles = triangles.ToArray(); mesh.RecalculateNormals(); return mesh; } private Mesh CreateSafeFallbackMesh(float r, float rotationAngle) { Mesh mesh = new Mesh(); List<Vector3> vertices = new List<Vector3>(); float innerRadius = Mathf.Max(0.1f, r * (1 - sectorThickness)); // 核心顶点 vertices.Add(Vector3.zero); // 中心 vertices.Add(RotateAroundYAxis(new Vector3(r, 0, 0), rotationAngle)); // 外点1 vertices.Add(RotateAroundYAxis(new Vector3(r * Mathf.Cos(sectorAngle), 0, r * Mathf.Sin(sectorAngle)), rotationAngle)); // 外点2 vertices.Add(RotateAroundYAxis(new Vector3(innerRadius, 0, 0), rotationAngle)); // 内点1 vertices.Add(RotateAroundYAxis(new Vector3(innerRadius * Mathf.Cos(sectorAngle), 0, innerRadius * Mathf.Sin(sectorAngle)), rotationAngle)); // 内点2 // 简单三角形组合 int[] triangles = { 0, 1, 2, // 外扇面 0, 3, 4, // 内扇面 1, 3, 4, // 侧面1 1, 4, 2 // 侧面2 }; mesh.vertices = vertices.ToArray(); mesh.triangles = triangles; mesh.RecalculateNormals(); return mesh; } // 创建侧面备用碰撞体网格 private Mesh CreateSideFallbackCollisionMesh(float r, float rotationAngle1, float rotationAngle2) { Mesh mesh = new Mesh(); List<Vector3> vertices = new List<Vector3>(); List<int> triangles = new List<int>(); float innerRadius = Mathf.Max(0.1f, r * (1 - sectorThickness)); // 获取关键点 Vector3 outer1Start = RotateAroundYAxis(new Vector3(r, 0, 0), rotationAngle1); Vector3 outer1End = RotateAroundYAxis(new Vector3(r * Mathf.Cos(sectorAngle), 0, r * Mathf.Sin(sectorAngle)), rotationAngle1); Vector3 inner1Start = RotateAroundYAxis(new Vector3(innerRadius, 0, 0), rotationAngle1); Vector3 inner1End = RotateAroundYAxis(new Vector3(innerRadius * Mathf.Cos(sectorAngle), 0, innerRadius * Mathf.Sin(sectorAngle)), rotationAngle1); Vector3 outer2Start = RotateAroundYAxis(new Vector3(r, 0, 0), rotationAngle2); Vector3 outer2End = RotateAroundYAxis(new Vector3(r * Mathf.Cos(sectorAngle), 0, r * Mathf.Sin(sectorAngle)), rotationAngle2); Vector3 inner2Start = RotateAroundYAxis(new Vector3(innerRadius, 0, 0), rotationAngle2); Vector3 inner2End = RotateAroundYAxis(new Vector3(innerRadius * Mathf.Cos(sectorAngle), 0, innerRadius * Mathf.Sin(sectorAngle)), rotationAngle2); // 添加顶点 vertices.Add(outer1Start); vertices.Add(outer1End); vertices.Add(inner1Start); vertices.Add(inner1End); vertices.Add(outer2Start); vertices.Add(outer2End); vertices.Add(inner2Start); vertices.Add(inner2End); // 生成连接三角形 triangles.AddRange(new[] { 0, 4, 1 }); // 外侧面1 triangles.AddRange(new[] { 1, 4, 5 }); // 外侧面2 triangles.AddRange(new[] { 2, 3, 6 }); // 内侧面1 triangles.AddRange(new[] { 3, 7, 6 }); // 内侧面2 triangles.AddRange(new[] { 0, 2, 4 }); // 起始端面1 triangles.AddRange(new[] { 2, 6, 4 }); // 起始端面2 triangles.AddRange(new[] { 1, 5, 3 }); // 结束端面1 triangles.AddRange(new[] { 3, 5, 7 }); // 结束端面2 mesh.vertices = vertices.ToArray(); mesh.triangles = triangles.ToArray(); mesh.RecalculateNormals(); return mesh; } private Mesh CreateSideSafeFallbackMesh(float r, float rotationAngle1, float rotationAngle2) { Mesh mesh = new Mesh(); List<Vector3> vertices = new List<Vector3>(); // 简单的四面体作为备用 Vector3 center = Vector3.zero; Vector3 outer1 = RotateAroundYAxis(new Vector3(r, 0, 0), rotationAngle1); Vector3 outer2 = RotateAroundYAxis(new Vector3(r, 0, 0), rotationAngle2); Vector3 top = Vector3.up * r * 0.5f; vertices.Add(center); vertices.Add(outer1); vertices.Add(outer2); vertices.Add(top); int[] triangles = { 0, 1, 2, 0, 1, 3, 0, 2, 3, 1, 2, 3 }; mesh.vertices = vertices.ToArray(); mesh.triangles = triangles; mesh.RecalculateNormals(); return mesh; } private float RadarScanningRadius(RADAR radar) { if (radar.f <= 0 || radar.S_min <= 0) { Debug.LogWarning("雷达参数错误,使用默认半径"); return 10f; } float wavelength = radar.c / radar.f; float numerator = radar.P_t * Mathf.Pow(radar.G, 2) * wavelength * radar.σ; float denominator = Mathf.Pow(4 * Mathf.PI, 3) * radar.S_min; if (denominator <= 0 || numerator <= 0) { Debug.LogWarning("雷达参数无效,使用默认半径"); return 10f; } return Mathf.Max(0.1f, Mathf.Pow(numerator / denominator, 1f / 4f)); } private Vector3 RotateAroundYAxis(Vector3 point, float angle) { float cos = Mathf.Cos(angle); float sin = Mathf.Sin(angle); return new Vector3( point.x * cos - point.z * sin, point.y, point.x * sin + point.z * cos ); } private int FindClosestVertexByAngle(Vector3[] vertices, int start, int end, float targetAngle, float rotation) { int closestIndex = start; float minDiff = float.MaxValue; for (int i = start; i < end; i++) { Vector3 localPos = Quaternion.Inverse(Quaternion.Euler(0, rotation * Mathf.Rad2Deg, 0)) * vertices[i]; float angle = Mathf.Repeat(Mathf.Atan2(localPos.z, localPos.x), 2 * Mathf.PI); float diff = Mathf.Abs(angle - targetAngle); if (diff < minDiff) { minDiff = diff; closestIndex = i; } } return closestIndex; } private void GenerateConvexTriangles(List<Vector3> vertices, List<int> triangles) { if (vertices.Count < 3) return; // 生成三角形扇 int center = 0; for (int i = 1; i < vertices.Count - 1; i++) { triangles.AddRange(new[] { center, i, i + 1 }); } // 闭合最后一个三角形 if (vertices.Count > 3) triangles.AddRange(new[] { center, vertices.Count - 1, 1 }); } private bool TryGetMappedIndex(Dictionary<Vector3, int> map, Vector3 original, out int index) { Vector3 rounded = new Vector3( Mathf.Round(original.x * 10000) / 10000, Mathf.Round(original.y * 10000) / 10000, Mathf.Round(original.z * 10000) / 10000 ); return map.TryGetValue(rounded, out index); } // 第一个扇形的碰撞检测 private void OnTriggerStay(Collider other) { CheckAndTrackObject(other); } // 第二个扇形的碰撞检测 private void OnTriggerStay2(Collider other) { CheckAndTrackObject(other); } // 统一的碰撞检测和目标跟踪逻辑 private void CheckAndTrackObject(Collider other) { if (other is BoxCollider) { GameObject detectedObj = other.gameObject; // 目标相对于雷达的本地坐标 Vector3 localPos = transform.InverseTransformPoint(detectedObj.transform.position); float X = Mathf.Abs(localPos.x); float Y = Mathf.Abs(localPos.y); float Z = Mathf.Abs(localPos.z); float projectionLength = Mathf.Sqrt(X * X + Z * Z); float angleWithXZPlane; if (projectionLength < Mathf.Epsilon) { angleWithXZPlane = Mathf.PI / 2; } else { angleWithXZPlane = Mathf.Atan2(Mathf.Abs(Y), projectionLength); } float targetAngle = Mathf.Atan2(localPos.z, localPos.x); // 检查是否在第一个扇形范围内 float adjustedRotation1 = currentRotation + initialAngleOffset; float sectorStartAngle1 = adjustedRotation1; float sectorEndAngle1 = adjustedRotation1 + sectorAngle; // 检查是否在第二个扇形范围内 float adjustedRotation2 = currentRotation + initialAngleOffset - initialAngleOffsetAfter; float sectorStartAngle2 = adjustedRotation2; float sectorEndAngle2 = adjustedRotation2 + sectorAngle; // 限制角度0-2PI targetAngle = Mathf.Repeat(targetAngle, 2 * Mathf.PI); sectorStartAngle1 = Mathf.Repeat(sectorStartAngle1, 2 * Mathf.PI); sectorEndAngle1 = Mathf.Repeat(sectorEndAngle1, 2 * Mathf.PI); sectorStartAngle2 = Mathf.Repeat(sectorStartAngle2, 2 * Mathf.PI); sectorEndAngle2 = Mathf.Repeat(sectorEndAngle2, 2 * Mathf.PI); // 检查是否在任何一个扇形内 bool isInSector1 = false; if (sectorStartAngle1 <= sectorEndAngle1) { isInSector1 = targetAngle >= sectorStartAngle1 && targetAngle <= sectorEndAngle1; } else { isInSector1 = targetAngle >= sectorStartAngle1 || targetAngle <= sectorEndAngle1; } bool isInSector2 = false; if (sectorStartAngle2 <= sectorEndAngle2) { isInSector2 = targetAngle >= sectorStartAngle2 && targetAngle <= sectorEndAngle2; } else { isInSector2 = targetAngle >= sectorStartAngle2 || targetAngle <= sectorEndAngle2; } bool isInSector = isInSector1 || isInSector2; bool isInRange = localPos.magnitude <= radius; bool isradian = 0 <= angleWithXZPlane && angleWithXZPlane <= sectorAngle; if (isInSector && isInRange && isradian) { if (!trackedObjects.ContainsKey(detectedObj)) { trackedObjects[detectedObj] = new TrackedObjectInfo(); } TrackedObjectInfo info = trackedObjects[detectedObj]; info.lastDetectedAngle = currentRotation; info.lastLocalPosition = localPos; // 本地坐标 info.detectedInCurrentSweep = true; info.missedSweeps = 0; } } } private void OnDestroy() { if (simplifiedSectorMesh != null) Destroy(simplifiedSectorMesh); if (simplifiedSectorMesh2 != null) Destroy(simplifiedSectorMesh2); if (sectorObject != null) Destroy(sectorObject); if (sectorObject2 != null) Destroy(sectorObject2); if (simplifiedZxSectorMesh1 != null) Destroy(simplifiedZxSectorMesh1); if (simplifiedZxSectorMesh2 != null) Destroy(simplifiedZxSectorMesh2); if (zxSectorObject1 != null) Destroy(zxSectorObject1); if (zxSectorObject2 != null) Destroy(zxSectorObject2); } // 获取当前检测到的目标的本地坐标 public Dictionary<GameObject, Vector3> GetCurrentlyDetectedLocalPositions() { return trackedObjects.Where(it => it.Value.detectedInCurrentSweep) .ToDictionary(it => it.Key, it => it.Value.lastLocalPosition); } // 获取所有跟踪目标的最后本地坐标 public Dictionary<GameObject, Vector3> GetAllTrackedLocalPositions() { return trackedObjects.ToDictionary(it => it.Key, it => it.Value.lastLocalPosition); } public float RadianTo360Degree() { return Mathf.Rad2Deg * currentRotation; } public void StartRotation() { currentRotation = 0; // 重置旋转角度 enabled = true; } }目前生成的是0-90纬度的半球,保留0-45度的部分
最新发布
09-18
<think> 我们正在处理一个关于Unity中生成半球体网格的问题,用户希望将原本生成0-90纬度范围的半球修改为只保留0-45纬度范围的部分。 根据用户描述,原代码生成的是一个0到90纬度的半球(即从北极到赤道),现在需要将其改为0到45纬度(即从北极到北纬45度)。 在球坐标系中,通常纬度用θ表示(θ从0到π),其中θ=0对应北极点,θ=π/2对应赤道。用户原来的半球是θ从0到π/2(即0到90度),现在希望θ从0到π/4(0到45度)。 生成球体网格的基本思路: 1. 在经度和纬度上划分网格(经度:0~360度,纬度:0~45度)。 2. 根据球面坐标生成顶点。 3. 构建三角形索引。 修改要点: 原代码中纬度(θ)的循环范围是从0到π/2(90度),现在需要改为从0到π/4(45度)。同时,由于纬度范围缩小,顶点数和三角形索引也需要相应调整。 假设原代码片段如下(这是一个典型的半球生成代码): ```csharp // 假设参数 int longitudes = 24; // 经度分割数 int latitudes = 10; // 纬度分割数(原0-90度) // 生成顶点 List<Vector3> vertices = new List<Vector3>(); for (int lat = 0; lat <= latitudes; lat++) { float theta = (float)lat * Mathf.PI * 0.5f / latitudes; // 原范围:0 ~ π/2 float sinTheta = Mathf.Sin(theta); float cosTheta = Mathf.Cos(theta); for (int lon = 0; lon <= longitudes; lon++) { float phi = (float)lon * 2.0f * Mathf.PI / longitudes; // 经度:0 ~ 2π float sinPhi = Mathf.Sin(phi); float cosPhi = Mathf.Cos(phi); Vector3 vertex = new Vector3(cosPhi * sinTheta, cosTheta, sinPhi * sinTheta); vertices.Add(vertex); } } // 生成三角形 List<int> triangles = new List<int>(); for (int lat = 0; lat < latitudes; lat++) { for (int lon = 0; lon < longitudes; lon++) { int current = lat * (longitudes + 1) + lon; int next = current + longitudes + 1; triangles.Add(current); triangles.Add(next); triangles.Add(current + 1); triangles.Add(next); triangles.Add(next + 1); triangles.Add(current + 1); } } ``` 修改为0-45度半球: 1. 修改纬度循环的范围:原latitudes分割的是0-90度,现在我们要分割0-45度,但是分割的纬度层数(latitudes)可以保持不变,也可以根据需求调整。这里我们保持纬度分割数不变,但只生成0-45度部分。 因此,我们只需要调整theta的计算公式: 原:theta = lat * (π/2) / latitudes -> 范围0到π/2 改为:theta = lat * (π/4) / latitudes -> 范围0到π/4 但是注意,这样修改后,最后的纬度层将位于45度(π/4)处。 另外,由于顶点数减少,三角形索引的生成逻辑不变,因为仍然是一个矩形网格,但注意纬度层数不变(latitudes还是同样的层数)?实际上,如果保持latitudes分割数不变,那么每一层之间的角度间隔变小了(45/10度,原来是90/10度)。但如果用户希望半球看起来更平滑,可能需要增加分割数,但这不是必须的。 所以,主要修改就是: 将 theta 的计算由原来的乘以 (Mathf.PI * 0.5f) 改为乘以 (Mathf.PI * 0.25f)(即π/4)。 但是,用户可能希望保持原有的分割数(latitudes)不变,但生成的半球范围缩小。所以,我们只需要改变theta的最大值即可。 注意:这样修改后,整个半球的高度会降低(因为从北极到45度,而不是到90度),所以看起来是一个更“扁”的半球帽。 另一种考虑:如果用户希望保留原有的高度分割(即每一层之间的高度差不变),那么我们需要调整latitudes的值,使得在0-45度范围内有同样的层数,这样顶点数会减少。但是用户没有明确说明,我们按照第一种方式(保持纬度分割数不变)修改。 因此,修改后的代码片段: ```csharp // 修改后的半球生成(0-45度) int longitudes = 24; // 经度分割数 int latitudes = 10; // 纬度分割数(现在分割0-45度范围) List<Vector3> vertices = new List<Vector3>(); for (int lat = 0; lat <= latitudes; lat++) { // 修改:将最大角度改为Mathf.PI/4(45度) float theta = (float)lat * (Mathf.PI * 0.25f) / latitudes; // 范围:0 ~ π/4 float sinTheta = Mathf.Sin(theta); float cosTheta = Mathf.Cos(theta); for (int lon = 0; lon <= longitudes; lon++) { float phi = (float)lon * 2.0f * Mathf.PI / longitudes; float sinPhi = Mathf.Sin(phi); float cosPhi = Mathf.Cos(phi); Vector3 vertex = new Vector3(cosPhi * sinTheta, cosTheta, sinPhi * sinTheta); vertices.Add(vertex); } } // 三角形生成部分不变(注意:latitudes还是10,所以循环次数不变) List<int> triangles = new List<int>(); for (int lat = 0; lat < latitudes; lat++) { for (int lon = 0; lon < longitudes; lon++) { int current = lat * (longitudes + 1) + lon; int next = current + longitudes + 1; triangles.Add(current); triangles.Add(next); triangles.Add(current + 1); triangles.Add(next); triangles.Add(next + 1); triangles.Add(current + 1); } } ``` 注意:这样修改后,生成的半球顶点在垂直方向(Y轴)的范围为cos(0)=1到cos(π/4)=√2/2≈0.707,即高度从1下降到0.707。如果用户希望保持原来的高度(从1到0)并只取上半部分,那么需要重新考虑高度映射?但是按照球坐标,高度就是cosθ,θ从0到45度,高度从1下降到0.707,这是正确的。 如果用户希望整个半球看起来更小(高度上压缩),那么可以调整高度,但通常不需要,因为这就是球坐标的表示。 另外,如果用户希望半球底部在Y=0处(即原来90度时底部在Y=0,现在45度时也希望底部在Y=0),那么需要重新映射Y坐标。但原问题没有提到,所以按照标准球坐标处理。 总结:主要修改就是调整theta的最大值从π/2变为π/4。 如果用户有更具体的需求(比如高度调整),可以根据需要进一步修改。 请用户根据实际情况调整参数(如longitudes和latitudes)以达到所需的精细度。 注意:由于修改后半球的范围变小,可能需要在生成后调整缩放或位置以适应场景。 如果用户原始代码有其他逻辑(比如半径、缩放等),请相应调整。 如果用户需要完整的代码示例,请提供原始代码,以便我们进行针对性修改。 </think> 根据你的需求,需要将Unity中生成的半球网格范围从0-90度调整为0-45度。以下是修改后的C#代码实现: ```csharp using UnityEngine; using System.Collections.Generic; public class HemisphereGenerator : MonoBehaviour { public float radius = 1f; public int longitudeSegments = 24; public int latitudeSegments = 10; // 现在分割的是0-45度范围 void Start() { MeshFilter meshFilter = gameObject.AddComponent<MeshFilter>(); Mesh mesh = new Mesh(); meshFilter.mesh = mesh; List<Vector3> vertices = new List<Vector3>(); List<Vector2> uvs = new List<Vector2>(); List<int> triangles = new List<int>(); // 生成顶点 (纬度范围: 0° 到 45°) for (int lat = 0; lat <= latitudeSegments; lat++) { // 修改关键:角度范围从0-90改为0-45 float theta = lat * Mathf.PI * 0.25f / latitudeSegments; // π/4 = 45° float sinTheta = Mathf.Sin(theta); float cosTheta = Mathf.Cos(theta); for (int lon = 0; lon <= longitudeSegments; lon++) { float phi = lon * 2 * Mathf.PI / longitudeSegments; float sinPhi = Mathf.Sin(phi); float cosPhi = Mathf.Cos(phi); // 球面坐标转笛卡尔坐标 Vector3 vertex = new Vector3( radius * cosPhi * sinTheta, radius * cosTheta, // Y轴向上 radius * sinPhi * sinTheta ); vertices.Add(vertex); // UV映射 uvs.Add(new Vector2((float)lon / longitudeSegments, (float)lat / latitudeSegments)); } } // 生成三角形 for (int lat = 0; lat < latitudeSegments; lat++) { for (int lon = 0; lon < longitudeSegments; lon++) { int current = lat * (longitudeSegments + 1) + lon; int next = current + longitudeSegments + 1; // 第一个三角形 triangles.Add(current); triangles.Add(next); triangles.Add(current + 1); // 第二个三角形 triangles.Add(next); triangles.Add(next + 1); triangles.Add(current + 1); } } // 添加底部中心顶点 (45°边界) vertices.Add(Vector3.zero); uvs.Add(new Vector2(0.5f, 1f)); int centerIndex = vertices.Count - 1; // 生成底部三角形扇 for (int lon = 0; lon < longitudeSegments; lon++) { int topIndex = latitudeSegments * (longitudeSegments + 1) + lon; triangles.Add(centerIndex); triangles.Add(topIndex); triangles.Add(topIndex + 1); } // 应用网格数据 mesh.vertices = vertices.ToArray(); mesh.uv = uvs.ToArray(); mesh.triangles = triangles.ToArray(); mesh.RecalculateNormals(); mesh.RecalculateBounds(); } } ``` ### 关键修改说明: 1. **纬度范围调整**: ```csharp // 原90度范围(0-90) // float theta = lat * Mathf.PI * 0.5f / latitudeSegments; // 新45度范围(0-45) float theta = lat * Mathf.PI * 0.25f / latitudeSegments; ``` 将角度上限从$\frac{\pi}{2}$(90°)改为$\frac{\pi}{4}$(45°) 2. **底部封闭处理**: - 添加了中心顶点作为45°平面的中心点 - 生成三角形扇连接所有45°边界的顶点 3. **顶点生成**: - 使用球坐标转换公式:$x = r\cos\phi\sin\theta$, $y = r\cos\theta$, $z = r\sin\phi\sin\theta$ - $\theta$范围:$0$到$\frac{\pi}{4}$(0-45°) - $\phi$范围:$0$到$2\pi$(完整经度) ### 使用说明: 1. 将此脚本附加到GameObject 2. 调整参数: - `radius`:半球半径 - `longitudeSegments`:经向细分度(值越大越平滑) - `latitudeSegments`:纬向细分度(仅针对0-45°范围) ### 注意事项: - 生成的半球轴向:Y轴向上(Unity默认) - 底部平面位于45°纬线位置 - 顶部顶点位于北极点(0°,Y轴最高点) - 如果需要更精确的控制,可考虑使用Unity的`Mathf.Deg2Rad`进行角度转换[^1]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值