Как найти ближайшую точку на линии?

У меня есть точка (A) и вектор (V) (предположим, что это бесконечная длина), и я хочу найти ближайшую точку (B) на линии к моей исходной точке (A). Какое простейшее выражение использует Unity Vector2 или Vector3, чтобы получить это?

6 ответов

Решение

Бесконечная длина:

Если у вас есть линия бесконечной длины с началом и направлением, вычислите произведение точки на направление линии, затем умножьте ее на направление и добавьте к ней начальную точку.

public Vector2 FindNearestPointOnLine(Vector2 origin, Vector2 direction, Vector2 point)
{
    direction.Normalize();
    Vector2 lhs = point - origin;

    float dotP = Vector2.Dot(lhs, direction);
    return origin + direction * dotP;
}

Конечная длина:

Если у вас есть линия с конечной длиной с начальными и конечными позициями, получите заголовок выполнения проекции от начальной точки до. Также используйте Mathf.Clamp хлопать на всякий случай, если линия отключена.

public Vector2 FindNearestPointOnLine(Vector2 origin, Vector2 end, Vector2 point)
{
    //Get heading
    Vector2 heading = (end - origin);
    float magnitudeMax = heading.magnitude;
    heading.Normalize();

    //Do projection from the point but clamp it
    Vector2 lhs = point - origin;
    float dotP = Vector2.Dot(lhs, heading);
    dotP = Mathf.Clamp(dotP, 0f, magnitudeMax);
    return origin + heading * dotP;
}
// For finite lines:
Vector3 GetClosestPointOnFiniteLine(Vector3 point, Vector3 line_start, Vector3 line_end)
{
    Vector3 line_direction = line_end - line_start;
    float line_length = line_direction.magnitude;
    line_direction.Normalize();
    float project_length = Mathf.Clamp(Vector3.Dot(point - line_start, line_direction), 0f, line_length);
    return line_start + line_direction * project_length;
}

// For infinite lines:
Vector3 GetClosestPointOnInfiniteLine(Vector3 point, Vector3 line_start, Vector3 line_end)
{
    return line_start + Vector3.Project(point - line_start, line_end - line_start);
}
Vector3 GetPoint(Vector3 p, Vector3 a, Vector3 b)
{
    return a + Vector3.Project(p - a, b - a);
}

Для бесконечных строк

      private Vector3 GetPointOnLine(Vector3 lineOrigin, Vector3 lineDirection, Vector3 point) 
    => lineOrigin - point - Vector3.Dot(lineOrigin - point, lineDirection) * lineDirection;

Укажите начало и направление вашей линии, а также точку, к которой вы хотите найти ближайшую точку, и вуаля...

Я предполагаю, что вы не знаете местоположения обеих точек, поскольку решение, чтобы найти расстояние между двумя известными точками - простое вычитание. Если вы хотите найти расстояние динамически, я бы предложил использовать raycasting,

Добавление такого сценария в GameObject, который вы считаете pointA может работать на вас:

using UnityEngine;

public class Raycasting : MonoBehaviour 
{
    [SerializeField]
    private LayerMask pointMask; //Set this to the same tag as any GameObject you consider a "point"
    private Vector2 pointA, distance;

    private void Update()
    {
        CheckForPoint();

        Debug.Log(distance);
    }

    private void CheckForPoint()
    {
        pointA = transform.position; //The GameObject's current position in the world

        RaycastHit2D pointB =  Physics2D.Raycast(pointA, Vector2.right * Mathf.Sign(transform.localScale.x), Mathf.Infinity, pointMask);

        //Draws a visible ray in the game view (must have gizmos enabled)
        Debug.DrawRay(pointA, Vector2.right * Mathf.Sign(transform.localScale.x), Color.green); 

        if (pointB)
        {
            distance = (Vector2)pointB.transform.position - pointA;
        }
    }
}

Убедитесь, что на точках, которые вы ищете, есть коллайдер; Затем вы можете установить расстояние от Raycast до Mathf.Infinity (или любой другой длины, которую вы хотите), и pointB будет первым объектом, с которым сталкивается луч.

Вы можете прочитать больше о Physics2D.Raycast API здесь, хотя тот же подход будет работать и в 3D.

      public Vector2 FindNearestPointOnLine(Vector2 origin, Vector2 end, Vector2 point)
{
    //Get heading
          
    Vector2 heading = (end - origin);
    float magnitudeMax = heading.magnitude();
    heading = Vector2.Normalize(heading);
        
    //Do projection from the point but clamp it
    Vector2 lhs = point - origin;
    float dotP = Vector2.Dot(lhs, heading);
    dotP = UnityEngine.Mathf.Clamp(dotP, 0f, magnitudeMax);
    return origin + heading * dotP;
}

vector2.величина не найдена

Другие вопросы по тегам