Chapter 13-2. 미니 RPG : 이동(NavMesh)

Date:     Updated:

카테고리:

태그:

인프런에 있는 Rookiss님의 [C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part3: 유니티 엔진 강의를 듣고 정리한 필기입니다. 😀
🌜 강의 들으러 가기 Click

Chapter 13. 미니 RPG 만들기

🚀 초기 세팅

📜PlayerController

	void OnMouseClicked(Define.MouseEvent evt)
	{
		//...

		RaycastHit hit;
		if (Physics.Raycast(ray, out hit, 100.0f, LayerMask.GetMask("Ground")))
		{
			_destPos = hit.point;
			_state = PlayerState.Moving;
		}
	}

마우스로 클릭하는 곳으로 플레이어가 이동하게 했었다. 그러므로 위 코드에 맞춰 Terrain의 Layer를 “Ground”로 변경함.


📜CameraController

    void LateUpdate()
    { 
        if (_mode == Define.CameraMode.QuarterView)
        {
            RaycastHit hit;
            if (Physics.Raycast(_player.transform.position, _delta, out hit, _delta.magnitude, LayerMask.GetMask("Ground")))
            {
                float dist = (hit.point - _player.transform.position).magnitude * 0.8f;
                transform.position = _player.transform.position + _delta.normalized * dist;
            }
            else
            {
				transform.position = _player.transform.position + _delta;
				transform.LookAt(_player.transform);
			}
		}
    }

이렇게 카메라도 가려지는걸 방지할 수 있도록 처리했었다. 그래서 빌딩 오브젝트들도 Layer를 “Ground”로 변경함. 이 오브젝트들에 카메라가 충돌하면 가려져서 플레이어를 못 찍으니까 바로 다시 플레이어를 찍을 수 있도록.. (한 0.8 거리로 조정해서 다시 위치 세팅)


🚀 이동하기

근데 이렇게 빌딩들도 “Ground”이 되니 플레이어가 빌딩 위로!! 이동할 수도 있는 문제가 생긴다.

  • 해결 방법
    1. 플레이어의 콜라이더와 충돌이 일어나면 피하게 하던가
    2. 플레이어로부터 쏜 Raycast에 걸리는 곳으로는 이동하지 못하도록 막을 수 잇겠다.
    3. NavMesh 사용하여 갈 수 있는 곳으로만 가게 하기.
      • 👉 세 번째 방법을 사용해보자.

위 3 가지를 적절히 섞어서 경우에 따라 사용할 수도 있겠다.

✈ NavMesh

Window - AI - Navigation

image

Baked로 갈 수 있는 곳을 미리 판별해 구워보았다. 파란색은 전부 갈 수 있는 곳.

  • NavMesh의 갈 수 있는 곳 판단 기준
    • static이 체크되어 있는 오브젝트가 위치한 곳은 갈 수 없는 곳이라고 인식을 한다. static이 체크되어 있지 않은 오브젝트라면 그 오브젝트는 언제든지 다른 곳으로 이동할 수 있다고 판단되어 그 곳으로 이동이 가능하게 된다.
      • 그러니까 플레이어도 밟고 갈 수 있는 장애물 정도면 NavMesh 에서 이를 갈 수 있는 곳으로 처리하게끔 해당 오브젝트의 static 체크를 해제해주면 된다.
    • 원기둥 실린더인 NavMesh Agent의 키, 반지름, 넘을 수 있는 문턱, 갈 수 있는 경사 각도 등등을 고려하여 갈 수 있는지 없는지를 판단한다.
      • 플레이어가 지붕엔 못 올라가도록 최대 경사를 좀 낮췄다.
      • NavMesh Agent 컴포넌트가 붙게 되는 애들만 위와 같이 NavMesh 로 연산된 갈 수 있는 파란색 위치들에만 갈 수 있게 된다.
        • 즉 NavMesh Agent 컴포넌트가 있어야지만 갈 수 있는 길 없는 길 이렇게 판단할 수 있는 능력이 생긴 다는 것.
        • 플레이어에게 NavMesh Agent 컴포넌트를 붙일 것이다. Agent 실린더를 플레이어에게 투영해서 플레이어의 크기 등등과 맞춰주는 것이 좋다. unityChan에게 NavMesh Agent 컴포넌트를 붙여주자.

image


📜PlayerController

using UnityEngine.AI; // ✨

	void UpdateMoving()
	{
		Vector3 dir = _destPos - transform.position;
		if (dir.magnitude < 0.1f)  
		{
			_state = PlayerState.Idle;
		}
		else
		{
			NavMeshAgent nma = gameObject.GetOrAddComponent<NavMeshAgent>();
			float moveDist = Mathf.Clamp(_speed * Time.deltaTime, 0, dir.magnitude);
			nma.Move(dir.normalized * moveDist); // 정밀도 있게 완전 가까이까지 이동시켜주진 않음

			Debug.DrawRay(transform.position, dir.normalized, Color.green);
			if (Physics.Raycast(transform.position + Vector3.up * 0.5f, dir, 1.0f, LayerMask.GetMask("Block")))
            {
				_state = PlayerState.Idle;
				return;
            }

			//transform.position += dir.normalized * moveDist;
			transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(dir), 20 * Time.deltaTime);
		}

		// 애니메이션
		Animator anim = GetComponent<Animator>();
		// 현재 게임 상태에 대한 정보를 넘겨준다
		anim.SetFloat("speed", _speed);
	}

지붕을 클릭한다면 사실 지붕 위로 올라가게 하려는게 아니라 그 지붕 뒤에 가려진 공간으로 이동시키겠다는 의미인데 Raycast 는 지붕에 충돌하여 플레이어가 지붕으로 자꾸 올라가려는 시도를 하게 된다. 이상함! 지금 지붕은 “Ground”로 처리되어 있어 메인 카메라에서 쏘는 Raycast와 충돌하기 때문이다. 따라서 다시 지붕의 레이어를 “Ground” 레이어를 “Block”로 바꿨다. 그럼 이제 지붕이 Raycast 안 맞고 지붕 뒤에 가려진 땅이 Raycast를 맞을 것이다. Terrain만 “Ground” Raycast를 받을 수 있으므로! 그래도 집을 클릭하면 플레이어가 집으로 계속 가려고 시도를 한다면 집 내부가 NavMesh 로 갈 수 있는 지역으로 인식된 것일 수도 있으니 집 오브젝트를 한번 치워서 집 내부에 파란 길이 있는지를 확인해보자. 이런 경우엔 어쩔 수없이 플레이어로부터 Raycast 를 쏴서 충돌되는 곳이 있다면 못 가게끔 하는 식으로 구현해야 겠다. 직빵으로 갈 수 없는 곳이라면 돌아서 목적지에 가게끔 그런 길찾기 시스템을 적용하는 것도 좋은 경험이 될 것 같다.

  • 플레이어가 Idle 되는 두가지 조건
    • if (dir.magnitude < 0.1f) 👉 도착
      • 목적지와 0.1 정도 밖에 차이 안날 때는 도착했다고 인식하고 Idle 재생
      • 원래 0.0001f 로 했었는데 NavMeshAgentMove 함수는 그렇게 정밀하게까지 이동시켜주지는 않는다. 파란색으로 구워진 길만 이동할 수 있어서인지.. 그래서 0.1f로 올려준다.
    • 도착한건 아니더라도 플레이어로부터 1.0f 거리에 Raycast를 쐈을 떄 “Block” 레이어를 가진 오브젝트와 충돌할 때.
      if (Physics.Raycast(transform.position + Vector3.up * 0.5f, dir, 1.0f, LayerMask.GetMask("Block")))
      


🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄

맨 위로 이동하기

Unity Lesson 2 카테고리 내 다른 글 보러가기

댓글 남기기