자동으로 길 찾아가는 케릭터 구현하기


2017/10/16 - [Korean/유니티] - 유니티 길만들기 #1 Snap 사용하기 (Grid Cell 만큼 움직이기)

2017/10/17 - [Korean/유니티] - 유니티 길만들기 #2 Navigation을 이용한 길 Bake 사용방법

2017/10/18 - [Korean/유니티] - 유니티 길만들기 #3 자동길찾기 케릭터 구현하기


이번에는 지난번 포스팅에서 만들어놓은 길 위를 걸어다니는 케릭터를 구현해 볼까 합니다. 이 구현코드에 대한 자세한 설명하지 않도록 하겠습니다. 자세한 설명이 필요하다면 유니티 홈페이지 교육코너의 동영상을 참고하시면 될 것 같습니다. 여기에 사용된 코드는 그 강좌에서 필요한 만큼만 잘라내서 사용했습니다.


참고 교육과정: https://unity3d.com/kr/learn/tutorials/projects/adventure-game-tutorial



자 이제 자동길찾기 구현을 시작해 보겠습니다.


Step.1 케릭터를 생성합니다.



위의 화면처럼 케릭터를 하나 생성하였습니다. 


생성과정은 


1. 먼저 GameObject를 하나 만든 다음에 


2. Character.cs (글 하단 첨부파일참조) 스크립트를 넣어주시고,


3. 'Nav Mesh Agent' 컴포넌트도 추가해 줍니다. 


4. GameObject 이름을 Character로 변경해 줍니다. (물론 이렇게 변경하지 않으셔도 상관없어요) 


5. 마지막으로 케릭터 모델을 하나 만들어서 저 Character 노드 밑에 넣어주시면 끝! 


케릭터 모델이 없으면 그냥 원통이나 사각형을 해도 되겠지요.


  • Nav Mesh Agent: 요녀석이 실질적으로 Bake된 길을 따라서 움직일 수 있도록 만드는 컴포넌트 입니다. 여기에 보면 Speed, Angular Speed, Accelerations 등등 다양한 프로퍼티들이 있는데 요런것들은 유니티 API 레퍼런스를 참조해 보시면 이해가 빠르실꺼에요.


Step.2 케릭터에게 길이 클릭되었다는 이벤트를 받을 수 있는 함수를 등록합니다.




먼저 길에다가 클릭 이벤트를 등록해야되는데 이전 포스팅에서 만들어 두었던 길(화면상의 Ground)을 선택하시고  'Event Trigger' 컴포넌트를 넣어줍니다. Event Trigger에서 'Add New Event' 버튼을 클릭하시고 'PointerClick'을 선택합니다.



Event Trigger 창의 하단에 보면 + 버튼을 누르고 Object 에는 Character를 넣어주시고, 실행할 함수는 'OnGroundClick' 함수를 선택해 줍니다.



요기까지하면 길이 클릭되면 케릭터에게 이벤트를 전달할 수 있습니다.


이제 남은건 길이 클릭되었다는 이벤트를 발생시켜야하는데 그부분은 카메라에 넣어주면됩니다.


Step.3 카메라에 클릭 이벤트 등록하기



메인 카메라를 선택하시고 'Physics Raycaster' 스크립트를 추가해줍니다.  엥? 이게다임? 간단하네요.



Step.4 마지막 단계 Event System 등록하기


말은 거창한데, Event System을 생성시켜주면 끝! 화면아래처럼, Hierarchy 창에서 마우스 오른쪽을 누르고 UI > Event System 요걸 클릭해주면됩니다.



끝끝끝! 실행 버튼을 눌러보세요. 지금가지의 과정을 잘 따라왔다면 길 위를 클릭하면 케릭터가 길을 따라서 움직이는걸 볼 수 있습니다. 안된다면 차근차근 다시 살펴보세요. 어디가 잘못되었는지.


지금까지의 과정을 녹화한 동영상입니다. 이해가 잘 안되시면 참고해 보세요. (물론 동영상은 아무론 설명도 없이 1분 20초만에 동안 시현만 하다가 끝나니까 중간중간 잘 멈추고 보셔야될꺼에요)






케릭터 스크립트 파일 다운받기: Character.cs


//---------------------------------------------------------------- Unity Engine

using UnityEngine;

using UnityEngine.AI;

using UnityEngine.EventSystems;



public class Character : MonoBehaviour

{

    private NavMeshAgent agent;

    private Vector3 destinationPosition;


    // Nav mesh agent

    private float turnSmoothing = 15f;

    private float turnSpeedThreshold = 0.5f;

    private const float stopDistanceProportion = 0.1f;

    private const float navMeshSampleDistance = 4f;



    private void Start()

    {

        agent = GetComponent<NavMeshAgent>();


        agent.gameObject.SetActive(false);

        destinationPosition = transform.position;

        agent.gameObject.SetActive(true);

        agent.updateRotation = false;


        agent.speed = StatManager.stat.speed;


    }


    //-----------------------------------------------------------------------------

    // Update

    //-----------------------------------------------------------------------------

    private void Update()

    {

        if (agent.pathPending)

            return;


        if (agent.remainingDistance <= agent.stoppingDistance * stopDistanceProportion)

            Stopping();

        else if (agent.desiredVelocity.magnitude > turnSpeedThreshold)

            Heading();


    }



    //-----------------------------------------------------------------------------

    // Stopping

    //-----------------------------------------------------------------------------

    private void Stopping()

    {

        if (agent.isStopped)

            return;


        agent.isStopped = true;

    }


    //-----------------------------------------------------------------------------

    // Heading

    //-----------------------------------------------------------------------------

    private void Heading()

    {

        Quaternion targetRotation;

        targetRotation = Quaternion.LookRotation(agent.desiredVelocity);

        transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, turnSmoothing * Time.deltaTime);

    }


    //-----------------------------------------------------------------------------

    // OnGroundClick

    //-----------------------------------------------------------------------------

    public void OnGroundClick(BaseEventData data)

    {

        PointerEventData pData = (PointerEventData)data;


        NavMeshHit hit;

        if (NavMesh.SamplePosition(pData.pointerCurrentRaycast.worldPosition, out hit, navMeshSampleDistance, NavMesh.AllAreas))

            destinationPosition = hit.position;

        else

            destinationPosition = pData.pointerCurrentRaycast.worldPosition;


        agent.SetDestination(destinationPosition);

        agent.isStopped = false;

    }

}



2017/10/16 - [Korean/유니티] - 유니티 길만들기 #1 Snap 사용하기 (Grid Cell 만큼 움직이기)

2017/10/17 - [Korean/유니티] - 유니티 길만들기 #2 Navigation을 이용한 길 Bake 사용방법