-
유니티 2D에서 파쿠르 기능 구현하는 일지쾌락없는 책임 (공부)/Unity 2021. 11. 24. 21:24반응형
제 구현이 완벽하지 않으며 단순 일지를 적은 것입니다.
진짜 파쿠르 기능을 만들기 위해서는 더 좋은 방법들이 많이 있을겁니다.파쿠르 기능이 필요했다.
현재 2D 플랫포머 게임을 팀에서 만들고 있던 중 '플레이어가 벽 끝에서 벽을 잡고 있는게 어색하다. 한번 파쿠르 기능을 넣는게 어떤가?' 라는 이야기가 있었고 생각나는 로직이 있어서 만들어 보겠다고 했습니다. 이때는 래퍼런스를 생각하지 않고 기능을 만들었으며 결론부터 말하자면 대실패를 한번 했습니다.
제 똥을 봐 주세요
일단 처음 생각한 방법이 '모션만 파쿠르를 하고 실제 콜라이더는 벽에 있는 상태에서 정지해 있다가 모션이 끝날 때 콜라이더를 이동하는게 어떤가?' 입니다. 그걸 위해서 overlapcircle을 좌우 위에 추가를 했으며 '위에는 판정이 없지만 아래 판정이 남아 있을 때 파쿠르 기능을 시작하자' 라는 생각이었습니다. 또한 위 주황색 오브젝트들이 파쿠르가 끝날 때 콜라이더가 이동할 위치였습니다. 일단 이 overlapcircle은 문제가 없었으나 이 모션에 대한 문제가 있었습니다.
첫째로 콜라이더가 이동할때의 이미지였습니다. [ 파쿠르 모션의 마지막 -> 콜라이더 옮겨짐 -> idle 애니메이션으로 ] 진행을 했는데 여기 콜라이더가 옮겨지는 과정에서 아직 애니메이션이 파쿠르 단계에 있으므로 1~2프레임 정도 공중에 뒤틀린다는 단점이 있었습니다. 이는 현재 애니메이션 상태를 체크한 뒤 애니메이션이 끝나는 시점에 애니메이션 변경, 콜라이더 이동을 동시에 했으면 부드러운 전환이 가능하지 않을까 했지만 해당하는 방법을 구현하지 못했습니다.
사실 첫번째 문제의 경우 공식문서를 찾다보면 해결법이 나오지 않을까 했지만 두번째로 생긴 문제로 인해 결국 파쿠르를 변경하게 되었습니다.
두 번째 문제가 위 사진과도 같은데 평범한 벽에서는 살짝 정지되고 발판 위에 뜨는 감이 있지만 치명적인 문제가 '움직이는 발판' 에서 나오게 되었습니다. 위 구상에서 벽에 있는 상태에서 정지해 있다가 이동이 큰 문제였는데 이 경우 2가지 문제가 생기게 되었습니다.
1. 파쿠르가 시작하는 시점에서 콜라이더가 멈춰 이미지가 벽에 잠겨버린다
- 플레이어의 콜라이더가 kinematic으로 변경됨으로서 벽에 정지하게 되는데 이 과정에서 이미지가 벽에 침식되는 문제가 있습니다.
- 또한 kinematic으로 인해 움직이지 않으니 모션이 '파쿠르를 하는 시점의 위치'에서 시작이 되어 이미 지나간 자리에서 파쿠르를 하는 모습이 만들어지게 됩니다.
2. 파쿠르 모션 후 이동하는 곳이 모션과 맞지 않는다
- 유니티에서 자식 오브젝트는 부모 오브젝트의 로컬 좌표계를 사용하게 되는데 이 과정에서 플레이어가 이동할 좌표(위 그림에서 주황 점)도 함께 움직여서 정해진 위치에는 어느정도 밪게 이동을 하게 되지만 모션과 맞게 떨어지지 않아 위 사진과 같은 어색함이 있습니다.
플랫포머 게임에서 움직이는 발판을 제거한다는건 상당한 디메리트이고 그렇다고 파쿠르 기능이 아주 뛰어난 것도 아니었습니다. 때문에 이 파쿠르는 수정될 필요가 있었으며 이에 따라 새로운 기능을 생각했습니다.
이전의 파쿠르 판정은 동일하게 두고 플랫폼 끝에서 기존 점프와는 관계 없는 추가적인 점프 (Addforce 사용)를 주게 해서 파쿠르와 비슷한 효과를 낼 수 있게 했습니다. 이 점에서 플레이어는 느린 파쿠르 모션을 기다릴 필요가 없었고 각종 판정들에 있어서 실제 모션과 콜라이더가 동기화가 잘 되었습니다. (위의 경우 파쿠르 중 가시를 만나게 되면 콜라이더와 스프라이트가 맞지 않아 불합리한 점이 있었습니다)
위 개선점을 유명한 2D 플랫포머 게임인 '셀레스트'에서 아이디어를 얻어 가져왔으며 개인적인 플레이에서 이 방법이 더 편안함을 주고 있었습니다.
현재는 치명적인 문제는 없지만 파쿠르를 할 시 판정 정도, 정확히는 시간에 따라 점프량이 달라지게 됩니다. 위 움짤에서 움직이는 발판이 플레이어에게서 멀어지면 판정 시간이 짧아 약간의 점프만 되며 아닐 시에는 높은 점프를 하게 됩니다. 이는 변수 하나를 추가해 해결할 수 있을것으로 보입니다. 아 이것도 글쓰면서 생각이 난 사항이라 역시 글을 써야 더 나은 방향이 보이나 봅니다.
혹시나 이 글을 보시는 분들은 아마 더 나은 코드를 짜실거라 믿고 있으며 혹시나 만드시거나 발견하신다면...모쪼록 보여주시면 감사하겠습니다...
코드로 구현
[SerializeField] float parkourJumpForce; void ParkourJump() { print("점프"); rigid.AddForce(Vector2.up*parkourJumpForce); } void Parkour() { isParkouring = true; canMove = false; rigid.velocity = Vector3.zero; rigid.isKinematic = true; anim.SetTrigger("Parkour"); StartCoroutine(AfterParkour()); } [SerializeField] float parkourTime; IEnumerator AfterParkour() { yield return new WaitForSeconds(parkourTime); isParkouring = false; yield return new WaitForSeconds(0.13f); this.transform.position = (coll.onRightWallDown) ? rightParkourPoint.position : leftParkourPoint.position; rigid.isKinematic = false; canMove = true; }
지금 사용하고 있는 함수는 위의 ParkourJump이며 아래 코루틴을 사용하는 함수는 결점이 많은 코드입니다. 누가 짰는지 모르겠지만 정말 실력이 없는 프로그래머가 짰나 봅니다. 혹시나 모션을 넣는다면 yield return 부분에서 애니메이션의 시간을 인자로 사용하는게 더 좋아 보입니다.
반응형'쾌락없는 책임 (공부) > Unity' 카테고리의 다른 글
[Unity] List가 들어있는 클래스에서 Instantitate를 하면 NullReferenceException: Object reference not set to an instance of an object (0) 2022.03.26 유니티 애드몹 적용 중 Gradle failed 대응해보기 (0) 2022.01.25 유니티 Stopwatch로 시간 측정하기 (0) 2021.09.14 유니티 드래그앤 드롭, UI / 오브젝트 드래그앤 드롭 (1) 2021.09.08 유니티 한방향 발판 만들기 - One Way Platform (0) 2021.08.05