Handles와DrawGizmos 활용해서 시야각만들기

2022. 7. 12. 05:42유니티 unity

구현영상

 

 

 

 

 

코드 보기!

더보기
public float Radius = 5f;
    [Range(0, 360)] 
    public float Angle = 90f;
       
    public LayerMask targetMask; // 적을 검색하기 위한 레이어마스크
    public LayerMask walllMask; // 장애물 마스크
       
    public List<Transform> Enemies = new List<Transform>(); // 범위안에있는 적들

    void FindEnemy()
    {

        Enemies.Clear(); // 맨처음에 리스트를 생성하고 clear로 정리한다.
        Collider[] results = new Collider[10];//최대10개까지 
        var size = Physics.OverlapSphereNonAlloc(transform.position, Radius, results, targetMask);
        //OverlapSphereNonAlloc 를 사용해서 최적화
        
        for (int i = 0; i < size; ++i)
        {
            Transform enemy = results[i].transform;

            Vector3 dirToTarget = (enemy.position - transform.position).normalized; // 방향구하기
            if (Vector3.Angle(transform.forward, dirToTarget) < Angle / 2)
            {
                float dstToTarget = Vector3.Distance(transform.position, enemy.position);
                if (!Physics.Raycast(transform.position, dirToTarget, dstToTarget, walllMask)) // 벽검사
                {
                    //아닐경우 리스트에 넣는다.
                    Enemies.Add(enemy);

                }
            }
        }
    }

    private void OnDrawGizmosSelected()
    {
        FindEnemy(); // 적 찾기
        Handles.DrawWireArc(transform.position, Vector3.up, Vector3.forward, 360, Radius); //3d공간에 circular 그리기 
        float angle = -Angle * 0.5f + transform.eulerAngles.y; //왼쪽
        float angle2 = Angle * 0.5f + transform.eulerAngles.y;//오른쪽
        Vector3 AngleLeft = new Vector3(Mathf.Sin( angle* Mathf.Deg2Rad), 0, Mathf.Cos(angle * Mathf.Deg2Rad)); // 왼쪽
        Vector3 AngleRight = new Vector3(Mathf.Sin(angle2 * Mathf.Deg2Rad), 0, Mathf.Cos(angle2 * Mathf.Deg2Rad)); // 오른쪽
        
        Handles.color=Color.green;
        
        Handles.DrawLine(transform.position, transform.position + AngleLeft * Radius);
        Handles.DrawLine(transform.position, transform.position + AngleRight * Radius);
        Handles.color = Color.red;
        foreach (Transform enemy in Enemies)
        {
            Handles.DrawLine(transform.position, enemy.position);
        }
    }

 

 

 

https://docs.unity3d.com/2022.2/Documentation/ScriptReference/Gizmos.html

 

Unity - Scripting API: Gizmos

Success! Thank you for helping us improve the quality of Unity Documentation. Although we cannot accept all submissions, we do read each suggested change from our users and will make updates where applicable. Close

docs.unity3d.com

https://docs.unity3d.com/ScriptReference/Handles.html

 

Unity - Scripting API: Handles

Handles are the 3D controls that Unity uses to manipulate items in the Scene view. There are a number of built-in Handle GUIs, such as the familiar tools to position, scale and rotate an object via the Transform component. However, it is also possible to d

docs.unity3d.com

 

 

기즈모나 Handles는 정해진 함수내에서만 사용가능합니다.

https://docs.unity3d.com/2022.2/Documentation/Manual/GizmosAndHandles.html

 

Unity - Manual: Important Classes - Gizmos & Handles

Important Classes - Debug Important Classes - Gizmos & Handles The Gizmos and Handles classes allows you to draw lines and shapes in the SceneA Scene contains the environments and menus of your game. Think of each unique Scene file as a unique level. In ea

docs.unity3d.com

void OnDrawGizmos() // 씬내에서 보임
void OnDrawGizmosSelected() // 오브젝트를 선택시만 보임
void OnSceneGUI() // 씬내에서보임 선택한 오브젝트만

 

 

Gizmos는 여러가지 함수들이 있는데 자주쓰는 것은

 

Color

Gizmos.color = Color.색상;

DrawCube(Vector3 center, Vector3 size)크기와위치에 큐브를 그립니다

DrawLine(Vector3 from, Vector3 to)  선을 그립니다
DrawMesh(Mesh mesh, Vector3 position = Vector3.zero, Quaternion rotation = Quaternion.identity, Vector3 scale = Vector3.one) 

메쉬모양을 그립니다
DrawRay(Ray r),DrawRay(Vector3 from, Vector3 direction) 레이로(방향) 선을 그립니다.
DrawSphere(Vector3 center, float radius) 구체를 그립니다
DrawWireCube(Vector3 center, Vector3 size) 와이어 큐브
DrawWireSphere(Vector3 center, float radius) 와이어구체

 

public static void DrawRay(Vector3 from, Vector3 direction) => Gizmos.DrawLine(from, from + direction);

 

 

 

우선적으로 시야각을 만들려면

 

60분법(Degree) 

일상샐활에서 표현하는 각도 

 

호도법 라디안 radian

 

호의 길이를 이용해서 각도를 표시하는 방법

 

 

출처 - https://mathbang.net/496

 

 

 

 

 

삼각형은 빗변(hypotenuse), 높이(opposite), 밑변(adjacent)으로 구성되어 있다.

그리고 여기서 각 A를 중심으로 사인(sine/sin), 코사인(cosine/cos), 탄젠트(tangent/tan)가 각각 정의된다.

 
 

 

 

 

 

 

	public float Radius = 5f; //범위
    [Range(0, 360)] //인스펙터에서 슬라이더로 표시 
    public float Angle = 90f; //기본값 90도
    public LayerMask targetMask; // 적을 검색하기 위한 레이어마스크
    public LayerMask walllMask; // 장애물 마스크
    public List<Transform> Enemies = new List<Transform>(); // 범위안에있는 적들
    
    
    
void OnDrawGizmosSelected() // 선택된오브젝트만 기즈모그리기
{
	FindEnemy(); // 적 찾기
	//원그리기
	Handles.DrawWireArc(transform.position, Vector3.up, Vector3.forward, 360, Radius);
    
}

 

 

 

만약 60도 시야각을 원한다면 플레이어의 방향기준으로 60/2를 왼쪽 오른쪽으로 그리면 된다.

sin과 cos를 이용해서 각도를 구하고

float angle = -Angle * 0.5f + transform.eulerAngles.y; //왼쪽 왼쪽으로가야하니까 -가붙어있습니다.
float angle2 = Angle * 0.5f + transform.eulerAngles.y;//오른쪽
Vector3 AngleLeft = new Vector3(Mathf.Sin( angle* Mathf.Deg2Rad), 0, Mathf.Cos(angle * Mathf.Deg2Rad)); // 왼쪽
Vector3 AngleRight = new Vector3(Mathf.Sin(angle2 * Mathf.Deg2Rad), 0, Mathf.Cos(angle2 * Mathf.Deg2Rad)); // 오른쪽

Mathf.Deg2Rad =π/180   약 (0.01745329f)

디그리(60분법)에다가 Mathf.Deg2Rad 를 곱하면 라디안(효도법)이 나옵니다.

거기에다가 플레이어의 각도y값를 더하면 된다. 

 

구한 방향으로

Handles.color=Color.green; // 색상변경

Handles.DrawLine(transform.position, transform.position + AngleLeft * Radius); // 왼쪽 그리기
Handles.DrawLine(transform.position, transform.position + AngleRight * Radius); // 오른쪽 그리기

Handles.color = Color.red; // 색상변경


foreach (Transform enemy in Enemies) // 감지한 적들 기즈모 그리기
{
    Handles.DrawLine(transform.position, enemy.position);
}

 

 

적들을 감지할때는 OverlapSphereNonAlloc 를 사용했습니다.

OverlapSphereNonAlloc(Vector3 position, float radius, Collider[] results, int layerMask = AllLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal);

 

범위내에있는 적들을 전부 확인한뒤 

적이있는 방향을 구한뒤

 

Angle함수를이용해서 두 벡터의 사이의 각도를 구한 뒤 각도안에있는적들을 확인하고

경로에 벽을 검사한뒤 벽이없다면 리스트안에 넣습니다.

 

void FindEnemy()
{

    Enemies.Clear(); // 맨처음에 리스트를 생성하고 clear로 정리한다.
    Collider[] results = new Collider[10];//최대10개까지 
    var size = Physics.OverlapSphereNonAlloc(transform.position, Radius, results, targetMask);
    //OverlapSphereNonAlloc 를 사용해서 최적화 targetMask 적들만 감지한다.
    
    for (int i = 0; i < size; ++i)
    {
        Transform enemy = results[i].transform;

        Vector3 dirToTarget = (enemy.position - transform.position).normalized; // 방향구하기
        if (Vector3.Angle(transform.forward, dirToTarget) < Angle / 2)
        {
            float dstToTarget = Vector3.Distance(transform.position, enemy.position);
            if (!Physics.Raycast(transform.position, dirToTarget, dstToTarget, walllMask)) // 벽검사
            {
                //아닐경우 리스트에 넣는다.
                Enemies.Add(enemy);

            }
        }
    }
}

 

 

매프레임마다 검사를 하면 무거울 수 있으니 0.2초 정도마다 돌리면 될 거 같습니다.