7주차 WIL은 포탈, 플레이어 및 여러 자잘한 수정들을 하며 작업을 하였다. 일단 이번에는 영상이 있으니 한번 보고 들어가자.
시연영상
이번 프로젝트에서 팀장을 하며 총괄을 하였고, 게임의 클래스 설계 및 어떤 방식으로 개발을 해 나갈지, 및 인터페이스 구조를 짜고 작업을 진행하였다. (기획이 부족하다보니, 퍼즐 자체는 허술하지만, 개발 위주로 작업하였다.)
이 중에서 스테이지 2에 존재하는 포탈 물리 처리 및 플레이어 로직들을 주로 작업하였다.
추가적으로 다른 팀원분들의 자잘한 버그들도 수정을 피드백을 주가며 작업을 하긴 했다. 하지만 이곳은 내 TIL이니 내 트러블 슈팅 위주로 언급을 하겠다.
즉 이번 주차는 Rigidbody를 심화해서 작업 한 것이라고 보면 된다. 하나하나 살펴보며 Rigidbody를 자세히 다뤄 보도록 하자.
우선 MovingPlatform은 내 부분이 아니지만, 다른 팀원분것을 처리하다보니 오류가 발생하여 내가 해결한 부분이다.
사전 상황
- Player : rigidbody의 Moveposition으로 이동
- Rigidbody, Collider 보유
- MovingPlatform : transform의 Translate로 이동
- Collider 보유
- Player가 MovingPlatform위에 올라갈 시 MovingPlatform의 자식으로 변경 처리
문제 요약
- 플레이어가 플랫폼의 이동에 따라가지 않는 현상 발생
- 플레이어가 이동 플랫폼 위에 있을 시, 이동플랫폼과 같이 따라가야하지만, 따라가지 않고 플레이어는 월드 위치 기준 제자리에 그대로 존재함.
시도 과정
1. 플랫폼의 이동을 Translate 대신 MovePosition을 사용
- 플랫폼에 Rigidbody를 달아준 후, Moveposition으로 이동 시키기를 시도
- 플레이어가 움직이는 플랫폼의 로컬 좌표 또한 이동 방향에 따라가는 현상 발생
- 단 이 시점에서, 플레이어만 작동을 이상하게 하며, 다른 오브젝트들은 정상 작동됨
2. 플레이어 위치를 자식으로 놓지만, 월드 좌표를 가져옴
- 플레이어가 가져와질때, 월드 좌표를 참조하면 해결이 될까 고민해봤음
- 플레이어가 자식이 되는 순간 플랫폼에서 튕겨나감
3. 플레이어를 자식으로 두지 않고 따로처리
- 플레이어의 이동을 따로 처리하는 로직 생성
- 매순간 너무 부하가 걸리며, 정확하지 않고, 플랫폼이 매 순간 플레이어를 참조해야하는 상황 발생
원인 파악
- 1번 시도를 하였을 경우, 플레이어만 이동을 다르게 하고, 다른 오브젝트는 원하는 방향대로 이동을 하는 현상 발견
- PlayerController의 컴포넌트를 제거하니, 원하는 방향대로 이동을 하는것으로 파악 → PlayerController의 OnMove로직에서 문제가 발생된것으로 파악됨.
- OnMove로직에서 MovePosition이 실제 이동되는 로직이므로, 문제 위치는 MovePosition에 존재한다.
해결 방법
- MovePosition의 파라미터로 받는 변수의 위치를 참조 해 줄 경우 rigidbody.position을 사용 해 주어야 함.
- transform.position과 rigidbody.position을 넣어줬을 경우, 각각의 기능이 다르게 작동됨.
- transform.position을 사용 할 경우
- 플레이어의 transform.position을 기준으로 이동하지만, 부모로 설정된 movingPlatform 또한 moveposition을 사용하여 이동하기 때문에, transform.position이 변경될 가능성이 큼, 즉 원하는 방향대로 동작하지 않고, 부모의 위치를 참조하여 추가적인 이동이 발생 할 수 있음
- rigidbody.position을 사용 할 경우
- rigidbody.position은 리지드 바디 내부에 있는 위치를 기점으로 설정하기 때문에, 부모와 관계없이 해당 rigidbody만을 참조하여 이동을 할 수 있음. 또한 transform.position을 사용하는 것보다 직관적으로 이동이 가능하며, 연산속도 또한 빠름.
- MovePosition만 사용하면, 플레이어가 미끄러질 가능성이 있기 때문에, Physics Material을 추가로 활용하여 미끄러짐이 덜 하도록 설정 (마찰력 설정)
추가 정보
position의 이동 로직의 경우 두가지로 나뉘게 된다.
transform.position의 경우 전체월드 내부에서 얼마나 움직이는 지를 확인하여 움직이게 된다.
rigidbody.MovePosition 의 경우 상대 위치로부터 얼마나 움직이는 지를 확인하여 움직이게 된다.
크게 position의 이동로직은 두 분류로 나뉘게 되는데, 대부분의 Move함수들은 아래를 따르게 된다.
참조 링크 : https://docs.unity3d.com/ScriptReference/Rigidbody-position.html
내용을 참고하면, rigidbody.position을 사용 할 경우, transform.position을 사용 하는 것 보단, 훨씬 빠르고, 직관적이라고 한다. 물리 처리를 할 시 transform.position이 아닌 rigidbody.position을 사용 하도록 하자.
팀원의 내용을 이 또한 내가 수정하였다. 이는 간단하게만 집고 넘어가자. 아무래도 위에 PhysicsMaterial을 사용하여 마찰력을 늘려주었더니, 총알에 맞아도 날라가지 않는 현상이 발생하였다. (마찰력이 강함)
이 시점에서, 총알의 ForceMode를 살펴보니 Empluse로 되어있었다. 아무래도 중량을 감지하여 힘을 주다보니, 이를 velocityChange로 바꾸어 처리를 해 주었다. 중량을 무시하고 힘을 가하겠다는 뜻이다.
ForceMode
- ForceMode.Force : 가속도를 준다 (기존속도가 있을때 유리) - 질량비례
- ForceMode.Accelerate : 가속도를 준다 - 질량 무시
- ForceMode.Empluse : 순간적인 힘을 가한다 (속도변화에 유리함) - 질량 비례
- ForceMode.VelocityChange: 순간적인 힘을 가한다 - 질량 무시
이렇게 네 종류가 있다는것만 집고, 넘어가도록 하자.
3. 포탈 설치 시점 문제
상황
세가지 상황을 동시에 해결해야 하는 상황이 발생
- 첫번째 포탈을 설치 한 위치에 오브젝트가 존재하고, 이 후 두번째 포탈을 설치시 해당 오브젝트가 바로 이동하지 않는 현상
- 오브젝트의 속도가 높아 포탈을 통한 이동을 하기 전 다른 물체에 충돌하여 속도가 초기화 되는 현상
- 포탈 설치시 포탈의 크기가 변하는 상황 해결 및 나가는 방향으로 이동을 제어
포탈의 경우 OnTriggerEnter로 이동 로직 처리 시 Flag를 잠근 후, OnTriggerExit으로 Flag를 다시 활성화 시킴
시도
- 포탈의 collider을 OnTriggerEnter 대신 OnTriggerStay 사용
- 플레이어의 경우, 포탈 이동시, 사이즈가 크기 때문에, OnTriggerStay가 바로 불러져 이동위치가 계속해서 바뀌는 현상 발생
- Flag를 지정해서 처리를 하려고 시도하였지만, 물체가 탈출전까지 포탈이 비활성화 되는 문제 발생
- 해당 사유로 콜라이더를 변경하였지만, 2번 상황이 발생
- 리스트로 오브젝트를 저장하고, 소환 시 처리를 하는 것 또한 연산에 부하가 발생, 및 오브젝트가 탈출 시 Flag가 제대로 처리되지 않는 경우 발생
- 포탈의 transform을 조절하여 플레이어 위치 설정
- 해당 방식은 연산도 많고, 직관적이지도 않아서 복잡해지는 현상 발생
원인 파악
- 결국 OnTriggerEnter를 강제로 발생 시키면 문제가 해결 될 것이다.
- 충돌은 콜라이더를 변경해 준다면 해결이 된다.
- 회전방향을 제어해 주기 위하여 실제 콜라이더와 포탈의 forward방향을 분리해준다.
해결 방법
- OnTriggerEnter을 강제로 발생 시키기 위하여, 포탈 생성 시점에서, Collider을 꺼줬다가 켜주면 OnTriggerEnter가 호출이 된다.
- 함수는 이전에 있던 portal의 콜라이더를 껐다 킨 후, 해당 콜라이더를 껐다가 킨다.
- 포탈 설치 시점에서 해당 함수를 호출 해주면, 1번문제가 해결이 된다.
- 포탈 콜라이더는 대신 캡슐 촐라이더를 크게 유지시켜, 플레이어가 속도가 높아도 벗어나지 않게 설정 할 수 있다.
- boxCollider대신 capsuleCollider을 사용한다.
- 플레이어 내부에서 최대 속도를 제어 해 주는 로직을 추가하여, 플레이어가 너무 빨리 이동하여 충돌하거나, 맵을 벗어나는 부분을 제어한다.
- 겹침 방지를 위해 소환을 portal의 forward방향으로 해준다.
- 포탈 프리팹에서 실제 포탈과, 회전이 된 콜라이더 따로 배치
- forward는 포탈에서 가져와서 오브젝트에 설정 해 주면 됨
- 포탈의 크기를 설치 시점에 벽의 크기를 참고하여 변경
남아있는 이슈
- 포탈을 새로 설치 시 한번은 이동이 되지만, 콜라이더가 크고 추가적인 힘을 안주다보니, 한번만 이동 후 다른 포탈에서 추가 이동을 안하는 현상 존재
- 아무래도 포탈의 콜라이더가 현제 캡슐 콜라이더다 보니, 실제 위치보다 훨씬 크게 콜라이더가 잡혀있다. BoxCollider을 적당히 조절 하면 해결이 되었을 가능성이 있지만, 플레이어의 이동속도를 고려해 설정 해 주어야 했기 때문에 일주일 프로젝트에서 추가 작업을 하진 않았다.
4. 포탈 출구 방향
상황
우선 포탈 콜라이더는 cylinder콜라이더를 사용한다. 회전이 되어있는 상태인데, 여기서 forward를 처리하면 플레이어의 forward방향을 이와 같이 바꾸는데, 이럴 경우, 회전 된 값에 의하여 설치된 벽에 따라 사이즈가 다르게 되거나, 플레이어가 포탈에서 나올 시 이상한 방향으로 회전하는 상황이 발생하였다. 또한 물체의 탈출 연산도 엄청 복잡하게 되어 있어서, 다른 방식이 필요하였다.
해결 방법
튜터님께서 도움을 주셨다. 우선 해당 콜라이더와 포탈의 부모를 따로 만들어준다. 그리고 실제 보이는 콜라이더만 다르게 한다면 해당 문제는 다음과 같이 해결된다
- 포탈 발사시 : 포탈의 콜라이더는 부모의 회전방향에 따라 알아서 따라감
- 포탈 탈출시 : 포탈 콜라이더가 아닌 포탈 부모의 Forward를 참조하여 쉽게 플레이어의 forward방향을 연산 가능하다.
5. 점프대의 발사방향
이것도 팀원이 한 작업을 내가 고치긴 했다. 일단 한번 보자.
내가 작업한 내용을 보면, Player을 Moveposition으로 처리한다. velocity를 변경하는 방식을 사용하지 않고, 이를 사용한 이유는, 점프대가 여러 방향으로 밀 가능성이 있기 때문에 이를 이렇게 설정 해 두었다.
다만 점프대에서 이 사실을 몰라, 플레이어의 수작 법선백터를 받아오다보니 발사방향이 충돌 시점에 충돌 법선백터로 처리가 되고 있었다.
이는 간단하게 점프대에 힘의 수치와, 힘을 줄 방향을 추가하여 rigidbody가 충돌체에 들어왔을 경우 처리를 해 주어보니 해결이 되었다.
기술 스택
이번 리지드 바디를 활용한 부분들을 정리 해 보았다.
- 플레이어 이동을 MovePosition으로 이동시켜 어느 방향으로 force가 가해져도 이동이 가능하도록 로직 변경
- MovingPlatform의 경우 Physics Material을 활용하여 플레이어가 위에 있어도 이동이 되지만, 미끄러지지 않게 설정MovingPlatform위에 있는 Player가 Bullet에 맞을 경우를 대비하여, Bullet의 경우 넉백의 ForceMode를 VelocityChange로 설정하여 날림
- 점프의 경우, raycast를 활용하여 바닥 레이어가 map인지 확인 후 empluse를 활용하여 점프
- portal의 경우, 해당 rigidbody의 속도를 출구 portal의 forward방향으로 변경해주어, 포탈에 입장시 속도가 해당 방향으로 변화하게 설정
- 인터페이스중 IGrabable을 활용하여 플레이어 그랩 시 해당 인터페이스인지를 확인 후 처리
- Grab시, 각 IGrabable의 기능을 실행하지만, 기본적으로 해당 Rigidbody를 잠시 Kinetic으로 바꿔주어 플레이어가 드는것처럼 처리 해 주었다.
요약정리
이번에 사용한 내용들은 다음과 같다.
- rigidbody.position 과 transform.position의 차이
- raycastHit에 저장된 정보를 활용하여 여러 물리 연산 처리
- transform.forward활용하여 법선 백터를 구함
- 다양한 ForceMode를 활용
깃허브
이번에 작업 후 github에 있는 리포지토리들을 포크 한 후 네이밍 정리를 한번 해 주긴 하였다.