EasyCastleUNITY

CGV 개발일지8 (VR 플레이어 손, 머리 위치 동기화) 본문

CGV(Castle Giant Virtual) 프로젝트 일지

CGV 개발일지8 (VR 플레이어 손, 머리 위치 동기화)

EasyCastleT 2023. 12. 13. 22:33

전에 개발일지6 번에서는 Photon Fusion을 사용하여 멀티플레이를 구현해보고자 했었습니다. 

 

하지만, Fusion을 사용하면, VR 플레이어를 동기화 하는 구조가 너무나도 복잡해졌습니다. 

 

그래서, 방향을 선회하여, Fusion이 아닌 Photon Pun2를 사용하여 멀티플레이를 구현하고자 합니다. 

먼저 Photon Pun2를 프로젝트에 세팅해주겠습니다. 

 

https://assetstore.unity.com/packages/tools/network/pun-2-free-119922

 

PUN 2 - FREE | 네트워크 | Unity Asset Store

Get the PUN 2 - FREE package from Photon Engine and speed up your game development process. Find this & other 네트워크 options on the Unity Asset Store.

assetstore.unity.com

Photon Pun2는 Asset Store에 올라가 있습니다. 그래서 간단하게 다운로드를 마쳤습니다. 

 

그 다음에 사용한 앱을 만들어 보겠습니다. 

포톤 홈페이지에서 관리하면으로 넘어간 다음 새로운 앱 만들기를 클릭합니다. 

그 다음 위 사진과 같이 입력하고 앱을 생성해주었습니다. 

생성한 앱

그러면 Pun2를 설치한 프로젝트로 들어갑니다. 

프로젝트에서 위 사진의 경로대로 가서 PUN Wizzard를 엽니다. 

 

위 사진에서 Setup Project를 누르고 나오는 창에 만든 앱의 앱 id를 입력합니다. 

연결이 되면 위의 사진 같은 창이 나옵니다. 

 

이렇게 기본적인 멀티 세팅은 끝납니다. 


https://doc.photonengine.com/fusion/v1/technical-samples/fusion-vr-host

 

VR Host | Photon Engine

Fusion VR Host demonstrates a quick and easy approach to start multiplayer games or applications with VR. The choice between Shared or Host/Server...

doc.photonengine.com

비록 Pun2를 통해 개발을 할 예정이지만, 기본적인 가상현실 플레이어의 구조는 위 사진을 참고하여 만들생각입니다. 

그러므로 필요한 스크립트는 실제로 VR 플레이어가 움직이는 정보를 가지고 있을, 

HardwareRig, HardwareHead, HardwareHand

해당 정보를 받아, Network 상에서 움직이게 하는 

NetworkRig, NetworkHead, NetworkHand 

를 만들어 움직임을 동기화하도록 해보겠습니다. 

 

중요한 점은 Hardware가 움직이는 것을 Network가 따라가는 것이 중요합니다.

Hardware 스크립트들은 OVR SDK에 있는 OVRCameraRig의 assign하여 사용합니다. 


하드웨어 구조

HardwareRig -> OVRCameraRig

HardwareHand -> OVRCameraRig의 TrackingSpace의 LeftHandAnchor와 RightHandAnchor에 부착

HardwareHead -> OVRCameraRig의 TrackingSpace의 CenterEyeAnchor에 부착

 


네트워크 리그 구조

Network는 방에 들어오면 만들어야 하기에, Prefab으로 NetworkRig를 만들었습니다.

NetworkRig 프리팹 구조

NetworkRig의 자식으로 HeadSet, LeftHand, RightHand를 가지고 있습니다. 

NetworkRig는 NetworkRig 컴포넌트를 가지고 있습니다. 

 

Headset은 NetworkHead 컴포넌트를 가지고 있습니다. 

LeftHand, RightHand는 각각 NetworkHand를 가지고 있습니다. 

 

그리고 각각의 Visuals의 실제로 이동할 모델들을 넣어주었습니다. 

아직 프로토타입이기에 머리는 유니티에서 기본적으로 제공하는 Cube를 그리고 양 손은 OVR SDK에 포함되어 있는 CustomHandLeft와 CustomHandRight를 사용하였습니다. 

 

이렇게 기본적인 네트워크 구조 설계를 마쳤습니다. 이제 컴포넌트 안쪽을 작성해보겠습니다. 


HardwareRig

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class HardwareRig : MonoBehaviour
{
    public HardwareHead headset;
    public HardwareHand leftHand;
    public HardwareHand rightHand;

    public NetworkRig networkRig;

    private void Update()
    {
        if (this.networkRig != null)
        {
            this.NetworkTransformTracking();
        }
    }

    private void NetworkTransformTracking()
    {
        this.networkRig.TrasformSet(this.transform);

        this.networkRig.networkHeadset.SetLocalPosition(this.headset.transform.localPosition);
        this.networkRig.networkLeftHand.SetLocalPosition(this.leftHand.transform.localPosition);
        this.networkRig.networkRightHand.SetLocalPosition(this.rightHand.transform.localPosition);

        this.networkRig.networkHeadset.SetLocalRotation(this.headset.transform.localRotation);
        this.networkRig.networkLeftHand.SetLocalRotation(this.leftHand.transform.localRotation);
        this.networkRig.networkRightHand.SetLocalRotation(this.rightHand.transform.localRotation);
    }
    public void TransformSet(Transform trans)
    {
        this.transform.position = trans.position;
        this.transform.rotation = trans.rotation;   
    }

}

HardwareRig는 하드웨어들의 정보를 전부 관리해야 되기에, HardwareHead와 각 손의 HardwareHand를 가지고 있습니다. 

 

NetworkTransformTracking 함수를 통해 각 부위의 정보를 networkRig의 헤드셋과 양손으로 전달하여 Hardware들을 따라가게 합니다. 

 

HardwareHead

HardwareHead는 위치정보를 가져오기 위해 만든 스크립트입니다.

HardwareHand

using Oculus.Interaction;
using Oculus.Interaction.HandGrab;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class HardwareHand : MonoBehaviour
{
    public OVRInput.Controller controller;

    public HandGrabInteractor handGrabInteractor;
    private void Start()
    {
        if(this.controller == OVRInput.Controller.RTouch)
        {
        	Debug.Log("오른손 잡음");
            this.handGrabInteractor.WhenInteractableSelected.Action += interactable =>
            {
                var grabbable = interactable.transform.GetComponentInParent<Grabbable>();
                Debug.LogFormat("Right interactable name : {0}",grabbable.gameObject.tag);
            };
        }
        
         if(this.controller == OVRInput.Controller.LTouch)
        {
        	Debug.Log("왼손 잡음");
            this.handGrabInteractor.WhenInteractableSelected.Action += interactable =>
            {
                var grabbable = interactable.transform.GetComponentInParent<Grabbable>();
                Debug.LogFormat("Left interactable name : {0}",grabbable.gameObject.tag);
            };
        }
    }
 
}

 

어느 손인지 구분하기 위해 OVRInput.Controller의 controller를 가지고 있습니다. 

그리고 각 손의 HandGrabInteractor와 연결하여 어느 손이 어떤 물체를 잡았는지에 대해, 정보를 가져옵니다. 

위 사진처럼 인스펙터에서 어느 손인지 지정해줄 수 있습니다. 


NetworkRig

 

using Photon.Pun;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class NetworkRig : MonoBehaviour
{
    public NetworkHead networkHeadset;
    public NetworkHand networkLeftHand;
    public NetworkHand networkRightHand;
    public HardwareRig hardwareRig;

    public PhotonView pv;

    private void Awake()
    {
        this.pv = this.GetComponent<PhotonView>();
        if (this.pv.IsMine)
        {
            this.hardwareRig = GameObject.FindAnyObjectByType<HardwareRig>();
            this.hardwareRig.networkRig = this;
        }
    }

    public void TrasformSet(Transform trans)
    {
        this.transform.position = trans.position;
        this.transform.rotation = trans.rotation;
    }
    
}

 

네트워크 리그는 Network들을 관리하여야 하기에 NetworkHead와 각 손의 NetworkHand 그리고 HardwareRig와 동기화하여야 하기에 hardwareRig의 정보를 가지고 있습니다. 

 

네트워크 객체는 생성되고, 네트워크 동기화를 하기 위해, PhotonView를 가지고 있습니다. 

객체가 생성될 때, Awake를 통해 PhotonView와 hardwareRig를 찾습니다. 

 

NetworkHead

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class NetworkHead : MonoBehaviour
{
    public void SetLocalPosition(Vector3 pos)
    {
        this.transform.localPosition = pos;
    }

    public void SetLocalRotation(Quaternion rot)
    {
        this.transform.localRotation = rot;
    }
}

중요한 점은 local이라는 점입니다. 각각의 OvrCameraRig의 각 Anchor의 local값을 받아 위치를 동기화 합니다. 

NetworkHand

using Photon.Pun;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class NetworkHand : MonoBehaviour
{
    public enum eHandSide
    {
        Left,Right
    }

    public eHandSide handSide;
   
    public void SetLocalPosition(Vector3 pos)
    {
        this.transform.localPosition = pos;
    }

    public void SetLocalRotation(Quaternion rot)
    {
        this.transform.localRotation = rot;
    }
}

enum을 사용하여 무슨 쪽인지를 지정해줍니다. 

지정해주는 모습
하드웨어 리그에, 각각의 컴포넌트를 가지고 있는 것들을 assign 합니다.
네트워크 리그에,  각각의 컴포넌트를 가지고 있는 것들을 assign 합니다.


Photon Transform View 부착

Photon Transform View이 컴포넌트를 붙여두면 알아서 위치 값을 네트워크 상에서 동기화 해줍니다. 

 

NetworkRig 프리팹의 NetworkRig, Headset, LeftHand, RightHand에 부착합니다. 


이렇게 작성을 마치고 시연해 보도록 하겠습니다. 

 

VR 플레이어 시점
다른 플레이어 시점

이렇게 손 머리 위치 동기화 구현 R&D를 마치겠습니다.