Unity/2020

Unity 2D FOV 연출

무명의 공룡 2020. 6. 24. 15:58

본 글은 Code Monkey의 영상을 참고하였습니다. 

https://unitycodemonkey.com/video.php?v=CSeUMTaNFYk

 

Code Monkey - Field of View Effect in Unity (Line of Sight, View Cone)

Field of View Effect in Unity (Line of Sight, View Cone) 13/10/2019 Let's make an awesome Line of Sight effect that interacts with walls, enemies outside the Field of View are not visible. Excellent effect for any sort of Stealth or Horror game.

unitycodemonkey.com

 

 

2D FOV 연출.. 뭐 이런거

2D 게임에서 종종 보이는 방법입니다.

잘만 활용하면 단조로운 맵도 입체감 있게 만들 수 있고 공포 분위기 연출에 훌륭한 방법 중 하나입니다.

 

여러가지 방법이 있겠지만 여기선 매프레임 수십번의 레이를 쏘아 레이 도착점을 이어 메쉬를 그리는 방법입니다.

코드가 쉬운 편이고 퀄리티와 퍼포먼스에 주는 영향도 직관적입니다.

 

위 코드몽키 영상에서 나온 코드에서 살짝 손을 보아 시야각을 그리는 방식은 동일하지만 다루기 더 편하게 만들어본 코드입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
using UnityEngine;
 
[RequireComponent(typeof(MeshFilter))]
[RequireComponent(typeof(MeshRenderer))]
 
public class FieldOfView : MonoBehaviour
{
    [Range(0360)] public float fov = 90f;     //시야각
    public int rayCount = 90;                   //레이의 수 (높을수록 퀄리티↑퍼포먼스↑)
    public float viewDistance = 10f;            //거리
    public LayerMask layerMask;                 //벽으로 인식할 레이어
 
    private Camera mainCamera;                  //카메라
    private Mesh mesh;                          //시야각을 그려줄 메쉬
 
    private void Start()
    {
        mainCamera = Camera.main;
        mesh = new Mesh();
        mesh.name = "FOV_Effect";
        GetComponent<MeshFilter>().mesh = mesh;
    }
 
    private void LateUpdate()
    {
        Vector3 mousePosition = mainCamera.ScreenToWorldPoint(Input.mousePosition);
        transform.eulerAngles = new Vector3(00, GetAngleFromVector(new Vector3(mousePosition.x - transform.position.x, mousePosition.y - transform.position.y, 0)));
        DrawFOV();
    }
 
    private void DrawFOV()
    {
        float angle = fov * 0.5f;
        float angleIncrease = fov / rayCount;
 
        Vector3[] vertices = new Vector3[rayCount + 1 + 1];
        Vector2[] uv = new Vector2[vertices.Length];
        int[] triangles = new int[rayCount * 3];
 
        vertices[0= Vector3.zero;
 
        int vertexIndex = 1;
        int triangleIndex = 0;
        for (int i = 0; i <= rayCount; i++)
        {
            Vector3 vertex;
            RaycastHit2D raycastHit2D = Physics2D.Raycast(transform.position, GetVectorFromAngle(transform.eulerAngles.z + angle), viewDistance, layerMask);
            if (raycastHit2D.collider == null)
            {
                vertex = GetVectorFromAngle(angle) * viewDistance;
            }
            else
            {
                vertex = GetVectorFromAngle(angle) * (raycastHit2D.distance / viewDistance) * viewDistance;
            }
            vertices[vertexIndex] = vertex;
 
            if (0 < i)
            {
                triangles[triangleIndex + 0= 0;
                triangles[triangleIndex + 1= vertexIndex - 1;
                triangles[triangleIndex + 2= vertexIndex;
 
                triangleIndex += 3;
            }
 
            ++vertexIndex;
            angle -= angleIncrease;
        }
 
        mesh.vertices = vertices;
        mesh.uv = uv;
        mesh.triangles = triangles;
    }
    private Vector3 GetVectorFromAngle(float angle)
    {
        float angleRad = angle * (Mathf.PI / 180f);
        return new Vector3(Mathf.Cos(angleRad), Mathf.Sin(angleRad));
    }
    private int GetAngleFromVector(Vector3 dir)
    {
        dir = dir.normalized;
        float n = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
        if (n < 0) n += 360;
        int angle = Mathf.RoundToInt(n);
 
        return angle;
    }
}
cs

 

아래 GetVectorFromAngle, GetAngleFromVector 함수는 원래 코드몽키 유틸리티용 코드에 포함된 함수입니다만 한 눈에 보기 쉬우라고 옮겨놨습니다.

 

rayCount값의 경우 fov와 동일해야 제일 깔끔하게 나오는 것 같습니다.

rayCount가 더 높아도 좋겠지만 그만큼 퍼포먼스가 상승합니다.

 

 

참고로 GIF에서 벽 테두리가 보이는 것은 쉐이더 효과라 위 코드만으론 불가능합니다.