예지의 개발 로그포스 (Yeji's Log Force)

OVR Player Controller VR 멀티 플레이 - 카메라 꼬임 문제 해결법 본문

[Unity] Projects & Study/VR 멀티 항만 시뮬레이션 게임

OVR Player Controller VR 멀티 플레이 - 카메라 꼬임 문제 해결법

Yeji Heo 2023. 7. 12. 23:29

~  배경 ~

올해 시작한 프로젝트에서 VR 멀티 플레이를 구현하고 있다.

 

  •  빌드하는 HMD는 Meta Quest2
  •  Unity에서 OVR Player Controller를 활용.
  •  멀티 플레이용 서버 구축을 위해 Photon 활용.

~ 처음에 멀티플레이를 구현한 방법 ~

  • Photon 서버를 통해 room에 접속하도록 한다(+로그인)
  • 플레이어가 접속하면 Unity프로젝트에서 Resources 폴더에 있는 NetworkedPlayer라는 Prefab을 Scene에 Instantiate하도록 한다.
  • NetworkedPlayer는 OVR Player Controller를 약간 수정하여 만들었다. (OVR Player Controller자체가 Player로서 역할을 한다고 생각했기 때문에)

 


 

 문제 상황 1 : 카메라 서로 뒤바뀜, 동작X

 

여러 명의 사용자가 접속했을 때 서로의 카메라가 꼬이고, 그로 인해 제대로 동작하지 않게 됨.

예) Player1이 접속해 자유롭게 플레이 하다가, Player2가 들어오면 Player1의 카메라를 자기 카메라로 삼는다.

또한, Player1의 카메라는 작동하지 않게 된다.

 

 해결하기 - 여러개의 카메라 

OVR Player Controller에는 VR Camera가 포함되어있다.

OVR Player Controller가 플레이어 수 만큼 Instantiate된다는 것은, 카메라 갯수도 여러개가 된다는 뜻.

OVR Player Controller는 이 프로젝트에서 Player에 해당하므로, Photon서버를 통해 동기화 된다.

즉, 카메라도 동기화 되며 모든 플레이어에게는 플레이어 수 만큼의 카메라가 생기는 것이다.

 

==> Photon View에서 IsMine을 이용하여 로컬플레이어의 카메라만 살리고,

리모트 플레이어의 카메라의 카메라는 비활성화했다

public class DeactiveIfNotMine : MonoBehaviourPunCallbacks
{
    void Start()
    {
        if(!photonView.IsMine){
            gameObject.SetActive(false);
        }
    }
}

 


 

문제 상황 2: 기존 플레이어 카메라 중단은 해결이 X

이제 카메라가 서로 뒤바뀌는 문제는 해결되었다.

(플레이어는 자신의 OVRPlayeyController의 카메라를 통해서 맵을 보게 된 것!)

다만, 새로운 플레이어가 입장할 경우, 기존에 접속해있던 플레이어의 카메라는 여전히 중단된다. 

 

해결하기 - ?? 굳이 이렇게 할 필요가 없어

 

기존에 접속해있던 플레이어의 카메라가 여전히 중단된다. 

여전히 OVR이 플레이어로서 생성되기 때문에, 어쨌든 카메라는 다중으로 생성된다(비활성화 하더라도)

그러므로 OVR의 스크립트 내부에서 카메라를 연결하려는 시도를 하면서 에러가 나는 것이 아닐까...... 생각해봤다.

혹은, 각 플레이어의 카메라도 동기화되고있나..?라는 생각(그치만 그런식으로 RPC 함수를 작성해준 적은 없어서 아마 아니지 않을까..?흠..)

아무튼 그래서 OVR Camera Rig.cs랑 스크립트 이것저것 읽어보고, Camera Find 관련 내용을 찾아봤는데 양이 많아서 사실 아직은 잘 모르겠다. 

 

주절주절 말이 길었다.

OVR 스크립트들을 어찌저찌 제어해서 다양한 시도를 해보고 싶었지만, 생각할수록 좀 비효율적인데..? 싶었다

 

==> 그냥 OVR카메라를 여러개 생성하지 않으면 되지! OVR Player Controller와 Player의 개념을 분리한다. 

 

 

 ~ 최종 해결 방법: OVR Player Controller와 Player를 분리 ~

 

1. 사용자들이 스폰되는 Scene에 미리OVR Player Controller를 하나 둔다.

얘는 각 플레이어의 독립적인 카메라로서 Photon View도 달지 않았고,

그냥 Oculus에서 제공되는 Prefab그 자체이다.

 

2. Resources에 NetworkedPlayer를 생성한다. 

 

얘는 카메라는 당연히 포함하지 않고,

닉네임(Text Mesh)이랑 몸(Mesh, Material)을 가지고 있다.

Photon View와 Photon Transform View는 추가했다

 

각 플레이어가 사용하는 카메라 위치는 동기화되면 안 되지만,

(동기화 되면 이제는 OVR Camera가 하나니까 모두가 같은 카메라를 바라보게 됨)

 

각 플레이어의 위치는 서로 동기화 되어 알아볼 수 있어야 하기 때문이다!

 

3. 여기에 플레이어 이동을 위한 Player Controller.cs를 작성해 붙여줬다.

    void Start()
    {
        PV = GetComponent<PhotonView>();

        if (PV.IsMine)
        {
            LocalVRCam = GameObject.Find("OVRPlayerController");
        }
    }

    void Update() 
    {
        if(PV.IsMine){
            
            Vector2 thumbstick = OVRInput.Get(OVRInput.Axis2D.PrimaryThumbstick);
            float h = thumbstick.x; 
            float v = thumbstick.y; 

            Vector3 dir = new Vector3(h, 0, v);
            dir =  Camera.main.transform.TransformDirection(dir);

            if (OVRInput.Get(OVRInput.Button.PrimaryIndexTrigger))
                Speed = 10;
            else
                Speed = 5;

            LocalVRCam.GetComponent<CharacterController>().SimpleMove(dir * Speed); // 로컬플레이어는 이처럼 VR캠을 움직이더라도, Photon Transform View를 추가하지 않았으니 다른 플레이어 입장에서는 바뀌지 않는다.

			//이후 플레이어가 카메라의 위치와 각도를 따라가게 한다. 그렇게 변경된 플레이어의 위치와 각도는 Photon Transform View에 의해 동기화된다.
            this.transform.position = new Vector3(LocalVRCam.transform.position.x, LocalVRCam.transform.position.y-1.8f, LocalVRCam.transform.position.z);  //LocalVRCam.transform.position; 
            this.transform.rotation = LocalVRCam.transform.rotation; 
        }
    }

Start단에서 로컬 플레이어만(PV.IsMine이 true)

하이어라키에 있던 OVRPlayerController를 찾는다.

 

그런 다음, 컨트롤러 입력을 통한 움직임은 Update에서 처리한다.

OVRPlayerController의 CharacterController를 사용해 움직임을 구현했다.

SimpleMove를 통해 카메라가 바라보는 방향대로 카메라를 이동시킨다.

 

이동하는 카메라의 위치와 각도를 NetworkedPlayer(Avatar)가 잘 따라갈 수 있도록 값을 대입해준다. 그럼 끝! !

 

+ 첨언:  Player에 Camera 위치와 각도를 대입하는 방식으로 구현한 이유

게임에서 Player가 Camera를 따라가게하는 (혹은 Camera가 Player를) 방법으로는

이렇게 Transform 값을 대입해주는 것도 있지만, 

하이어라키에서 부모/자식 관계를 이용하는 방법도 있다.

 

지금껏 나의 프로젝트에서 부모/자식 관계를 이용하지 않았던 케이스는 

- Player의 자식으로 Camera를 넣었을 때: Player가 굴러가면 Camera도 뱅글뱅글 굴러가서 어지러움

이 있었는데 이번에 하나 추가됐다.

 

우선 부모/자식으로 넣어준다고 가정을 해보자

1. OVRPlayerController의 자식으로 Player 넣기

OVR이 움직이고 Player가 따라간다는 의미가 된다.

그런데 OVR은 각 플레이어의 독립적인 카메라로서 동기화 되지 않게 하고싶다.

그래서 Photon Transform View를 넣지 않겠다고 했었다.

이렇게 해보니 부모인 OVR이 동기화 되지 않으니, 자식인 Player의 위치도 동기화 되지 않는 것을 발견했다(Player에 PhotonView와 PhotonTransformView 컴포넌트를 추가해주더라도.)

 

2. Player의 자식으로 OVRPlayerController 넣기

1의 이유로 인해서, 반대로 Player를 부모로 삼아봤다.

이번엔 Player가 움직이고 OVR이 따라간다는 의미가 된다.

나는 Character Controller를 통해 플레이어 움직임을 구현하려 했으므로, Player에 해당 컴포넌트를 추가해줬다.

한편, OVRPlayerController는 아래 경고로 알 수 있다시피 Character Controller를 무조건 컴포넌트로 가지고 있어야 한다.

OVRPlayerController.cs가 이 컴포넌트에 의존하기 때문이다.

그럼 부모(Player), 자식(OVRPlayerController) 모두 Character Controller를 가져야만 하는 상황이 되었다.

실행해보니, 둘이 중복되어서 그런지 입력에 따른 움직임 처리가 이상하게 꼬여버린다. 

 

따라서!!

Character Controller를 필수로 가져야 하는 OVRPlayerController이 이동을 담당하게하고,

Player가 위치와 각도값만 따오도록 하는 구현을 해준 것이다!

 


~ 소감 ~

 

이번 문제는 처음 다뤄보는 Photon 네트워크 개념이 섞여 있었다보니

Photon의 동기화와 네트워크 개념에 대해 공부가 부족해서 뭔갈 놓치고 있나???했었다.

 

그래서 오히려 Photon과 네트워크 공부에 더 노력을 쏟느라

해결하는데 생각보다 시간이 걸렸다ㅋㅋㅠㅠ

해결하고보니 간단한 문제여서 시원하고도 허탈했다

 

하지만 덕분에 이것저것 찾아가면서 공부해보고

멀티게임 구현에 자신감도 생겨서 

개인적으로 흐뭇한 기분이 든다

멀티 음성채팅까지 마저 구현한 다음에, 더 나은 방법도 있는지도 다시 고민해봐야겠다!

 

♥ 긴 글 읽어주셔서 감사합니다 ★

Comments