Search Unity

Visual field of view mesh problems (free code)

Discussion in 'Scripting' started by Dahlvash, Sep 12, 2017.

  1. Dahlvash

    Dahlvash

    Joined:
    Feb 9, 2014
    Posts:
    54
    Hi. I'm currently working on a field of view mesh. The difference with most is I'm building the bulk of the mesh from finding collider vertices, casting a ray to the vertex, and one just either side of it. This creates a perfectly smooth view mesh no matter how the player/objects move (no jitteryness like you would get with a fixed stepped ray casting method).
    I have the code working but with an odd problem, the vertex ordering seems to mess up when looking to the left at around a 5/10 degree arc from (-1, 0) see attached images.

    I just cant seem to work out where I'm going wrong though the vertex ordering seems to most likely be the culprit.
    Any help would be greatly appreciated!

    Notes:
    Green lines coming from the centre of the '1' block are the rays to each vertex, short red line is view centre, blue and magenta lines are the edges of the view angle and the horizontal green lines can be ignored (just bottoms of each collider used for my 2d sprite sorting)

    Code below:
    Code (CSharp):
    1. public interface IFacing : IEventSystemHandler
    2. {
    3.     void setFacing_(Vector2 newFacing);
    4.     Vector2 getFacingVector_();
    5.     float getFacingAngle_();
    6. }
    7.  
    8. [DisallowMultipleComponent]
    9. public class Facing_Mouse : MonoBehaviour, IFacing
    10. {
    11.     [SerializeField]
    12.     protected Vector2 facing;
    13.  
    14.     [SerializeField]
    15.     bool drawFacing = false;
    16.    
    17.     protected Vector2 mousePosition = new Vector2();
    18.  
    19.     public float getFacingAngle_()
    20.     {
    21.         return Mathf.Atan2(facing.x, facing.y) * Mathf.Rad2Deg;
    22.     }
    23.  
    24.     public Vector2 getFacingVector_()
    25.     {
    26.         return facing;
    27.     }
    28.  
    29.     public void setFacing_(Vector2 newFacing)
    30.     {
    31.         facing = newFacing;
    32.     }
    33.  
    34.     public void setFacing()
    35.     {
    36.         mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
    37.         facing = (mousePosition - (Vector2)transform.position).normalized;
    38.     }
    39.  
    40.     private void Update()
    41.     {
    42.         setFacing();  
    43.     }
    44.  
    45.     private void OnDrawGizmos()
    46.     {
    47.         if (drawFacing)
    48.         {
    49.             Debug.DrawLine(transform.position, (Vector2)transform.position + facing, Color.red);
    50.         }
    51.     }
    52. }
    Code (CSharp):
    1. public class FieldOfViewEdge : MonoBehaviour
    2. {
    3.     [Range(1f, 360f)]
    4.     public float viewAngle = 90f;
    5.  
    6.     [Range(0.1f, 32f)]
    7.     public float viewRadius = 5f;
    8.  
    9.     [Range(0.0001f, 0.1f)]
    10.     public float vertexSplitAngle = 0.01f;
    11.  
    12.     [SerializeField]
    13.     [Range(0.01f, 1f)]
    14.     float meshResolution;
    15.  
    16.     public LayerMask mask;
    17.     public MeshFilter viewmeshFilter;
    18.     private Mesh viewmesh;
    19.     IFacing facing;
    20.  
    21.     private void Awake()
    22.     {
    23.         facing = GetComponent<IFacing>();
    24.     }
    25.  
    26.     private void Start()
    27.     {
    28.         viewmesh = new Mesh();
    29.         viewmesh.name = "view mesh";
    30.         viewmeshFilter.mesh = viewmesh;
    31.     }
    32.  
    33.     private void draw()
    34.     {
    35.         var colliders = getCollidersInRange();
    36.         var points = colliders.SelectMany(c => getColliderVeterticies(c)).ToList();
    37.         points = pointsInConeOfVision(points);
    38.         points = pointsInRange(points);
    39.         points = splitPointsLR(points);
    40.         points = trimLengths(points);
    41.         points = orderPoints(points);
    42.  
    43.         buildMesh(points);
    44.  
    45.         drawLinesToPoints(points);
    46.         drawFoV();
    47.     }
    48.  
    49.     List<Collider2D> getCollidersInRange()
    50.     {
    51.         return Physics2D.OverlapCircleAll(transform.position, viewRadius).ToList();
    52.     }
    53.  
    54.     List<Vector3> getColliderVeterticies(Collider2D collider)
    55.     {
    56.         if (collider is PolygonCollider2D)
    57.         {
    58.             return (collider as PolygonCollider2D).points.Select(p => collider.transform.TransformPoint(p)).ToList();
    59.         }
    60.         else if (collider is CompositeCollider2D)
    61.         {
    62.             var comp = (collider as CompositeCollider2D);
    63.             for (int i = 0; i < comp.pathCount; i++)
    64.             {
    65.                 Vector2[] points = new Vector2[comp.GetPathPointCount(i)];
    66.                 comp.GetPath(i, points);
    67.                 return points.Select(p => (Vector3)p).ToList();
    68.             }
    69.         }
    70.         else if (collider is BoxCollider2D)
    71.         {
    72.             //todo
    73.         }
    74.         else if (collider is CircleCollider2D)
    75.         {
    76.             //todo
    77.         }
    78.         else if (collider is CapsuleCollider2D)
    79.         {
    80.             //todo
    81.         }
    82.  
    83.         return new List<Vector3>();
    84.     }
    85.  
    86.     List<Vector3> pointsInConeOfVision(List<Vector3> points)
    87.     {
    88.         return points.Where(p => inCone(p)).ToList();
    89.     }
    90.  
    91.     List<Vector3> pointsInRange(List<Vector3> points)
    92.     {
    93.         return points.Where(p => Vector3.Distance(p, transform.position) <= viewRadius).ToList();
    94.     }
    95.  
    96.     List<Vector3> splitPointsLR(List<Vector3> points)
    97.     {
    98.         List<Vector3> splitPoints = new List<Vector3>();
    99.  
    100.         foreach (Vector3 v in points)
    101.         {
    102.             splitPoints.Add(rotatePointAroundPivot(v, transform.position, -vertexSplitAngle));
    103.             splitPoints.Add(rotatePointAroundPivot(v, transform.position, vertexSplitAngle));
    104.         }
    105.  
    106.         points.AddRange(splitPoints);
    107.         return points;
    108.     }
    109.  
    110.     List<Vector3> trimLengths(List<Vector3> points)
    111.     {
    112.         for (int i = 0; i < points.Count; i++)
    113.         {
    114.             var direction = (points[i] - transform.position).normalized;
    115.             var hit = Physics2D.Raycast(transform.position, direction, viewRadius, mask);
    116.  
    117.             if (hit.collider != null)
    118.             {
    119.                 //if (Vector3.Distance(transform.position, hit.point) < viewRadius)
    120.                 points[i] = hit.point;
    121.             }
    122.             else
    123.             {
    124.                 points[i] = (direction * viewRadius) + transform.position;
    125.             }
    126.         }
    127.  
    128.         return points;
    129.     }
    130.  
    131.     List<Vector3> orderPoints(List<Vector3> points)
    132.     {
    133.         points.Sort((a, b) => { return compareDirections(b, a); });
    134.         return points;
    135.     }
    136.  
    137.     void buildMesh(List<Vector3> points)
    138.     {
    139.         viewmesh.Clear();
    140.         if (points.Count() == 0)
    141.         {
    142.             return;
    143.         }
    144.  
    145.         int vertexCount = points.Count + 1;
    146.         Vector3[] vertices = new Vector3[vertexCount];
    147.         int[] triangles = new int[(vertexCount - 2) * 3];
    148.  
    149.         vertices[0] = Vector3.zero;
    150.         for (int i = 0; i < vertexCount - 1; i++)
    151.         {
    152.             vertices[i + 1] = transform.InverseTransformPoint(points[i]);
    153.  
    154.             if (i < vertexCount - 2)
    155.             {
    156.                 triangles[i * 3] = 0;
    157.                 triangles[i * 3 + 1] = i + 1;
    158.                 triangles[i * 3 + 2] = i + 2;
    159.             }
    160.         }
    161.  
    162.         viewmesh.Clear();
    163.         viewmesh.vertices = vertices;
    164.         viewmesh.triangles = triangles;
    165.         viewmesh.RecalculateNormals();
    166.     }
    167.  
    168.     void drawLinesToPoints(List<Vector3> points)
    169.     {
    170.         points.ForEach(p => Debug.DrawLine(transform.position, p, Color.green));
    171.         if (points.Count > 0)
    172.             Debug.DrawLine(transform.position, points[0], Color.red);
    173.     }
    174.  
    175.     void drawFoV()
    176.     {
    177.         var refDirL = Quaternion.AngleAxis(-(viewAngle * 0.5f), Vector3.forward) * facing.getFacingVector_();
    178.         var refDirR = Quaternion.AngleAxis((viewAngle * 0.5f), Vector3.forward) * facing.getFacingVector_();
    179.         Debug.DrawLine(transform.position, (refDirL.normalized * (viewRadius * 2f)) + transform.position, Color.magenta);
    180.         Debug.DrawLine(transform.position, (refDirR.normalized * (viewRadius * 2f)) + transform.position, Color.blue);
    181.     }
    182.  
    183.     public int compareDirections(Vector3 a, Vector3 b)
    184.     {
    185.         double aTanA, aTanB;
    186.         Vector3 reference = transform.position;
    187.  
    188.         aTanA = Mathf.Atan2(a.y - reference.y, a.x - reference.x);
    189.         aTanB = Mathf.Atan2(b.y - reference.y, b.x - reference.x);
    190.  
    191.         if (aTanA < aTanB)
    192.             return -1;
    193.         else if (aTanA > aTanB)
    194.             return 1;
    195.         return 0;
    196.     }
    197.  
    198.     bool inCone(Vector3 targetPos)
    199.     {
    200.         Vector2 dir = targetPos - transform.position;
    201.         float angle = Vector2.Angle(facing.getFacingVector_(), dir);
    202.  
    203.         return angle <= (viewAngle * 0.5f);
    204.     }
    205.  
    206.     Vector3 rotatePointAroundPivot(Vector3 point, Vector3 pivot, float angles)
    207.     {
    208.         var dir = point - pivot; // get point direction relative to pivot
    209.         dir = Quaternion.Euler(0, 0, angles) * dir; // rotate it
    210.         point = dir + pivot; // calculate rotated point
    211.         return point;
    212.     }
    213.  
    214.     private void OnDrawGizmos()
    215.     {
    216.         Gizmos.color = Color.green;
    217.         Gizmos.DrawWireSphere(transform.position, viewRadius);
    218.     }
    219.  
    220.     private void LateUpdate()
    221.     {
    222.         draw();
    223.     }
    224. }

    Edited for working image links and explanation of the lines in each image.
     

    Attached Files:

    Last edited: Sep 13, 2017
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,745
    Just a heads-up: your photobucket account doesn't let you link directly so your images are not visible.

    You can actually just upload the images here on the Unity forum.

    I'd have to see images of what you're trying and what you're seeing, but if you think it's vertex ordering, you can do your own fore/aft-facing poly check and flip the verts if needed on a dynamic basis.
     
  3. Dahlvash

    Dahlvash

    Joined:
    Feb 9, 2014
    Posts:
    54
    Updated original post with working images. Added a key for the lines in the images.
    By vertex ordering I mean ordering clockwise from the left most vertex inside the view arc.
    Im was pretty sure the math was correct but its the only thing I can think of that off in some way.
    Snippet from above code:
    Code (CSharp):
    1.  
    2. ...
    3. List <Vector3> orderPoints(List<Vector3> points)
    4. {
    5.     points.Sort((a, b) => { return compareDirections(b, a); });
    6.      return points;
    7. }
    8. ...
    9. public int compareDirections(Vector3 a, Vector3 b)
    10. {
    11.     double aTanA, aTanB;
    12.     Vector3 reference = transform.position;
    13.     aTanA = Mathf.Atan2(a.y - reference.y, a.x - reference.x);
    14.     aTanB = Mathf.Atan2(b.y - reference.y, b.x - reference.x);
    15.     if (aTanA < aTanB)
    16.         return -1;
    17.     else if (aTanA > aTanB)
    18.         return 1;
    19.     return 0;
    20. }
    21. ...
    22.  
     
  4. Dahlvash

    Dahlvash

    Joined:
    Feb 9, 2014
    Posts:
    54
    Having replied it looks like the reference for the atan angles dont actually use a direction but rather a point.
    I assume atan uses a fixed direction to compare against? Possibly positive along the x axis.
    Would my assumption be correct and if so how can i account for facing?
     
  5. Dahlvash

    Dahlvash

    Joined:
    Feb 9, 2014
    Posts:
    54
    Still cant quite work it out.
    Is there no one that can help?