스파인 바운딩 박스를 3D 레이캐스트로 검출해내기
요즘 끄적끄적 만들고 있는게 옛날식 2.5D 구조를 가진 게임, 즉 공간은 3D 좌표계지만 거기에 2D 캐릭터를 수직으로 세워서 움직이는 형식의 게임이라, 1인칭 시점에서 이걸 조준하려면 사실 스파인 캐릭터의 2D 바운딩 박스가 3D로 검출이 되는가를 먼저 확인했었어야 했다. 하지만 내가 하는 게 언제나 그렇듯이 그런거 다 따지고 만드는게 아니라 일단 부딪쳐보고 거기서 필요한건 찾아가며 때우는 식이다 보니... 이게 그렇게 생각처럼 간단하게 되는게 아니라는 걸 알고 나서는 이미 때는 늦은 것이었다...
스파인의 바운딩 박스는 상당히 좋은 기능이다. 스파인 자체가 원체 UI가 간단명료하고 깔끔하고 빠르게 작업할 수 있는데다, 거기에 폴리곤을 적당히 집어넣고 본에 붙이면 바로 유니티에서 콜라이더로 변환되어 움직이고 ON/OFF도 된다. 이걸 유니티에서 하려 하면 노가다도 노가다고, 스파인 수정이 되면 유니티에서 리소스를 또 손봐야하고, 동작에 맞게 ON/OFF를 시키려면 스파인 애니에다가 anim 파일을 또 추가해야 하고 그럼 meta 파일도 또 생기고... 아무리 생각해도 유니티에서 별도로 콜라이더를 추가하는 건 하고 싶지 않았다. 그러나 아무리 발악을 해도 3D 레이캐스트가 스파인의 바운딩박스를 인식하질 못하니, 정말 이걸 해야 하나 싶으면서도 일단은 최후의 수단으로 남겨 놓고 다른 똘똘한 방법을 찾아보기로 했다.
원래는 조준점에다 레이를 붙여서 검출된 놈을 공격하면 되는 간단한 구조였는데 레이가 안 먹히니 문제가 커졌다. 차선책으로 생각한 건 일단 조준점의 X, Y 좌표를 써서 OnTrigger2D로 조준점에 걸린 유닛을 모두 찾아내고, 찾아낸 유닛들의 Z값을 전부 받아와서 그중에 제일 Z값이 작은 놈에게 공격이 맞는 식으로 하자는 거였다. 일단 논리적으로 말은 되는 알고리즘인데 실제로 짜자니 문제가 계속 발생했다. 내가 제대로 알고리즘을 못 짠 탓이겠지만, 가장 문제가 되는 건 조준점에 몇 놈이 겹칠지를 알 수 없다는 거였다. 사실 당연한 상황이다. 실제 게임이라면 조준도 움직이고 적들도 계속 움직일 거고, 조준점에 적 몇 놈이 겹칠 지는 아무도 모른다.
이것 때문에 검색해보고 시도했던 방법이 대충 아래와 같다.
1) 조준점에 걸린 놈들을 List로 묶고 각각의 Z값도 List로 묶은 뒤 List.Min을 구해서 그 Z위치에 있는 놈이 어떤 놈인가를 구하기
2) 스파인 런타임의 BoundingBoxFollower를 뜯어보고 PolygonCollider2D를 생성하는 부분을 3D로 교체(...)
3) 적의 프리팹에 3D Plane을 심고 거기다 레이를 쏴서 일단 오브젝트 이름과 Z값을 알아낸 뒤 맞는 처리를 함
1번과 3번은 일단 얼추 보기에 그럴듯한 아이디어이긴 했다. 실제로 제일 앞에 나와있는 놈이 어떤 놈인지 이름과 Z값을 알아내는 데까지는 성공했다. 문제는 여기서 총알을 쐈을 때였는데, 1)의 경우는 계속 실시간으로 갱신되는 List 안에서 계속 Min을 구한 뒤 그거에서 다시 OnTrigger2D를 하는 게 너무 복잡하고 어려웠다. 3)은 최전방 캐릭터를 알아내는 게 Plane이라서 문제였다. Plane을 작게 하면 Plane 밖에 나간 팔다리 등의 인식이 안되고, Plane을 크게 하면 화면상으로 팔다리 사이의 공간으로 뒤에 있는 적을 쏴도 앞에 있는 놈이 맞아버린다.
2번은 내가 스스로 생각해도 어이없는 발상이긴 했는데 평범하게 PolygonCollider2D를 PolyconCollider3D로 바꾸면 알아서 3D 콜라이더로 되지 않을까 하는 순진한 생각이었고, 결과적으로는 당연하게도 실패. 우선 PolygonCollider3D 따위는 없었다(...). 공식 런타임을 겁없이 손대려 했던 것도 좀 꺼림칙했는데, 에러 나는거 보고 얌전히 포기.
이걸로 하루 반 정도를 보내고 나니 머리도 아프고, 영 해결될 가망이 없어 보여 그냥 유니티에서 콜라이더 노가다 해야 하나 싶어지던 참에, 구글 검색으로 찾은 글 하나가 아무래도 심상치 않아 보였다.
https://answers.unity.com/questions/1087239/get-2d-collider-with-3d-ray.html?childToView=1713394#answer-1713394
글이 약간 길긴 하지만 결과적으로는 Physics2D.GetRayIntersection 메소드를 쓰면 3D 레이캐스트로도 2D 콜라이더(바운딩 박스)를 검출할 수 있다는 내용이다. 조금 반신반의 하면서 적용해 봤는데 처음엔 실패했지만, 위의 1)~3)을 전부 시도해보고 나니 뭔가 좀 감이 오는 것 같아서 재도전했더니 상큼하게 성공.
잘 안됐던 원인은 샘플 코드의 Camera.main.ScreenPointToRay(screenPosition)을 그대로 적용하려고 했기 때문이었다. 내 경우엔 조준이 카메라와 일치하지 않고 따로 놀기 때문에 레이 자체도 굳이 ScreenPointToRay 같은 걸 써서 카메라에 붙일 필요가 없었다. 평범하게 transform.position을 써서 조준에 붙였더니 잘 된다. 이걸 하고 나니 OnTriggerEnter2D, OnTriggerStay2D, OnTriggerExit2D로 장황하게 어설픈 체크 하는 코드도 필요가 없어져서 전부 삭제.
뭔가 이거 만들면서 엄청나게 공부가 많이 되고 있는데 되게 뿌듯함...