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
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Convert 3D Cone of View into 2.5D

Discussion in 'Scripting' started by Abdou23, May 16, 2017.

  1. Abdou23

    Abdou23

    Joined:
    May 2, 2014
    Posts:
    77
    I got this script from an online tutorial ( courtesy of Sebastian Lague) and it works great for a 3D Top down game, But now I'm converting my concept into 2.5D But I can't figure out how to exactly how to make it work on that perspective. I would appreciate any kind of help.


    Code (CSharp):
    1.  
    2.  
    3. public float viewRadius;
    4.  
    5. [Range(0,360)]
    6. public float viewAngle;
    7. public LayerMask targetMask;
    8. public LayerMask obstacleMask;
    9.  
    10. [HideInInspector]
    11. public List<Transform> visibleTargets = new List<Transform>();
    12. public float meshResolution;
    13. public int edgeResolveIterations;
    14. public float edgeDstThreshold;
    15. public MeshFilter viewMeshFilter;
    16. Mesh viewMesh;
    17.  
    18. void Start() {
    19.     viewMesh = new Mesh ();
    20.     viewMesh.name = "View Mesh";
    21.     viewMeshFilter.mesh = viewMesh;
    22.     StartCoroutine ("FindTargetsWithDelay", .2f);
    23. }
    24.  
    25. IEnumerator FindTargetsWithDelay(float delay) {
    26.     while (true) {
    27.         yield return new WaitForSeconds (delay);
    28.         FindVisibleTargets ();
    29.     }
    30. }
    31.  
    32. void LateUpdate() {
    33.     DrawFieldOfView ();
    34. }
    35.  
    36. void FindVisibleTargets() {
    37.     visibleTargets.Clear ();
    38.     Collider[] targetsInViewRadius = Physics.OverlapSphere (transform.position, viewRadius, targetMask);
    39.     for (int i = 0; i < targetsInViewRadius.Length; i++) {
    40.         Transform target = targetsInViewRadius [i].transform;
    41.         Vector3 dirToTarget = (target.position - transform.position).normalized;
    42.         if (Vector3.Angle (transform.forward, dirToTarget) < viewAngle / 2) {
    43.             float dstToTarget = Vector3.Distance (transform.position, target.position);
    44.             if (!Physics.Raycast (transform.position, dirToTarget, dstToTarget, obstacleMask)) {
    45.                 visibleTargets.Add (target);
    46.              }
    47.         }
    48.     }
    49. }
    50.  
    51. void DrawFieldOfView() {
    52.     int stepCount = Mathf.RoundToInt(viewAngle * meshResolution);
    53.     float stepAngleSize = viewAngle / stepCount;
    54.     List<Vector3> viewPoints = new List<Vector3> ();
    55.     ViewCastInfo oldViewCast = new ViewCastInfo ();
    56.     for (int i = 0; i <= stepCount; i++) {
    57.         float angle = transform.eulerAngles.y - viewAngle / 2 + stepAngleSize * i;
    58.         ViewCastInfo newViewCast = ViewCast (angle);
    59.         if (i > 0) {
    60.             bool edgeDstThresholdExceeded = Mathf.Abs (oldViewCast.dst - newViewCast.dst) > edgeDstThreshold;
    61.             if (oldViewCast.hit != newViewCast.hit || (oldViewCast.hit && newViewCast.hit && edgeDstThresholdExceeded)) {
    62.                 EdgeInfo edge = FindEdge (oldViewCast, newViewCast);
    63.                 if (edge.pointA != Vector3.zero) {
    64.                      viewPoints.Add (edge.pointA);
    65.                 }
    66.                 if (edge.pointB != Vector3.zero) {
    67.                     viewPoints.Add (edge.pointB);
    68.                 }
    69.             }
    70.         }
    71.         viewPoints.Add (newViewCast.point);
    72.         oldViewCast = newViewCast;
    73.     }
    74.     int vertexCount = viewPoints.Count + 1;
    75.     Vector3[] vertices = new Vector3[vertexCount];
    76.     int[] triangles = new int[(vertexCount-2) * 3];
    77.     vertices [0] = Vector3.zero;
    78.     for (int i = 0; i < vertexCount - 1; i++) {
    79.         vertices [i + 1] = transform.InverseTransformPoint(viewPoints [i]);
    80.         if (i < vertexCount - 2) {
    81.             triangles [i * 3] = 0;
    82.             triangles [i * 3 + 1] = i + 1;
    83.             triangles [i * 3 + 2] = i + 2;
    84.         }
    85.     }
    86.     viewMesh.Clear ();
    87.     viewMesh.vertices = vertices;
    88.     viewMesh.triangles = triangles;
    89.     viewMesh.RecalculateNormals ();
    90. }
    91.  
    92. EdgeInfo FindEdge(ViewCastInfo minViewCast, ViewCastInfo maxViewCast) {
    93.     float minAngle = minViewCast.angle;
    94.     float maxAngle = maxViewCast.angle;
    95.     Vector3 minPoint = Vector3.zero;
    96.     Vector3 maxPoint = Vector3.zero;
    97.     for (int i = 0; i < edgeResolveIterations; i++) {
    98.         float angle = (minAngle + maxAngle) / 2;
    99.         ViewCastInfo newViewCast = ViewCast (angle);
    100.         bool edgeDstThresholdExceeded = Mathf.Abs (minViewCast.dst - newViewCast.dst) >     edgeDstThreshold;
    101.         if (newViewCast.hit == minViewCast.hit && !edgeDstThresholdExceeded) {
    102.         minAngle = angle;
    103.         minPoint = newViewCast.point;
    104.         } else {
    105.             maxAngle = angle;
    106.             maxPoint = newViewCast.point;
    107.         }
    108.     }
    109.     return new EdgeInfo (minPoint, maxPoint);
    110. }
    111.  
    112. ViewCastInfo ViewCast(float globalAngle) {
    113.     Vector3 dir = DirFromAngle (globalAngle, true);
    114.     RaycastHit hit;
    115.     if (Physics.Raycast (transform.position, dir, out hit, viewRadius, obstacleMask)) {
    116.         return new ViewCastInfo (true, hit.point, hit.distance, globalAngle);
    117.     } else {
    118.         return new ViewCastInfo (false, transform.position + dir * viewRadius, viewRadius, globalAngle);
    119.     }
    120. }
    121.  
    122. public Vector3 DirFromAngle(float angleInDegrees, bool angleIsGlobal) {
    123.     if (!angleIsGlobal) {
    124.         angleInDegrees += transform.eulerAngles.y;
    125.     }
    126.     return new Vector3(Mathf.Sin(angleInDegrees * Mathf.Deg2Rad),0,Mathf.Cos(angleInDegrees * Mathf.Deg2Rad));
    127. }
    128.  
    129. public struct ViewCastInfo {
    130.     public bool hit;
    131.     public Vector3 point;
    132.     public float dst;
    133.     public float angle;
    134.     public ViewCastInfo(bool _hit, Vector3 _point, float _dst, float _angle) {
    135.         hit = _hit;
    136.         point = _point;
    137.         dst = _dst;
    138.         angle = _angle;
    139.     }
    140. }
    141.  
    142. public struct EdgeInfo {
    143.     public Vector3 pointA;
    144.     public Vector3 pointB;
    145.     public EdgeInfo(Vector3 _pointA, Vector3 _pointB) {
    146.         pointA = _pointA;
    147.         pointB = _pointB;
    148.     }
    149. }
    150.  
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,797
    The good news is that everything in Unity3D is actually 3D, even the 2D stuff.

    You can have a perspective camera (cone projection) or orthogonal (no vanishing point).

    Your only issue would be to get the camera positioned so that it faces the way you want, and the things you want to see are within the viewable frustum of the camera. If the above script does that, you're golden.

    If there are different constraints and/or directions, you can just start your game, press pause, and figure out the camera interactively in the editor, then write a script to drive the camera to those relative offsets. Unity is super easy that way.
     
  3. Abdou23

    Abdou23

    Joined:
    May 2, 2014
    Posts:
    77
    If I understand you correctly, you are saying that I could move the camera on top of the objects to make it appear as if it's 2.5D. But that would mess up several things, for example, the Y axis would actually be the Z axis, and the gravity would not be right.
    Before I stumbled upon this code I was actually using and a cone shaped object positioned in front of the enemy to detect the player, and if it hit him I would use a raycast to make sure there are no obstacles between them, the problem was that the cone would pass through objects which didn't look very good or professional.