본문 바로가기
개발/Unity

유니티 - 네비메쉬 에이전트의 경로를 라인 렌더러로 표시하기 (Draw the Path of NavMesh with LineRender)

by 피로물든딸기 2022. 8. 25.
반응형

깃허브 데스크탑으로 프로젝트 관리하기 강의 오픈!! (인프런 바로가기)

 

Unity 전체 링크

이전 글에서 네비메쉬를 이용하여 Bake로 맵을 구우면 Player가 장애물을 쉽게 피하면서 움직일 수 있었다.


이제 라인 렌더러를 이용하여 Player가 이동하는 경로 그리기를 해보자.

아래와 같이 PathFinder.cs를 수정한다.
goTarget은 삭제하고 makePath로 함수 이름을 변경하였다. (TargetChanger.cs의 goTarget 함수도 이름을 변경)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

public class PathFinder : MonoBehaviour
{
    public GameObject target;
    NavMeshAgent agent;
    LineRenderer lr;

    void Start()
    {
        agent = this.GetComponent<NavMeshAgent>();

        lr = this.GetComponent<LineRenderer>();
        lr.startWidth = lr.endWidth = 0.1f;
        lr.material.color = Color.blue;
        lr.enabled = false;
    }

    public void makePath()
    {
        lr.enabled = true;
        StartCoroutine(makePathCoroutine());
    }

    IEnumerator makePathCoroutine()
    {
        agent.SetDestination(target.transform.position);
        lr.SetPosition(0, this.transform.position);

        yield return new WaitForSeconds(0.1f);

        drawPath();
    }

    void drawPath()
    {
        int length = agent.path.corners.Length;

        lr.positionCount = length;
        for (int i = 1; i < length; i++)
            lr.SetPosition(i, agent.path.corners[i]);
    }
}


Start에서는 LineRenderer의 설정을 초기화한다.

    LineRenderer lr;

    void Start()
    {
        agent = this.GetComponent<NavMeshAgent>();

        lr = this.GetComponent<LineRenderer>();
        lr.startWidth = lr.endWidth = 0.1f;
        lr.material.color = Color.blue;
        lr.enabled = false;
    }


agent의 path.corners에 agent가 이동할 경로가 Vector3[]로 저장되어 있다.
따라서 LineRenderer의 SetPosition으로 경로를 그릴 수 있다.

    void drawPath()
    {
        int length = agent.path.corners.Length;

        lr.positionCount = length;
        for (int i = 1; i < length; i++)
            lr.SetPosition(i, agent.path.corners[i]);
    }


agent가 SetDestination을 호출하자마자 drawPath를 하면 정상적으로 경로를 잡지 못하기 때문에
코루틴을 이용하여 딜레이(0.1초)를 준 다음 경로를 그린다.
그 전에 LineRender에 최초 출발점을 Setting 한다.

    IEnumerator makePathCoroutine()
    {
        agent.SetDestination(target.transform.position);
        lr.SetPosition(0, this.transform.position);

        yield return new WaitForSeconds(0.1f);

        drawPath();
    }


이제 makePath를 TargetChanger가 호출하면 된다.

    public void makePath()
    {
        lr.enabled = true;
        StartCoroutine(makePathCoroutine());
    }


Player에 LineRenderer를 추가하고 게임을 실행해보자.


이제 도착점이 바뀔 때마다 경로가 그려진다.


위와 같이 속도가 빠르면 경로를 벗어나기도 하므로, 적절히 NavMeshAgent의 Speed 값을 조절하자.


지나간 경로 삭제하기

위의 경우는 라인 렌더러 경로계속 남아 있어서 보기 불편할 수 있다.
따라서 지나간 경로는 사라지게 해보자.

makePathCoroutine을 아래와 같이 수정하자.
현재 위치와 도착점 사이의 거리가 충분히 줄어들 때까지 LineRenderer를 새로 그리면 된다.

    IEnumerator makePathCoroutine()
    {
        agent.SetDestination(target.transform.position);
        lr.SetPosition(0, this.transform.position);

        while (Vector3.Distance(this.transform.position, target.transform.position) > 0.1f)
        {
            lr.SetPosition(0, this.transform.position);
            
            drawPath();

            yield return null;
        }

        lr.enabled = false;
    }


이제 지나간 경로는 사라지는 것처럼 보이게 된다.


최종 코드는 다음과 같다.

PathFinder.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

public class PathFinder : MonoBehaviour
{
    public GameObject target;
    NavMeshAgent agent;
    LineRenderer lr;

    void Start()
    {
        agent = this.GetComponent<NavMeshAgent>();

        lr = this.GetComponent<LineRenderer>();
        lr.startWidth = lr.endWidth = 0.1f;
        lr.material.color = Color.blue;
        lr.enabled = false;
    }

    public void makePath()
    {
        lr.enabled = true;
        StartCoroutine(makePathCoroutine());
    }

    void drawPath()
    {
        int length = agent.path.corners.Length;

        lr.positionCount = length;
        for (int i = 1; i < length; i++)
            lr.SetPosition(i, agent.path.corners[i]);
    }

    IEnumerator makePathCoroutine()
    {
        agent.SetDestination(target.transform.position);
        lr.SetPosition(0, this.transform.position);

        while (Vector3.Distance(this.transform.position, target.transform.position) > 0.1f)
        {
            lr.SetPosition(0, this.transform.position);
            
            drawPath();

            yield return null;
        }

        lr.enabled = false;
    }
}


TargetChanger.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TargetChanger : MonoBehaviour
{
    RaycastHit hit;
    public GameObject player;

    void Update()
    {
        if (Input.GetMouseButtonUp(0))
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            Debug.DrawRay(ray.origin, ray.direction * 100.0f, Color.red);

            if (Physics.Raycast(ray, out hit, Mathf.Infinity))
            {
                this.transform.position = hit.point;
                player.GetComponent<PathFinder>().makePath();
            }
        }
    }
}

 

Unity Plus:

 

Easy 2D, 3D, VR, & AR software for cross-platform development of games and mobile apps. - Unity Store

Have a 2D, 3D, VR, or AR project that needs cross-platform functionality? We can help. Take a look at the easy-to-use Unity Plus real-time dev platform!

store.unity.com

 

Unity Pro:

 

Unity Pro

The complete solutions for professionals to create and operate.

unity.com

 

Unity 프리미엄 학습:

 

Unity Learn

Advance your Unity skills with live sessions and over 750 hours of on-demand learning content designed for creators at every skill level.

unity.com

반응형

댓글