XR Rig Body생성, 스킬 거리 최댓값 주기

2021. 9. 5. 20:47Unity/VR

이번에는 지금까지 XR Tool Kit에서 지원해주는 것만으로는 불가능한 XR용 Body를 생성하고 폭발형 스킬의 최대거리값을 설정해주는 작업을 진행 하였다.

 


XR RIg Body(몸체) 생성

 

기본적으로 XRToolKit이 지원해주는 XR Rig의 구성요소를 보자면 크게나눠 "머리","컨트롤러" 두개로 나뉘어진다.

 

 

그럼 여기서 문제는 텔레포트를 통한 시스템적 이동이 아닌 VR공간안에서 플레이어가 직접 걸어다니는 이동에 관련해서 이슈가 생긴다. 왜냐하면 시스템적이동은 XR Rig전체가 같이 이동하는반면 직접적인 이동은 XR Rig는 그대로 있고 Main카메라만 이동하기 때문이다. 

 

이 특성을 통해 생기는 이슈는 바로 Rotation즉 회전의 문제이다. 기본적으로 지금까지 채택하고 있었던 스킬Cube의 형태는 XR Rig에 자식으로 들어가있었다. 그렇게 해야 시스템적 이동을 해도 플레이어를 따라오기 때문이다. 하지만 이는 직접적인 이동에서 문제가 생기는데 XR Rig의 자식이니 플레이어가 플레이공간을 돌아다니면 플레이어를 따라 오지않아 거리가 멀어져 트리거 작동이 불가능 해 지기 때문이다.

그렇다고 Main Camera의 자식으로 하면 플레이어를 따라오긴하지만 보는 방향마다 오브젝트가 따라와 상당히 부자연 스러운 느낌을 낸다.

즉 플레이어를 따라오되 머리회전에 많은 영향을 받지않는 오브젝트를 만들어야 했다.

 

 

이는 XR Rig의 몸체를 만드는 것으로 해결을 했다.

본인이 직접만든 XR Rig의 로직은 엔진내부에서 드래그앤 드랍으로 자식화를 하는 것을 채택하지않고 직접 스크립팅으로 움직임을 따라가게 만들었다.

 void Update()
    {
        position = xrHeadTrans.transform.position;
        position.y = xrHeadTrans.position.y - 0.5f;
        this.transform.position = position;

        quaternion = xrHeadTrans.transform.rotation;
        quaternion.x = 0;
        quaternion.z = 0;
        this.transform.rotation = quaternion;
    }

위 코드를 보면 금방 이해가 되겠지만 설명하자면 MainCamera의 위치를 받아 몸체에 해당하는 곳으로 위치를 초기화 시켜주고 카메라의 y축 로테이션을 받아 나머지는 0을 유지한체로 몸체의 회전을 초기화한다. 이렇게하면 상당히 자연스러운 몸체의 움직임,위치가 완성된다.

 

 

참고로 GameObjcet.Transform.rotation의 형태는 quaternion으로 정의되어있다. 그러기에 position 처럼 Vector3로 제어하려고하면 컴파일 에러가 나기 때문에 주의해야 한다.

 

 

이제 이 몸체에 스킬Cube를 자식화시키면 직접적인 움직임에도 플레이어를 따라오며 부자연스러운 느낌도 해결할 수 있다.

 

 

 


폭발형 스킬 거리 최댓값 생성

 

기존의 IceBomb스킬의 문제점을 살펴보자.

 if (Physics.Raycast(EyeTrans.transform.position, EyeTrans.transform.forward, out hit, Mathf.Infinity, layerMask))

위는 IceBomb의 레이캐스트문이다. 잘보면 Distance항목을 Infinity로 설정해놓은 것을 볼 수 있다. 그렇기에 거리가 아무리 멀던 Raycast가 검출만 성공하면 어디든지 스킬이 발동될 수가 있다. 

 

그렇다면 이 이슈의 해결방법은 여러가지를 생각해낼 수 있는데 가장 맨처음은 Distance를 무한이아닌 유한의 값으로 조정하는 것이다.

하지만 이 방법은 한가지 심각한 문제점이 있다. 바로 스킬발동의 불편함이다. 거리를 유한하게두면 컨트롤이 어려운 Ray의 특성상 스킬이 불발되는 경우가 너무 자주 생긴다는 것이였다.

 

 

그래서 도입한 것이 각도별 레이캐스트이다.

 

먼저 캠에서 Raycast를 쏘고 검출이 된 포인트 지점과의 Distance를 계산한다. 

Distance가 너무 멀면 미리 만들어놓은 Raycast지점(각도를 5도 낮춘)으로 로직을 일임한다.

만약 2번째 Ray에서도 거리가 너무 멀면 3번째 Ray로 일임한다. 이렇게 총 4개의 Ray지점을 만들고 만약 4개의 Ray지점에서 전부 out Distance가 나오면 불발 이펙트를 실행한다.(이펙트 아직 미구현)

 if (Physics.Raycast(EyeTrans.transform.position, EyeTrans.transform.forward, out hit, Mathf.Infinity, layerMask))
            {
                Vector3 insPos = hit.point;
                Debug.Log(hit.distance);
                TempTrans.transform.position = insPos;
              
                if (hit.distance > 30)
                {
                   
                    EyeTrans2 = GameObject.Find("EyeTrans2");
                    if (Physics.Raycast(EyeTrans2.transform.position, EyeTrans2.transform.forward, out hit, Mathf.Infinity, layerMask))
                    {
                        insPos = hit.point;
                        TempTrans.transform.position = insPos;

                        if (hit.distance > 30)
                        {
                            EyeTrans3 = GameObject.Find("EyeTrans3");
                            if (Physics.Raycast(EyeTrans3.transform.position, EyeTrans3.transform.forward, out hit, Mathf.Infinity, layerMask))
                            {
                                insPos = hit.point;
                                TempTrans.transform.position = insPos;

                                if (hit.distance > 30)
                                {
                                    EyeTrans4 = GameObject.Find("EyeTrans4");
                                    if (Physics.Raycast(EyeTrans4.transform.position, EyeTrans4.transform.forward, out hit, Mathf.Infinity, layerMask))
                                    {
                                        insPos = hit.point;
                                        TempTrans.transform.position = insPos;
                                        if (hit.distance > 30)
                                        {
                                            Debug.Log("실패");
                                        }
                                        else
                                        {
                                            Instantiate(IceBomb, TempTrans.transform);
                                            Destroy(this.gameObject);
                                            Debug.Log("발동4");
                                        }
                                    }
                                }
                                else
                                {
                                    Instantiate(IceBomb, TempTrans.transform);
                                    Destroy(this.gameObject);
                                    Debug.Log("발동3");
                                }
                            }
                        }
                        else
                        {
                            Instantiate(IceBomb, TempTrans.transform);
                            Destroy(this.gameObject);
                            Debug.Log("발동2");
                        }
                    }
                }
                else
                {
                    Instantiate(IceBomb, TempTrans.transform);
                    Destroy(this.gameObject);
                    Debug.Log("발동1");
                }


            }else             // 처음부터 레이가 검출안되는경우 ex.하늘로 쏘아진 Ray
            {               
                        EyeTrans4 = GameObject.Find("EyeTrans4");
                        if (Physics.Raycast(EyeTrans4.transform.position, EyeTrans4.transform.forward, out hit, Mathf.Infinity, layerMask))
                        {
                            Vector4 insPos = hit.point;
                            TempTrans.transform.position = insPos;

                            if (hit.distance > 30)
                            {
                                Debug.Log("실패하늘");
                            }
                            else
                            {
                                Instantiate(IceBomb, TempTrans.transform);
                                Destroy(this.gameObject);
                                Debug.Log("발동4");
                            }
                        }
             }
                   
             }
        }

물론 이 검사를 매번 하지않도록 이 로직은 스킬발동 트리거인 "손을 땅으로 힘차게 내리는"동작을 실행했을 때 검사하도록 만들었다.

 

 

실제로 이 스킬을 실행해보면 스킬의 발동지점이 옛날보다 더 조정이 쉽게 바뀌었고(거리가 멀면 짧게 바꿔주고 다시 거리가 멀어지면 다시 짧게 바꿔주니까) 스킬의 불발도 거의안나게 되었다.

'Unity > VR' 카테고리의 다른 글

Unity 스킬 피격 처리  (0) 2022.01.15
Unity GunShot Skill 생성  (0) 2022.01.12
XR Ray Interactor, Teleportation Area를 통한 이동 시스템  (0) 2021.09.01
Unity IceBomb Skill제작  (0) 2021.07.16
Unity 스킬 획득,관리 시스템(3)  (0) 2021.07.11