Home > Back-end >  To find the range of paths between two points
To find the range of paths between two points

Time:10-06

Currently I building a flight simulator and are considering ways to limit the range of the player's flight path.
(Using collider's capabilities, such as OverlapSphere, makes it simpler, but it cannot be done in this scenario.)

Assuming that there is a defined flight path from point 1 to point 2, the flight limit is set in a certain area between the two points, and when it is out of the range, it will alert and return it to its original position.

example of situation1
example of situation2

Apart from the others, a certain flight-restricted area must be calculated on the path between the two points and made detectable to deviate from that area, but for me, whose mathematical ability is hopeless, I don't know how to deal with this.

I need help with this calculation or opinions on other ideas.

CodePudding user response:

I have been playing with some ideas and i think the best solution would be to use Point1 (A) and Point2 (B) as points of a line and calculate the shortest distance from the plane (Point3) to the line (AB). You can use something like this as a starting point: Shortest distance between a point and a line segment

CodePudding user response:

I think you would simply divide this into 3 possible cases to test:

  1. plane is within a certain distance to A

    quite trivial

    var planePosition = plane.transform.position;
    planePosition.y = 0;
    var aPosition = A.transform.position;
    aPosition.y = 0;
    
    if(Vector3.Distance(planePosition, aPosition) <= range) { return true; }
    
  2. plane is within a certain distance to B

    basically the same

    var planePosition = plane.transform.position;
    planePosition.y = 0;
    var bPosition = B.transform.position;
    bPosition.y = 0;
    
    if(Vector3.Distance(planePosition, bPosition) <= range) { return true; }
    
  3. plane is within a rectangle between A and B with a certain width (2x range)

    // vector A -> B
    var delta =  bPosition - aPosition;
    var distance = delta.magnitude;
    // direction A -> B with normalized length 1
    var direction = delta.normalized;
    // direction perpendicular to world Up and "direction"
    var cross = Vector3.Cross(direction, Vector3.up).normalized;
    
    // So now we can get our 4 rect points like
    var rectA = aPosition   cross * range;
    var rectB = aPosition - cross * range;
    var rectC = bPosition   cross * range;
    var rectD = bPosition - cross * range;
    
    // and now we can use the area check formular from the link
    var areaRect = cross * 2 * distance;
    
    var areaAPD = Mathf.Abs((planePosition.x * rectA.z - rectA.x * planePosition.z)   (planePosition.x * rectD.z - rectD.x * planePosition.z)   (rectA.x * rectD.z - rectD.x * rectA.z)) / 2f;  
    var areaDPC = Mathf.Abs((planePosition.x * rectD.z - rectD.x * planePosition.z)   (planePosition.x * rectC.z - rectC.x * planePosition.z)   (rectC.x * rectD.z - rectD.x * rectC.z)) / 2f;   
    var areaCPB = Mathf.Abs((planePosition.x * rectC.z - rectC.x * planePosition.z)   (planePosition.x * rectB.z - rectB.x * planePosition.z)   (rectC.x * rectB.z - rectB.x * rectC.z)) / 2f; 
    var areaPBA = Mathf.Abs((planePosition.x * rectB.z - rectB.x * planePosition.z)   (planePosition.x * rectA.z - rectA.x * planePosition.z)   (rectB.x * rectA.z - rectA.x * rectB.z)) / 2f;
    
    return areaAPD   areaDPC   areaCPB   areaPBA <= areaRect;
    

So I think together it could look like e.g.

public static class MathUtils
{
    // little helper extension to get rid of any differences in Y direction 
    public static Vector2 XZ(this Vector3 v) => new Vector2(v.x, v.z);
    
    public static float Area(Vector2 a, Vector2 b, Vector2 c) => Mathf.Abs((a.x * b.y - b.x * a.y)   (b.x * c.y - c.x * b.y)   (c.x * a.y - a.x * c.y)) / 2f;

    public static bool IsWithinRange(this Vector3 p, Vector3 a, Vector3 b, float range)
    {
        var cleanP = p.XZ();
        var cleanA = a.XZ();

        if(Vector2.Distance(cleanP, cleanA) <= range) return true;

        var cleanB = b.XZ();

        if(Vector2.Distance(cleanP, cleanB) <= range) return true;  

        var delta =  cleanB - cleanA;
        var distance = delta.magnitude;
        var direction = delta.normalized;
        var cross = Vector2.Perpendicular(direction).normalized;

        var rectA = cleanA   cross * range;
        var rectB = cleanA - cross * range;
        var rectC = cleanB   cross * range;
        var rectD = cleanB - cross * range;

        var areaRect = cross * 2 * distance;

        var areaAPD = Area(cleanP, rectA, rectD);
        var areaDPC = Area(cleanP, rectC, rectD);
        var areaCPB = Area(cleanP, rectC, rectB);
        var areaPBA = Area(cleanP, rectA, rectB);

        return areaAPD   areaDPC   areaCPB   areaPBA <= areaRect;
    }
}

You will need to test this of course as I am typing this on the phone ;)

CodePudding user response:

One way to do it is to use the bounding box between the two points. As stated here:

An axis-aligned bounding box, or AABB for short, is a box aligned with coordinate axes and fully enclosing some object. Because the box is never rotated with respect to the axes, it can be defined by just its center and extents, or alternatively by min and max points.

I have written a simple code for you to show how it works (the gizmos code is from here), if you want more complex shapes you can use multiple bounding boxes (moreover, you can create your path using the probuilder package, disable the shape and just use the collider of it)

    [SerializeField] private Transform startTransform;
    [SerializeField] private Transform endTransform;
    [SerializeField] private Transform plane;
    [SerializeField] private Vector3 size = Vector3.one * 3.5f;
    [SerializeField] private Bounds _bounds;

    private void Start()
    {
        _bounds = new Bounds {center = (startTransform.position   endTransform.position) / 2,extents = size};
    }

    private void Update()
    {
        print(_bounds.Contains(plane.position) ? "yes" : "no");
    }

    private void OnDrawGizmos()
    {
        var xVals = new[]
        {
            _bounds.max.x, _bounds.min.x
        };
        var yVals = new[]
        {
            _bounds.max.y, _bounds.min.y
        };
        var zVals = new[]
        {
            _bounds.max.z, _bounds.min.z
        };

        for (int i = 0; i < xVals.Length; i  )
        {
            var x = xVals[i];
            for (int j = 0; j < yVals.Length; j  )
            {
                var y = yVals[j];
                for (int k = 0; k < zVals.Length; k  )
                {
                    var z = zVals[k];

                    var point = new Vector3(x, y, z);
                    Gizmos.DrawWireCube(point, _bounds.extents);

                    if (i == 0)
                    {
                        Gizmos.DrawLine(point, new Vector3(xVals[1], y, z));
                    }

                    if (j == 0)
                    {
                        Gizmos.DrawLine(point, new Vector3(x, yVals[1], z));
                    }

                    if (k == 0)
                    {
                        Gizmos.DrawLine(point, new Vector3(x, y, zVals[1]));
                    }
                }
            }
        }
    }
  • Related