Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Question Trouble with mesh generation for a field of view

Discussion in 'Scripting' started by Accurina, Feb 28, 2023.

  1. Accurina

    Accurina

    Joined:
    Apr 1, 2021
    Posts:
    4
    Hi everyone, hope you are doing well.

    Currently I have an issue with generating a mesh to display a field of view, what I wanted for the script to be placed into the child of the parent, and then for the child to generate a mesh in a cone facing the parent object's forward position on the X and Z axis since the game would be in 3D, it would also take in account for obstacles and such as shown in the rough drawn sketch below.


    Fig 1. Sketch depicting what I was aiming for, fov mesh will spread out in a cone with an angle of AngleOfAttack and stop upon colliding with an object.


    However currently in my code, it doesn't generate a field of view facing forward, rather it generates it facing backwards as seen in the images as posted below, and not only that, but the origin of the parent object for some reason is moved to the middle of the map despite no change in the transform.position of it




    Fig 2. How object looks without playing.
    Fig 3. Data inputted into script in child object


    Fig 4. How script looks like while playing, despite no change in transform the origin (red, green, blue arrows) is no longer on the cube itself. FOV also generated backwards


    Fig 5. Upon slight rotation, the fov blocked does not change properly, points in the correct direction of forward but generates the meshes into the sunset instead of ending where its blocked



    Here is the code used for reference.

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class IFU_UnitFieldOfFireVision : MonoBehaviour // this is attached to a child GO
    4. {
    5.     public IFU_Entity EntityData;
    6.     [Header("Field of View Generator")]
    7.     public float AngleOfView;
    8.     public int rayCount;
    9.     public float ViewDistance;
    10.     public LayerMask GroundLayerMask;
    11.  
    12.     //public Vector3 ThisObjectTransformPos;
    13.     public GameObject MainBodyGO;
    14.  
    15.     float StartingAngle;
    16.     Mesh mesh;
    17.     Vector3 MainBodyPos;
    18.  
    19.  
    20.  
    21.     // Start is called before the first frame update
    22.     void Start()
    23.     {
    24.  
    25.         // to get unit data, if no unit data is present (testing purposes) it uses the number placed in the inspector inside this script
    26.         EntityData = this.gameObject.GetComponentInParent<IFU_Entity>();
    27.         if (EntityData != null)
    28.         {
    29.             AngleOfView = EntityData.AngleOfAttack;
    30.             ViewDistance = EntityData.AttackRangeRadius;
    31.             Debug.Log("set new pos");
    32.         }
    33.  
    34.         //EntityData = t
    35.         mesh = new Mesh();
    36.         GetComponent<MeshFilter>().mesh = mesh;
    37.         //Origin = Vector3.zero;
    38.     }
    39.  
    40.     private void Update()
    41.     {
    42.         SetOrigin(MainBodyGO.transform.position);
    43.         SetAimDirection(MainBodyGO.transform.rotation.eulerAngles); //  PROBLEM HERE, ITS USING THE TRANSFORM.POSITION OF OBJECT, WHICH IS GOOD BUT IT DOESN'T
    44.     }
    45.  
    46.     // Update is called once per frame
    47.     void LateUpdate()
    48.     {
    49.         float fov = AngleOfView;
    50.        
    51.         int raycount = rayCount;
    52.         float angle = StartingAngle;
    53.         float angleIncrease = fov / raycount;
    54.         float viewDistance = ViewDistance;
    55.  
    56.         Vector3[] Vertices = new Vector3[raycount + 1 + 1]; // positioning of points
    57.         Vector2[] uv = new Vector2[Vertices.Length]; // texture rendered - vector 2 as the image it references is flat 2d so it uses vector 2 only
    58.         int[] triangles = new int[raycount * 3]; // actual points of the mesh
    59.  
    60.         //Vertices[0] = this.transform.position; // generates at this transform + main body transform.position aka that body far from vector3.zero away from main body
    61.         //Vertices[0] = this.transform.localPosition; // mesh origin is at this transform's position
    62.         Vertices[0] = Vector3.zero; // same as above, mesh origin is at this transform's position
    63.         //Vertices[0] = ThisGameObject.transform.localPosition; // generates at this transform + main body transform.position aka that body far from vector3.zero away from main body
    64.  
    65.         int vertexIndex = 1; // 0 is the origin
    66.         int triangleIndex = 0;
    67.         for (int i = 0; i <= raycount; i++)
    68.         {
    69.             Vector3 Vertex = Vector3.zero + GetVectorFromAngle(angle) * viewDistance;
    70.  
    71.             RaycastHit raycastHit;
    72.  
    73.             if (Physics.Raycast(MainBodyPos, GetVectorFromAngle(angle), out raycastHit, viewDistance, GroundLayerMask)) //  PROBLEM HERE, STARTING ANGLE ISN'T AT VECTOR3.FORWARD, BUT IT STARTS AT WHATEVER THE MAIN BODY ROTATION IS
    74.             {
    75.                 Vertex = raycastHit.point;
    76.             }
    77.             else
    78.             {
    79.                 Vertex = Vector3.zero + GetVectorFromAngle(angle) * viewDistance;
    80.             }
    81.  
    82.             Vertices[vertexIndex] = Vertex;
    83.  
    84.  
    85.             if (i > 0) //  PROBLEM HERE, FOR RAYCAST HIT - THE GENERATION OF VERTEXES IT STARTS AT MAINBODY.POS AWAY FROM THE MAINBODY NOT AT TRANSFORM.POS
    86.             {
    87.                 triangles[triangleIndex + 0] = 0;
    88.                 triangles[triangleIndex + 1] = vertexIndex - 1;
    89.                 triangles[triangleIndex + 2] = vertexIndex;
    90.  
    91.                 triangleIndex += 3;
    92.             }
    93.  
    94.             vertexIndex++;
    95.             angle -= angleIncrease; // goes counter clockwise if +, - for anti clockwise
    96.         }
    97.  
    98.         mesh.vertices = Vertices;
    99.         mesh.uv = uv;
    100.         mesh.triangles = triangles;
    101.     }
    102.  
    103.  
    104.     Vector3 GetVectorFromAngle(float angle)
    105.     {
    106.         float angleRad = angle * (Mathf.PI / 180f);
    107.         return new Vector3(Mathf.Cos(angleRad), 0, Mathf.Sin(angleRad));
    108.  
    109.     }
    110.  
    111.     float GetAngleFromVectorFloat(Vector3 dir)
    112.     {
    113.         dir = dir.normalized;
    114.         float n = Mathf.Atan2(dir.z, dir.x) * Mathf.Rad2Deg;
    115.         if (n < 0)
    116.         {
    117.             n += 360;
    118.         }
    119.  
    120.         return n;
    121.     }
    122.     public void SetOrigin(Vector3 NewOrigin)
    123.     {
    124.         MainBodyPos = NewOrigin;
    125.     }
    126.  
    127.     public void SetAimDirection (Vector3 aimDirection)
    128.     {
    129.         StartingAngle = GetAngleFromVectorFloat(aimDirection) - (AngleOfView/2);
    130.     }
    131. }
    132.  
    I've tried fixing it for weeks but have no clue how to proceed with this, any help would be appreciated, thank you.
     

    Attached Files:

    Last edited: Feb 28, 2023
  2. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,571
    Your "GetVectorFromAngle" method returns a direction vector that is relative to worldspace. So it does not rotate with your object at all. You should use
    transform.TransformDirection()
    to transform the vector from local space of this object into worldspace. You need a worldspace vector when you use it in your raycast.

    Your second issue is this line:

    Code (CSharp):
    1. Vertex = raycastHit.point;
    Here raycastHit.point is a worldspace point. However when you create a mesh you need local space positions since the mesh is automatically rotated with the object that is rendering it.
     
  3. Accurina

    Accurina

    Joined:
    Apr 1, 2021
    Posts:
    4
    Hi, thank you for replying.

    After reading your advice and asking my friend, I've changed the Vector3 GetVectorFromAngle(float angle) to:


    Code (CSharp):
    1.     Vector3 GetVectorFromAngle(float angle)
    2.     {
    3.         float angleRad = angle * (Mathf.PI / 180f);
    4.         Vector3 test = new Vector3(Mathf.Cos(angleRad), 0, Mathf.Sin(angleRad));
    5.         Vector3 test2 = transform.TransformDirection(test);
    6.         return test2;
    7.      
    8.     }
    and for the raycastHit.point, i've added the transform.TransformDirection() to turn it to:

    Code (CSharp):
    1.             if (Physics.Raycast(MainBodyPos, GetVectorFromAngle(angle), out raycastHit, viewDistance, GroundLayerMask)) //  PROBLEM HERE, STARTING ANGLE ISN'T AT VECTOR3.FORWARD, BUT IT STARTS AT WHATEVER THE MAIN BODY ROTATION IS
    2.             {
    3.                 Transform test = MainBodyGO.transform;
    4.                 Vertex = test.InverseTransformPoint(raycastHit.point);
    5.             }
    6.             else
    7.             {
    8.                 Vertex = Vector3.zero + GetVectorFromAngle(angle) * viewDistance;
    9.             }

    For object collision the ray works fine, the only issue now that I have is that when the object rotates, when the object is facing the right, the mesh generated points backwards




    and when the object is facing the left, the mesh generated is funky to say the least




    Could this be an issue with transform.InverseTransformPoint() or should I be using transform.TransformDirection() instead?

    Thank you.
     
  4. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,571
    Well, since you change your "GetVectorFromAngle" method to now return a worldspace vector, you again use the wrong one in the "else" case:

    Code (CSharp):
    1. Vertex = Vector3.zero + GetVectorFromAngle(angle) * viewDistance;
    Again, the vertices have to be in local space. Only the raycast (which is not related to your object at all) requires a worldspace direction.

    We don't know what your StartingAngle and AngleOfView is. Keep in mind that cosine and sine with an angle of 0 would start at the "right" direction and go counter clockwise around the y axis when viewed from above.
     
  5. Accurina

    Accurina

    Joined:
    Apr 1, 2021
    Posts:
    4
    Hi, thank you for your help, however the only thing I don't understand is what you meant by
    Since I've tried adding
    Code (CSharp):
    1. float angleRad = (angle + MainBodyGO.transform.rotation.eulerAngles.y) * (Mathf.PI / 180f);
    instead but it wouldn't be correct, unless you meant by the mesh generated would be incorrect when the Y axis is at any value but 0.

    For the separation of returning world space and local space vectors, I've separated the functions into two seperate ones here

    Code (CSharp):
    1.     Vector3 GetVectorFromAngleWorldSpace(float angle)
    2.     {
    3.         float angleRad = (angle ) * (Mathf.PI / 180f);
    4.         Vector3 GetVectorFromAngle = new Vector3(Mathf.Cos(angleRad), 0, Mathf.Sin(angleRad));
    5.         Vector3 VectorInWorldSpace = transform.TransformDirection(GetVectorFromAngle);
    6.         return VectorInWorldSpace;
    7.      
    8.     }
    9.  
    10.     Vector3 GetVectorFromAngleLocalSpace(float angle)
    11.     {
    12.         float angleRad = (angle) * (Mathf.PI / 180f);
    13.         Vector3 VectorFromAngleLocalSpace = new Vector3(Mathf.Cos(angleRad), 0, Mathf.Sin(angleRad));
    14.         return VectorFromAngleLocalSpace;
    15.     }
    16.  
    and then I've added them to the main script in update here

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class IFU_UnitFieldOfFireVision : MonoBehaviour // this is attached to a child GO
    4. {
    5.     public IFU_Entity EntityData;
    6.     [Header("Field of View Generator")]
    7.     public float AngleOfView;
    8.     public int rayCount;
    9.     public float ViewDistance;
    10.     public LayerMask GroundLayerMask;
    11.  
    12.     //public Vector3 ThisObjectTransformPos;
    13.     public GameObject MainBodyGO;
    14.  
    15.     float StartingAngle;
    16.     Mesh mesh;
    17.     Vector3 MainBodyPos;
    18.  
    19.  
    20.  
    21.     // Start is called before the first frame update
    22.     void Start()
    23.     {
    24.         SetOrigin(MainBodyGO.transform.position);
    25.  
    26.         // to get unit data, if no unit data is present (testing purposes) it uses the number placed in the inspector inside this script
    27.         EntityData = this.gameObject.GetComponentInParent<IFU_Entity>();
    28.         if (EntityData != null)
    29.         {
    30.             AngleOfView = EntityData.AngleOfAttack;
    31.             ViewDistance = EntityData.AttackRangeRadius;
    32.             Debug.Log("set new pos");
    33.         }
    34.  
    35.         mesh = new Mesh();
    36.         GetComponent<MeshFilter>().mesh = mesh;
    37.         //Origin = Vector3.zero;
    38.     }
    39.  
    40.     private void Update()
    41.     {
    42.         SetOrigin(MainBodyGO.transform.position);
    43.         SetAimDirection(MainBodyGO.transform.rotation.eulerAngles); //  PROBLEM HERE, ITS USING THE TRANSFORM.POSITION OF OBJECT, WHICH IS GOOD BUT IT DOESN'T
    44.         MeshGen();
    45.  
    46.         if (Input.GetKeyDown(KeyCode.A))
    47.         {
    48.             Debug.Log("WorldSpace" + GetVectorFromAngleWorldSpace(StartingAngle));
    49.  
    50.             Debug.Log("LocalSpace " + GetVectorFromAngleLocalSpace(StartingAngle));
    51.  
    52.             Debug.Log(StartingAngle + " starting angle");
    53.  
    54.             Debug.Log("MainBodyGO.transform.rotation.eulerAngle " + MainBodyGO.transform.rotation.eulerAngles );
    55.         }
    56.     }
    57.  
    58.     // Update is called once per frame
    59.     void LateUpdate()
    60.     {
    61.      
    62.  
    63.  
    64.    
    65.  
    66.     }
    67.  
    68.     public void MeshGen()
    69.     {
    70.         float fov = AngleOfView;
    71.  
    72.         int raycount = rayCount;
    73.         float angle = StartingAngle;
    74.         float angleIncrease = fov / raycount;
    75.         float viewDistance = ViewDistance;
    76.  
    77.         Vector3[] Vertices = new Vector3[raycount + 1 + 1]; // positioning of points
    78.         Vector2[] uv = new Vector2[Vertices.Length]; // texture rendered - vector 2 as the image it references is flat 2d so it uses vector 2 only
    79.         int[] triangles = new int[raycount * 3]; // actual points of the mesh
    80.  
    81.         Vertices[0] = Vector3.zero; // same as above, mesh origin is at this transform's position
    82.  
    83.         int vertexIndex = 1; // 0 is the origin
    84.         int triangleIndex = 0;
    85.         for (int i = 0; i <= raycount; i++)
    86.         {
    87.             Vector3 Vertex = Vector3.zero + GetVectorFromAngleLocalSpace(angle) * viewDistance;
    88.  
    89.             RaycastHit raycastHit;
    90.  
    91.             if (Physics.Raycast(MainBodyPos, GetVectorFromAngleWorldSpace(angle), out raycastHit, viewDistance, GroundLayerMask)) //  PROBLEM HERE, STARTING ANGLE ISN'T AT VECTOR3.FORWARD, BUT IT STARTS AT WHATEVER THE MAIN BODY ROTATION IS
    92.             {
    93.                 Transform test = MainBodyGO.transform;
    94.                 Vertex = test.InverseTransformPoint(raycastHit.point);
    95.             }
    96.             else
    97.             {
    98.                 Transform test = MainBodyGO.transform;
    99.                 Vertex = Vector3.zero + GetVectorFromAngleLocalSpace(angle) * viewDistance;
    100.             }
    101.  
    102.             Vertices[vertexIndex] = Vertex;
    103.  
    104.  
    105.             if (i > 0)
    106.             {
    107.                 triangles[triangleIndex + 0] = 0;
    108.                 triangles[triangleIndex + 1] = vertexIndex - 1;
    109.                 triangles[triangleIndex + 2] = vertexIndex;
    110.  
    111.                 triangleIndex += 3;
    112.             }
    113.  
    114.             vertexIndex++;
    115.             angle -= angleIncrease; // goes counter clockwise if +, - for anti clockwise
    116.         }
    117.  
    118.         mesh.vertices = Vertices;
    119.         mesh.uv = uv;
    120.         mesh.triangles = triangles;
    121.     }
    122.  
    123.     Vector3 GetVectorFromAngleWorldSpace(float angle)
    124.     {
    125.         float angleRad = (angle ) * (Mathf.PI / 180f);
    126.         Vector3 GetVectorFromAngle = new Vector3(Mathf.Cos(angleRad), 0, Mathf.Sin(angleRad));
    127.         Vector3 VectorInWorldSpace = transform.TransformDirection(GetVectorFromAngle);
    128.         return VectorInWorldSpace;
    129.      
    130.     }
    131.  
    132.     Vector3 GetVectorFromAngleLocalSpace(float angle)
    133.     {
    134.         float angleRad = (angle) * (Mathf.PI / 180f);
    135.         Vector3 VectorFromAngleLocalSpace = new Vector3(Mathf.Cos(angleRad), 0, Mathf.Sin(angleRad));
    136.         return VectorFromAngleLocalSpace;
    137.     }
    138.  
    139.     float GetAngleFromVectorFloat(Vector3 dir)
    140.     {
    141.         dir = dir.normalized;
    142.         float n = Mathf.Atan2(dir.z, dir.x) * Mathf.Rad2Deg;
    143.         if (n < 0)
    144.         {
    145.             n += 360;
    146.         }
    147.  
    148.         return n;
    149.     }
    150.     public void SetOrigin(Vector3 NewOrigin)
    151.     {
    152.         MainBodyPos = NewOrigin;
    153.     }
    154.  
    155.     public void SetAimDirection (Vector3 aimDirection)
    156.     {
    157.         StartingAngle = GetAngleFromVectorFloat(aimDirection) - (AngleOfView/2);
    158.     }
    159. }
    Wouldn't the problem be in the GetAngleFromVectorFloat(Vector3 dir), SetAimDirection or SetOrigin functions instead? Since the starting angle of the ray is not at the center of the game object but is tilted to the side.

     
  6. Accurina

    Accurina

    Joined:
    Apr 1, 2021
    Posts:
    4
    Ok, I have managed to solve it with help from friends.

    Their comments to solve it are:


    The problem of the code from OP is the wrong math solution. You cannot use trigonometric functions on more than 90 degrees, the math will break down. The solution is to use Quaternion or Matrix math instead - Best bet is quaternion as unity has a lot of functions for that.

    You will need to work with rotations (quaternions). Then just do a quaternion transform to a Vector3.forward you will get a correct position.Try to do a transform.rotateaxis. Then do PointPos = transform.rotation * Vector3.forward.

    the final code is:

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class IFU_UnitFieldOfFireVision : MonoBehaviour // this is attached to a child GO
    4. {
    5.     public IFU_Entity EntityData;
    6.     [Header("Field of View Generator")]
    7.     public float AngleOfView;
    8.     public int rayCount;
    9.     public float ViewDistance;
    10.     public LayerMask GroundLayerMask;
    11.  
    12.     public GameObject MainBodyGO;
    13.  
    14.     public float StartingAngle;
    15.     Mesh mesh;
    16.     Vector3 MainBodyPos;
    17.  
    18.     // Start is called before the first frame update
    19.     void Start()
    20.     {
    21.         SetOrigin(MainBodyGO.transform.position);
    22.  
    23.         // to get unit data, if no unit data is present (testing purposes) it uses the number placed in the inspector inside this script
    24.         EntityData = this.gameObject.GetComponentInParent<IFU_Entity>();
    25.         if (EntityData != null)
    26.         {
    27.             AngleOfView = EntityData.AngleOfAttack;
    28.             ViewDistance = EntityData.AttackRangeRadius;
    29.             rayCount = Mathf.FloorToInt(AngleOfView) * 4;
    30.         }
    31.  
    32.         mesh = new Mesh();
    33.         GetComponent<MeshFilter>().mesh = mesh;
    34.     }
    35.  
    36.  
    37.     // Update is called once per frame
    38.     private void Update()
    39.     {
    40.         SetOrigin(MainBodyGO.transform.position);
    41.         ReturnStartingAngle();
    42.         MeshGenQuarternionCalcs();
    43.     }
    44.  
    45.  
    46.  
    47.     // ========================================================================================================
    48.  
    49.     public void SetOrigin(Vector3 NewOrigin)
    50.     {
    51.         MainBodyPos = NewOrigin;
    52.     }
    53.  
    54.     public void ReturnStartingAngle()
    55.     {
    56.         StartingAngle = MainBodyGO.transform.localRotation.eulerAngles.y;
    57.     }
    58.  
    59.     public void MeshGenQuarternionCalcs()
    60.     {
    61.         float fov = AngleOfView;
    62.  
    63.         int raycount = rayCount;
    64.         float meshAngle = StartingAngle + fov/2;
    65.         float angleIncrease = fov / raycount;
    66.         float viewDistance = ViewDistance;
    67.  
    68.         Vector3[] Vertices = new Vector3[raycount + 1 + 1]; // positioning of points
    69.         Vector2[] uv = new Vector2[Vertices.Length]; // texture rendered - vector 2 as the image it references is flat 2d so it uses vector 2 only
    70.         int[] triangles = new int[raycount * 3]; // actual points of the mesh
    71.  
    72.         Vertices[0] = Vector3.zero; // same as above, mesh origin is at this transform's position
    73.  
    74.         int vertexIndex = 1; // 0 is the origin
    75.         int triangleIndex = 0;
    76.         for (int i = 0; i <= raycount; i++)
    77.         {
    78.  
    79.             Vector3 Vertex = Vector3.zero;
    80.  
    81.             RaycastHit raycastHit;
    82.  
    83.             if (Physics.Raycast(MainBodyPos, PointCalcWorldSpace(meshAngle), out raycastHit, viewDistance, GroundLayerMask))
    84.             {
    85.                 Transform MainBodyTransform = MainBodyGO.transform;
    86.                 Vertex = MainBodyTransform.InverseTransformPoint(raycastHit.point);
    87.             }
    88.             else
    89.             {
    90.                 Vertex = Vector3.zero + PointCalc(meshAngle);
    91.             }
    92.  
    93.             Vertices[vertexIndex] = Vertex;
    94.  
    95.  
    96.             if (i > 0)
    97.             {
    98.                 triangles[triangleIndex + 0] = 0;
    99.                 triangles[triangleIndex + 1] = vertexIndex - 1;
    100.                 triangles[triangleIndex + 2] = vertexIndex;
    101.  
    102.                 triangleIndex += 3;
    103.             }
    104.  
    105.             vertexIndex++;
    106.             meshAngle -= angleIncrease; // goes counter clockwise if +, - for anti clockwise
    107.         }
    108.  
    109.         mesh.vertices = Vertices;
    110.         mesh.uv = uv;
    111.         mesh.triangles = triangles;
    112.     }
    113.     Vector3 PointCalc(float angle)
    114.     {
    115.         Quaternion pointRot = MainBodyGO.transform.rotation * Quaternion.AngleAxis(angle, -Vector3.up);
    116.         Vector3 Point = pointRot * Vector3.forward * ViewDistance;
    117.  
    118.         return Point;
    119.     }
    120.  
    121.     Vector3 PointCalcWorldSpace(float angle)
    122.     {
    123.         Quaternion pointRot = MainBodyGO.transform.rotation * Quaternion.AngleAxis(angle, -Vector3.up);
    124.         Vector3 Point = pointRot * Vector3.forward * ViewDistance;
    125.         Vector3 VectorInWorldSpace = transform.TransformDirection(Point);
    126.         return VectorInWorldSpace;
    127.     }
    Once again, thank you everyone who helped me with this.