미니 RPG
게임을 만들 때 빈 프로젝트에서 처음부터 쌓아나가는 식으로 만들지 말고 여태까지 해온 온갖 매니저들 등등 이런 나만의 프레임 워크를 미리 구축해놓고 이를 활용해 만들어 나가기를 추천한다고 하셨다. 풀링 매니저, UI 매니저 등등 이런건 어떤 게임을 만들든 다 필요하다.
Light 연산
RealTime
실시간으로 빛을 연산하겠다는 의미 이로 인해 성능 부하가 어마어마 하다.
그리고 움직이는 오브젝트라면 그 오브젝트의 빛 연산을 미리 Baked 할 수 없다. 어떻게 움직일 줄 알고 하겠는가? 🤯
Baked
이미 계산 완료 후 에셋으로 저장해놓은 라이트맵을 이용해서 게임 실행 중에 라이트 연산을 하지 않고 이를 덮어 쓴다. 라이트맵은 빛을 받으면 어떤 색으로 보일지 그림자는 어떻게 할지 등등 미리 연산을 해두고 그 정보를 저장해둔 것이다. 즉 미리 그림자 같은 것들을 다 계산해서 라이트맵으로 구워놓음.
움직이지 않는 static 한 오브젝트들에 대해서는 라이트맵 미리 구워둘만 하다. 성능이 더 높아질 것! 👉 고정되어 있는 오브젝트들은 유니티 상에서 static을 체크하여 미리 라이트맵을 굽자.
Mixed
반반! 둘 다 하겠다는 의미
초기 세팅
📜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를 기존의 "Wall"대신 “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
Baked로 갈 수 있는 곳을 미리 판별해 구워보았다. 파란색 영역은 전부 갈 수 있는 곳.
NavMesh의 갈 수 있는 곳 판단 기준
1. static이 체크되어 있는 오브젝트가 위치한 곳은 갈 수 없는 곳이라고 인식을 한다. static이 체크되어 있지 않은 오브젝트라면 그 오브젝트는 언제든지 다른 곳으로 이동할 수 있다고 판단되어 그 곳으로 이동이 가능하게 된다. 즉 플레이어도 밟고 갈 수 있는 장애물 정도면 NavMesh 에서 이를 갈 수 있는 곳으로 처리하게끔 해당 오브젝트 의 static 체크를 해제해주면 된다.
2. 원기둥 실린더인 NavMesh Agent의 키, 반지름, 넘을 수 있는 문턱, 갈 수 있는 경사 각도 등등을 고려하여 갈 수 있는지 없는지를 판단한다. 해당 실습에서는 플레이어가 지붕엔 못 올라가도록 최대 경사를 좀 낮췄다. 해당 NavMesh Agent 컴포넌트가 붙게 되는 애들만 위와 같이 NavMesh로 연산된 갈 수 있는 파란색 위치들에만 갈 수 있게 된다. 즉 NavMesh Agent 컴포넌트가 있어야지만 갈 수 있는 길 없는 길 이렇게 판단할 수 있는 능력이 생기는 것이다. 따라서 Bake를 다 하고 난 이후에는 플레이어에게 NavMesh Agent 컴포넌트를 붙인다. Agent 실린더를 플레이어에게 투영해서 플레이어의 크기 등등 과 맞춰주는 것이 좋다. unityChan에게 NavMesh Agent 컴포넌트를 붙여주자.
📜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 로 했었는데 NavMeshAgent의 Move 함수는 그렇게 정밀하게까지 계산되지 않는다. 따라서 0.1f로 값을 수정한다.
도착한건 아니더라도 플레이어로부터 1.0f 거리에 Raycast를 쐈을 떄 “Block” 레이어를 가진 오브젝트와 충돌할 때
if (Physics.Raycast(transform.position + Vector3.up * 0.5f, dir, 1.0f, LayerMask.GetMask("Block")))
'공부 > 인프런 - Rookiss' 카테고리의 다른 글
Part 3-13-3. 미니 RPG : 타겟 락온 (0) | 2023.08.31 |
---|---|
Part 3-13-2. 미니 RPG : 스탯 설정, 마우스 커서 변경 (0) | 2023.08.30 |
Part 3-12-1. Data : Json을 이용한 Data Manager (0) | 2023.08.29 |
Part 3-11-1. Corutine : 코루틴 최적화, 커스텀 (1) | 2023.08.29 |
Part 3-10-1. Object Pooling : Pool Manager (0) | 2023.08.28 |