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

VB.NET/C#中任意图形重叠检测及近距离移动实现方法咨询

Hey there! Let's tackle your two main questions—detecting overlaps between arbitrary shapes (like L-shapes and triangles) and moving a triangle to get as close as possible to an L-shape—with practical, code-ready solutions in C# (I’ll note how to adapt this to VB.NET too).


1. Overlap Detection for Arbitrary Shapes

You already have the basics down for circles and rectangles, so let's jump into the go-to method for polygon-based shapes: the Separating Axis Theorem (SAT). It's efficient, easy to implement, and works for all convex polygons. For concave shapes like an L-shape, we just split them into smaller convex shapes (e.g., two rectangles) and check collisions against each part.

How SAT Works (Simplified)

The core idea is: if two convex shapes don't overlap, there's at least one straight line (axis) where their projections don't overlap. To check for overlap:

  • Grab all the edge normals from both shapes (these are our candidate axes)
  • For each axis, calculate the minimum and maximum projection values for both shapes
  • If any axis has non-overlapping projections, the shapes don't collide. If all axes have overlapping projections, they do collide.

C# Example Code

First, let's define some basic shape structures:

public struct Vector2
{
    public float X;
    public float Y;

    public Vector2(float x, float y) { X = x; Y = y; }

    public static Vector2 operator -(Vector2 a, Vector2 b) => new Vector2(a.X - b.X, a.Y - b.Y);
    public static Vector2 operator *(Vector2 v, float scalar) => new Vector2(v.X * scalar, v.Y * scalar);
    public float Dot(Vector2 other) => X * other.X + Y * other.Y;
    public Vector2 Normalize()
    {
        float mag = (float)Math.Sqrt(X*X + Y*Y);
        return mag == 0 ? this : new Vector2(X/mag, Y/mag);
    }
}

public class Polygon
{
    public List<Vector2> Vertices { get; set; }

    public Polygon(List<Vector2> vertices)
    {
        Vertices = vertices;
    }

    // Get all edge normals for the polygon
    public List<Vector2> GetNormals()
    {
        List<Vector2> normals = new List<Vector2>();
        for (int i = 0; i < Vertices.Count; i++)
        {
            Vector2 v1 = Vertices[i];
            Vector2 v2 = Vertices[(i+1)%Vertices.Count];
            Vector2 edge = v2 - v1;
            // Perpendicular (normal) vector
            Vector2 normal = new Vector2(-edge.Y, edge.X).Normalize();
            normals.Add(normal);
        }
        return normals;
    }

    // Get min and max projection values on an axis
    public (float Min, float Max) GetProjection(Vector2 axis)
    {
        float min = Vertices[0].Dot(axis);
        float max = min;
        foreach (var v in Vertices)
        {
            float proj = v.Dot(axis);
            if (proj < min) min = proj;
            if (proj > max) max = proj;
        }
        return (min, max);
    }
}

Now the SAT collision check function:

public static bool DoPolygonsOverlap(Polygon a, Polygon b)
{
    // Check all normals from polygon A
    foreach (var axis in a.GetNormals())
    {
        var projA = a.GetProjection(axis);
        var projB = b.GetProjection(axis);
        // Check if projections don't overlap
        if (projA.Max < projB.Min || projB.Max < projA.Min)
            return false;
    }

    // Check all normals from polygon B
    foreach (var axis in b.GetNormals())
    {
        var projA = a.GetProjection(axis);
        var projB = b.GetProjection(axis);
        if (projA.Max < projB.Min || projB.Max < projA.Min)
            return false;
    }

    // All axes have overlapping projections—shapes collide
    return true;
}

Handling Concave Shapes (Like L-Shapes)

For an L-shape, split it into two convex rectangles. Then check if the triangle overlaps with either rectangle:

// Example: Split L-shape into two rectangles
Polygon lShapePart1 = new Polygon(new List<Vector2>
{
    new Vector2(0,0), new Vector2(5,0), new Vector2(5,3), new Vector2(0,3)
});
Polygon lShapePart2 = new Polygon(new List<Vector2>
{
    new Vector2(3,0), new Vector2(8,0), new Vector2(8,5), new Vector2(3,5)
});

// Check triangle against both parts
bool overlapsWithLShape = DoPolygonsOverlap(triangle, lShapePart1) || DoPolygonsOverlap(triangle, lShapePart2);

2. Moving a Triangle to Get Closest to an L-Shape

To move the triangle as close as possible to the L-shape (without overlapping, or just touching), we'll extend the SAT logic to find the minimum separation vector—this tells us exactly how far and in which direction to move the triangle to make contact.

C# Example: Calculate Minimum Separation

public static (Vector2 SeparationVector, float Distance) GetMinimumSeparation(Polygon a, Polygon b)
{
    float minDistance = float.MaxValue;
    Vector2 bestAxis = new Vector2();

    // Check normals from A
    foreach (var axis in a.GetNormals())
    {
        var projA = a.GetProjection(axis);
        var projB = b.GetProjection(axis);
        float distance = Math.Max(projA.Min - projB.Max, projB.Min - projA.Max);
        if (distance > 0) // No overlap, calculate separation
        {
            if (distance < minDistance)
            {
                minDistance = distance;
                bestAxis = axis;
            }
        }
        else // Already overlapping, we need to push apart—use absolute distance
        {
            float overlap = -distance;
            if (overlap < minDistance)
            {
                minDistance = overlap;
                bestAxis = axis;
            }
        }
    }

    // Check normals from B
    foreach (var axis in b.GetNormals())
    {
        var projA = a.GetProjection(axis);
        var projB = b.GetProjection(axis);
        float distance = Math.Max(projA.Min - projB.Max, projB.Min - projA.Max);
        if (distance > 0)
        {
            if (distance < minDistance)
            {
                minDistance = distance;
                bestAxis = axis;
            }
        }
        else
        {
            float overlap = -distance;
            if (overlap < minDistance)
            {
                minDistance = overlap;
                bestAxis = axis;
            }
        }
    }

    // Determine direction: we want to move A towards/away from B correctly
    Vector2 centerA = new Vector2(a.Vertices.Average(v => v.X), a.Vertices.Average(v => v.Y));
    Vector2 centerB = new Vector2(b.Vertices.Average(v => v.X), b.Vertices.Average(v => v.Y));
    Vector2 direction = centerB - centerA;
    if (direction.Dot(bestAxis) < 0)
        bestAxis = bestAxis * -1;

    return (bestAxis * minDistance, minDistance);
}

Move the Triangle

Now use this to adjust the triangle's position:

// Assume we have our triangle and L-shape parts
Polygon triangle = new Polygon(new List<Vector2>
{
    new Vector2(10,10), new Vector2(12,10), new Vector2(11,12)
});

// Check against both L-shape parts and get the closest separation
var separation1 = GetMinimumSeparation(triangle, lShapePart1);
var separation2 = GetMinimumSeparation(triangle, lShapePart2);
var finalSeparation = separation1.Distance < separation2.Distance ? separation1 : separation2;

// Move the triangle by the separation vector to get it as close as possible
for (int i = 0; i < triangle.Vertices.Count; i++)
{
    triangle.Vertices[i] = triangle.Vertices[i] + finalSeparation.SeparationVector;
}

Adapting to VB.NET

The logic is identical—you just need to adjust the syntax:

  • Replace struct with Structure
  • Use List(Of Vector2) instead of List<Vector2>
  • Adjust operator overloading (e.g., Public Shared Operator -(a As Vector2, b As Vector2) As Vector2)
  • Replace lambda expressions with Function delegates where needed

Hope this gives you a solid starting point! The SAT method is flexible enough to handle almost any polygon-based shape, and the separation vector calculation lets you precisely position your triangle relative to the L-shape.

内容的提问来源于stack exchange,提问作者fra

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 10:12:15