(유니티3D)호를 그리며 가는 캐릭터
Tree-Sha를 만들 때 기획자가 처음부터 강조했던 부분이 원형의 맵입니다.
기획자에게는 비슷한 게임들과의 차별성을 두기 위함이라 들었는데요.
이같은 사실은 게임이 시작되거나 게임 도중 플레이어가 죽었을때마다 다음과 같은 카메라 워킹으로 남은 높이와 함께 맵의 생김새를 알 수 있습니다.
앞으로 죽을때마다 보게 될 화면. 대신 올라갈 수록 남은 높이가 짧아져 보여지는 화면도 짧아진다.
이 글에선 저것에 대해선 다루지 않겠습니다. 사실 저 카메라워킹은 버그로부터 생겨난 기능입니다. 카메라 건들다보니 저렇게 됐는데 의외로 괜찮아서 주 기능으로 개선했습니다.
아무튼 중요한건 저 원형의 맵을 도는 방식입니다.
위 화면을 보다보면 아시겠지만 본 게임의 맵은 기본적으로 원통의 맵을 올라가는데 있지만 도중도중 직선의 길도 섞여있습니다.
즉, 플레이어 캐릭터는 기본적으로 원의 둘레를 따라 움직여야 하며 필요에 따라 직선 이동도 가능해야 합니다.
또, 직선 이동 시 특정 상황에 밀려 길 밖으로 밀리지 않도록 보정도 해야합니다.
예를 들면 0 → / 이런 식으로 플레이어가 기울어진 벽으로 달린다면 점점 바깥으로 밀려나갈 것입니다. 원형 맵이기에 충분히히 일어날 수 있는 일이지요. 하지만 그런 일이 일어나면 곤란하기에 직선 때도 보정을 해줘야 합니다.
먼저 캐릭터의 움직임은 Rigidbody(강체)가 아닌 CharactorController를 사용하였습니다.
CharactorController를 사용한 이유는...
프로젝트 초창기 모바일 기반이기에 더 가벼운 것을 채택하는 편이 좋다 생각하였고 기본적으로 물리엔진을 사용하는 Rigidbody 보다는 CharactorController가 더 가볍다고 생각해 채택하였습니다.
하지만 제가 알아본 바에 의하면 CharactorController가 더 무겁다더군요... 거기다 물리엔진의 도움을 받을 수도 없어 캐릭터의 모든 움직임은 스스로 짜는 수밖에 없었습니다. 깨달았을땐 이미 너무 많이 진행되어 되돌릴 수 없었습니다.
일단 결과 화면부터 보시겠습니다.
본 이미지는 2배속입니다. 근데 이 템포도 괜찮은거 같다.
앞부분은 원형 이동을 하고 전환점을 기준으로 직선 이동으로 자연스럽게 바뀝니다.
여기서 저 전환점도 원형 이동을 응용한 것입니다. 타고 도는 원의 반지름을 굉장히 짧게 두면 저런식으로 급회전 연출을 만들 수 있습니다.
이 코드의
전체적인 느낌은 이렇습니다.
개발자가 정해 주어야 할 것은 기준선의 위치와 연결선의 길이(원의 반지름)입니다.
중앙에 기준선을 둡니다. 높이에는 영향을 받지 않습니다.
그리고 회전시킬 대상에 기준선으로부터 시작하는 선을 연결합니다.
그다음 캐릭터의 위치를 확인하고 위에서 개발자가 입력한 연결선의 길이만큼의 위치에 해당하는 좌표를 얻어냅니다.
그리고 캐릭터의 방향도 원의 접선 각으로 만들어줍니다.
이 좌표를 매 프레임 계속 구하면서 캐릭터를 위치와 방향을 보정해주면 캐릭터는 위 원을 도는것처럼 보입니다.
기준선의 위치와 연결선의 길이를 조정함으로써 다양하게 응용 가능합니다.
위 글을 코드로 보면
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | transform.eulerAngles = new Vector3(0, 90 + GetDegree(new Vector2(CircleCenter.position.x, CircleCenter.position.z), new Vector2(transform.position.x, transform.position.z)), 0); //기준선으로부터 캐릭터의 위치 방향의 각도로 캐릭터의 각도를 변경 Vector3 tempDir = new Vector3(transform.position.x - CircleCenter.position.x, 0, transform.position.z - CircleCenter.position.z); //기준선으로부터 캐릭터의 방향 벡터를 구함 Vector3 tempPos = tempDir.normalized * Radius; //노말라이즈 한 값에 반지름을 곱해 목표 좌표를 구함 transform.position = Vector3.Lerp(transform.position, new Vector3(CircleCenter.position.x + tempPos.x, transform.position.y, CircleCenter.position.z + tempPos.z), Time.deltatime * 2); //목표 좌표를 향해 보간 실시 //transform.position = new Vector3(CircleCenter.position.x + tempPos.x, transform.position.y, CircleCenter.position.z + tempPos.z); //목표 좌표로 캐릭터를 즉시 이동 float GetDegree(Vector2 from, Vector2 to) //두 점의 각도를 반환 { return Mathf.Atan2(from.y - to.y, to.x - from.x) * Mathf.Rad2Deg; } | cs |
이렇게 됩니다.
CircleCenter는 Transform이라 더미 오브젝트를 하나 넣어주어야 합니다. Vector3가 아닌 이유는 위 이미지에서 급회전과 같이 기준선의 위치를 바꿔야 할 때 씬뷰에서 눈으로 확인하며 위치를 조정해줄 필요가 있기 때문입니다.
그리고 위치 보정 시 Lerp를 쓰는 이유는 특정 상호작용 시 이 보정을 무시하고 움직일 필요가 있는데 되돌아 올때 즉시 이동하면 어색해 보이기 때문입니다.
그런데 이 방법은 솔직히 처음 구상했을 때부터 지금까지 드는 생각이지만 좀 더 쉽고 적은 연산으로 똑같은 결과를 낼 수 있을 것 같은데 잘 모르겠습니다.
Quaternion(사원수)를 활용하는게 더 안정적이라는 말도 들었는데 아직까지도 정확한 활용법을 잘 모르겠고 무엇보다 eulerAngles(오일러 각도)가 더 익숙한 나머지 사원수에는 어떻게 접근을 해야할지 모르겠더군요.
아무튼 보정은 이렇게 이루어집니다.
위치 보정 뿐만 아니라 회전 보정도 일어나기 때문에 개발자는 계속 캐릭터를 어딘가로 이동 시켜주면 알아서 추가 보정이 일어나며 자연스럽게 돌면서 이동하는 모습을 볼 수 있습니다.
'Tree-sha (팀 3D) > 분석' 카테고리의 다른 글
(유니티3D)보스AI분석 (0) | 2017.12.16 |
---|---|
(유니티3D)기믹분석3(부유석, 버섯, 미끄러운 이끼) (0) | 2017.12.14 |
(유니티3D)기믹분석2(가시발판, 문, 레버, 보라색 발판) (0) | 2017.12.12 |
(유니티3D)기믹분석1 (모닥불, 바이러스) (0) | 2017.12.11 |
(유니티3D)직진 보정 (0) | 2017.12.05 |