프로젝트/프로젝트 A

#001 Movement (with. NaviMeshAgent, Animator)

효따 2025. 8. 9. 21:34

안녕하세요.

 

이번에 오래된 PC를 처분(?) 하고 새로운 PC로 이것저것 다양한 것들을 설치하다 보니 시간이 훌쩍 지나갔는데요..

 

평일에 못했던 집안일 들과 함께.. 정말 정신없던 하루였던 것 같습니다.😥

 

 

가장 처음으로 프로젝트명을 아직 결정하지 못해 "ProjectA"를 만들어 주었습니다.

(with 2022.3.60 f1, Universal 3D)

 

이전에 즐겨했던 "Leuge Of Legend"라는 게임을 모티브로 제작하기 위해 어떠한 것들을 구성하면 좋을지 생각 중이었는데,

 

 Unity Hub에서 시간이 지나도 프로젝트가 생성이 안되고 무한 로딩이 되는 문제가 있었습니다.

 

조금 찾아보니 저와 같은 경우는 라이선스의 문제였는데요

 

왜 발생했는지는 찾진 못했지만.. 라이선스를 재발급하고 PC를 재부팅하니 해결이 되었습니다.

 

 

두 번째 문제는 URP 가 잘못된 버전으로 설치가 되어 콘솔창에 오류가 계속 남아있던 문제였습니다.

 

URP를 현재 유니티의 버전에 호환되는 버전으로 맞춰줘야 한다는 글들을 많이 보았는데요,

 

아직 작업한 내용이 없기에 깔끔하게 라이브러리 폴더를 삭제한 후 다시 유니티를 재실행하여 해결해 주었습니다.

 

 

두 문제 다 미처 캡처 사진을 못 찍었는데,

 

앞으로는 오류가 발생하거나 예기치 못한 상황들이 발생한다면 꼭 기록을 해놓겠습니다.😂

 


 

먼저 플레이어의 이동 관련 부분을 작업해야겠다고 생각했습니다.

 

Plane을 생성해 Ground로 두었고, 플레이어는 Unity Asset Store에서 무료로 다운로드하여 사용했습니다.

 

다음으로는 플레이어에 필요한 기본 컴포넌트들을 추가해 주었습니다.

Capsule Collider : 플레이어의 충돌 감지를 위함입니다.

NavMeshAgent : 플레이어의 이동을 하기 위함입니다.

 

다음은 플레이어의 Animator Controller를 추가해 주었습니다.

IdleToRun에 Blend Tree를 만들어 주었고, Blend Tree 엔 Idle, Run 애니메이션의 Parameter를

float 형식인 "Speed"로 제어가 되게 해 주었습니다.

(애니메이션은 Unity Asset Store에서 다운로드하여 사용했습니다.)

 


 

다음은 "PlayerMove" 스크립트의 전체 코드입니다.

using UnityEngine;
using UnityEngine.AI;

public class PlayerMove : MonoBehaviour
{
    [Header("Component")]
    [SerializeField] private Animator playerAnim;
    [SerializeField] private NavMeshAgent playerNav;

    [Header("Setting Value")]
    [SerializeField] private float playerNavRotateSpeed = 0.05f;
    private float animationSmmothTime = 0.1f;
    private float playerNavRotateVel = 0f;
   
    void Update()
    {
        AnimationController();
        MoveController();
    }

    private void AnimationController()
    {
        float speed = playerNav.velocity.magnitude / playerNav.speed;
        playerAnim.SetFloat("Speed", speed, animationSmmothTime, Time.deltaTime);
    }

    private void MoveController()
    {
        if (Input.GetMouseButtonDown(1))
        {
            RaycastHit hit;

            if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit, Mathf.Infinity))
            {
                if (hit.collider.tag == "Ground")
                {
                    playerNav.SetDestination(hit.point);
                    playerNav.stoppingDistance = 0f;

                    Quaternion rotationToLookat = Quaternion.LookRotation(hit.point - transform.position);
                    float rotationY = Mathf.SmoothDampAngle(
                        transform.eulerAngles.y,                    // 현재 y축 회전값
                        rotationToLookat.eulerAngles.y,             // 목표 Y축 회전값
                        ref playerNavRotateVel,                     // 보정 값
                        playerNavRotateSpeed * (Time.deltaTime));   // 회전하는데 걸릴 시간

                    transform.eulerAngles = new Vector3(0, rotationY, 0);
                }
            }
        }
    }
}

 

1. 플레이어 오브젝트에서 가져올 컴포넌트입니다.

    [SerializeField] private Animator playerAnim;
    [SerializeField] private NavMeshAgent playerNav;

 

2. 회전, 보정, 속도 등 세팅값입니다.

    [SerializeField] private float playerNavRotateSpeed = 0.05f;
    private float animationSmmothTime = 0.1f;
    private float playerNavRotateVel = 0f;

 

3. 현재 속도가 최대 속도의 몇 퍼센트인지 계산하는 함수입니다. = Animator.Parameter.Speed

현재 속도 (Magnitude) 최대 속도 (Speed) 결과값 (speed 변수)
0 m/s 5 m/s 0.0 m/s
2.5 m/s 5 m/s 0.5 m/s
5 m/s 5 m/s 1.0 ms
    private void AnimationController()
    {
        float speed = playerNav.velocity.magnitude / playerNav.speed;
        playerAnim.SetFloat("Speed", speed, animationSmmothTime, Time.deltaTime);
    }

 

4. 플레이어의 이동을 담당하는 함수입니다.

    private void MoveController()
    {
        if (Input.GetMouseButtonDown(1))
        {
            RaycastHit hit;

            if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit, Mathf.Infinity))
            {
                if (hit.collider.tag == "Ground")
                {
                    playerNav.SetDestination(hit.point);
                    playerNav.stoppingDistance = 0f;

                    Quaternion rotationToLookat = Quaternion.LookRotation(hit.point - transform.position);
                    float rotationY = Mathf.SmoothDampAngle(
                        transform.eulerAngles.y,                                   // 현재 y축 회전값
                        rotationToLookat.eulerAngles.y,                       // 목표 Y축 회전값
                        ref playerNavRotateVel,                                   // 보정 값
                        playerNavRotateSpeed * (Time.deltaTime));   // 회전하는데 걸릴 시간

                    transform.eulerAngles = new Vector3(0, rotationY, 0);
                }
            }
        }
    }

 

4.1 회전은 SmmothDampAngle() 함수를 사용해 주었으며 인자값으로는

Mathf.SmoothDampAngle(현재 y 값, 목표 y 값, 부드럽게 보간 해줄 값, 회전 스피드)

로 주었습니다.

 

이후 마지막으로 플레이어의 eulerAngles의 y축에 계산된 값을 넣어 주었습니다.

즉 NavMeshAgent는 이동 및 경로 계산에만 사용 되며, 회전은 코드로 제어를 해주었습니다.

 

5. 마지막으로 이 함수들은 Update()에서 실행을 해주었습니다.

    void Update()
    {
        AnimationController();
        MoveController();
    }

 


 

 

감사합니다.

'프로젝트 > 프로젝트 A' 카테고리의 다른 글

#006 Click (with. Object Pooling)  (4) 2025.08.11
#005 Skill Motion 2~4  (1) 2025.08.10
#004 Skill Motion 1 (with. UI)  (0) 2025.08.10
#003 Skill (with. UI & Cooldown)  (2) 2025.08.10
#002 Camera (with. cinemachine)  (3) 2025.08.10