ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 19장 좀비 서바이버 멀티플레이어 : 네트워크 게임 월드 구현
    Unity/레트로의 유니티 게임 프로그래밍 에센스 2021. 7. 20. 15:41

    19.1 네트워크 플레이어 캐릭터 준비

        19.1.1 Photon View 컴포넌트

            - 네트워크를 통해 동기화될 모든 게임 오브젝트는 Photon View 컴포넌트를 가져야 함.

            - Photon View 컴포넌트는 게임 오브젝트에 네트워크상에서 구별 가능한 식별자인 View ID를 부여

            - 또한 Observed Components 리스트에 등록된 컴포넌트들의 변화한 수치를 관측하고, 네트워크를 넘어서 다른 클라이언트에 전달.

            - View ID 1~999까지? -> 정보를 담을 스크립트를 위한 게임오브젝트로 개수 제한에서 벗어날 수 있지 않을까?

            - max 값 변경 : https://answers.unity.com/questions/596457/how-can-i-add-more-photonview-ids.html

     

    How can I add more PhotonView ID's? - Unity Answers

     

    answers.unity.com

            - 로컬에 있는 게임 오브젝트와 다른 클라이언트에 있는 리모트 게임 오브젝트는 같은 네트워크 ID를 부여받음.

            -  IPunObservable 인터페이스르 상속한 컴포넌트만 관측할 수 있음.

     

    *** 동기화 방법 ***

    > Photon View 컴포넌트의 Observe option 필드에서는 값을 관측하고 동기화하는 방식을 변경할 수 있음.

    > Observe option에서 가능한 설정.

        > Off : 동기화 하지 않음.

        > Reliable Delta Compressed : 상대방이 최근에 수신한 값과 동일한 값은 송신하지 않음.

        > Unreliable : 패킷의 수신 여부를 검사하지 않고 지속적으로 송신함.

        > Unreliable On Changed : Unreliable과 동일하나 값의 변화가 감지될 때만 송신.

    > Photon View를 거치지 않고 RPC등의 별개의 방법으로 값을 동기화할 경우에도 Off를 사용하여 대역폭을 아낄 수 있음.

     

        19.1.2 Photon Transform View 컴포넌트

            - Photon Transform View 컴포넌트는 자신의 게임 오브젝트에 추가된 트랜스폼 컴포넌트 값의 변화를 측정하고, Photon View 컴포넌트를 사용해 동기화.

            - Photon Transfomr View는 자신이 로컬이라면 송신, 리모트라면 수신함

            - Photon Transform View 컴포넌트는 Photon View 컴포넌트 없이는 동작할 수 없음.

       

        19.1.3 Photon Animator View 컴포넌트

            - Photon Animator View 컴포넌트는 로컬일 때는 자신의 애니매이터 컴포넌트의 파라미터들을 관측하고, Photon View 컴포넌트를 사용해 다른 클라이언트에 있는 자신의 리모프에 전달.

            - 리모트일 때는 네트워크를 통해 로컬이 건넨 수치들을 받아 자신의 애니메이터 컴포넌트의 파라미터에 덮어씀.

            - 동기화를 원하는 파라미터는 드롭다운 메뉴를 클릭하고 Discrete 또는 Continuous로 동기화.

            - DIscrete는 값의 연속적인 변화를 Continuous에 비해 잘 반영하지 못하지만 대역폭을 아낄 수 있음.

     

        19.1.4 CameraSetup 스크립트

            - 로컬인 캐릭터를 추적 대상으로 함.

            - phtonView.IsMin : 속한 게임 오브젝트가 주도권(로컬인지)을 가지고 있는지를 알려줌.

            - cinemachineVirtualCamera.Follow 추적 대상

            - cinemachineVirtualCamera.LookAt 주시 대상

        - Photon View에 Photon Transform View나 Photon Animator View를 추가함.

     

    19.2 네트워크용 플레이어 캐릭터 컴포넌트

        19.2.1 PlayerInput 스크립트

        19.2.2 PlayerMovement 스크립트

        19.2.3 PlayerShooter 스크립트

        19.2.4 LivingEntity 스크립트

            - 호스트에서만 체력 관리 및 대미지 처리 실행(서버측에서)

            - [PunRPC] 로 선언된 메서드는 다른 클라이언트에서 원격 실행할 수 있음.

            - RPC를 통해 어떤 메서드를 다른 클라이언트에서 원격 실행할 때는 Photon View 컴포넌트의 RPC() 메서드를 사용함.

            - photonVeiw.RPC() 파라미터 : 원격 실행할 메서드 이름(string), 원격 실행할 대상 클라이언트(RpcTarget 타입), 원격 실행할 메서드에 전달할 값(필요한 경우).

            - photonVeiw.RPC("DoSomething", RpcTarget.All);

            - PhotonNetwork.IsMasterClient : 현재 코드를 실행하는 클라이언트가 마스터 클라이언트인(호스트)인지 반환하는 프로퍼티.

        19.2.5 PlayerHealth 스크립트

            - 인터페이스의 상속한 메서드에 [PunRPC]가 있었다면 override할 때에도 [PunRPC]를 적어야함.

     

    19.3 네트워크 Gun

        - 부모 게임 오브젝트에 Photon View 컴포넌트가 이미 추가되어 있다고 해도 자식 게임 오브젝트에 독자적으로 실행할 네트워크 처리가 있다면 자식 게임 오브젝트에서도 Photon View 컴포넌트를 추가하여 View ID를 부여해야함.

        19.3.1 변경된 Gun 스크립트

            - Photon View 컴포넌트를 사용해 동기화를 구현할 모든 컴포넌트(스크립트)는 IPunObservable 인터페이스를 상속하고 OnPhotonSerializeView() 메서드를 구현해야 함.

            - OnPhontonSerializeView() 메서드는 Photon View 컴포넌트를 사용해 로컬과 리모트 사이에서 어떤 값을 어떻게 주고받을지 결정.

            - IPunObservable 인터페이스를 상속한 컴포넌트는 Photon View 컴포넌트의 Observed Components에 등록되어 로컬과 리모트에서 동기화될 수 있음.

            - stream.IsWriting : 현재 스트림이 쓰기 모드인지 반환. 로컬이면 true, 리모트이면 false.

            - SendNext() : 메서드로 스트림에 값을 삽입.

            - ReceiveNext() : 메서드로 가져옴.

            - ReceiveNext() 메서드를 통해 값이 들어올 때는 범용적인 object 타입으로 들어오기 때문에 읽는 측에서는 원본 타입으로 형변환함. 또한 스트림에 삽입한 순서대로 값이 도착. -> 주고 받는 변수들의 나열 순서가 일치해야함.

     

    ####

    클라이언트는 입력만 감지 및 호스트로 입력 송신. 호스트측이 클라이언트의 입력을 수신하여 동기화하고 그 입력에 따라 로컬 오브젝트 위치 이동 및 클라이언트에 위치 송신. 클라이언트는 수신 받은 위치로 리모트 오브젝트 위치에 동기화.

    ####

     

     19.4 네트워크 좀비

        19.4.1 변경된 Enemy 스크립트

            - 

    """

    LivingEntiry 스크립트에서 OnDamage() 메서드에 이미 [PunRPC] 속성을 선언했지만,

    Enemy 스크립트에서 OnDamage()를 오버라이드하면서 [PunRPC]속성이 해지되었기 때문에

    Enemy의 OnDamage() 메서드에서 [PunRPC]속성을 다시 선언.

    """

     

    19.5. 네트워크 아이템

        19.5.1 AmmoPack 스크립트

            - Destroy() 메서드를 사용하면 해당 월드에서만 사라짐. 만약 호스트측에서 Destroy()메서드를 호출했다면, 다른 클라이언트에서는 해당 게임 오브젝트가 파괴되지 않음. 따라서 Photon.Network.Destroy() 메서드 사용.

     

        19.5.2 HealthPack 스크립트

        19.5.3 Coin 스크립트

        19.5.4 PhotonNetwork.Instantiate()

            - PhotonNetwork.Instantiate() 메서드는 입력으로 Photon View 컴포넌트가 추가된 프리팹을 받아 해당 프리팹의 복제본을 모든 클라이언트에서 생성. 생성된 게임 오브젝트의 소유권은 PhotonNetwork.Instatiate()를 직접 실행한 측에 있음.

            - PUN 구현상의 문제로 PhotonNetwork.Instantiate()를 사용해 생성한 프리팹들은 Resources라는 이름의 폴더에 있어야 함.

            - PhotonNetwork..Instatiate()는 생성할 프리팹 자체를 입력받지 않고, 생성할 프리팹의 이름을 string타입으로 받음.

     

    ############

    Resources 폴더

    - Resources 폴더는 에셋의 사용 여부와 상관없이 항상 메모리에 해당 에셋을 로드하는 특수한 폴더.

    - 최적화를 위해 유니티는 씬에서 한 번도 사용하지 않는 에셋들은 자동을 빌드에서 제거되지만, Resources폴더에 있는 에셋들은 사용되지 않아도 빌드에 포함됨. 게임 실행부터 종료까지 메모리에 로드된 상태로 유지 됨.

    - 게임 도중에 Resources.Load() 메서드를 사용해서 해당 에셋을 실시간으로 사용.

    - 최대한 사용하지 않아서. 빌드 크기와 메모리 사용량을 줄이도록 하자...

    ############

     

        19.5.5 ItemSpawner 스크립트

            - Instantiate() -> PhotonNetwork.Instantiate()

            - Destroy() -> PhotonNetwork.Instantiate()

            - PhotonNetwork.Instantiate() 는 지연 시간을 줄 수 없어서 PhotonNetwork.Instantiate() 를 호출하는

                DestroyAfter()에 지연 시간을 줌.

     

    19.6 네트워크 매니저

        19.6.1 GameManager 스크립트

            - MonoBehaviourPunCallbacks를 상속한 스크립트는 여러 Python 이벤트를 감지할 수 있음.

            - PhotonNetwork.LeaveRoom()은 현재 네트워크 룸을 나가는 메서드. 단, 씬을 전환한다는 의미는 아님.

            - PhotonNetwork.Instantiate()를 사용해 게임 도중에 생성된 것이 아닌 처음부터 씬에 있었던 게임오브젝트라면, 그 소유권은 호스트에 있음. 게임 도중의 기준이란?? 게임 도중에 생성된 것이라면. 상황에 따라 다르다???

    < 19.7 GameManager AddScore() >

    - 호스트 입장에서 GameManager 게임 오브젝트는 로컬.

    OnPhoteonSerializeView : 주기적으로 자동 실행되는 동기화 메서드.

    < Start()에서 로컬 플레이어 캐릭터 생성>

    - 각 캐릭터들은 각각의 클라이언트 입장에서 로컬 플레이어 캐릭터.

    - PhotonNetWork.Instantiate() 메서드는 각각의 클라이언트에서 따로 실행됨. -> ??

    - 1. 클라이언트 B가 룸에 접속 -> B에서 GameManager의 Start() 실행.

    - 2. Start() 내부코드, PhotonNetwork.Instantiate()에 의해 플레이어 캐릭터 b를 클라이언트 A와 클라이언트 B에서 생성.

    - b는 B, a는 A가 소유권을 가짐.

    - 1> 클라이언트 A가 룸을 생성하고 접속.

    - 2> A에서 GameManager Start() 실행.

    - 3> A가 PhotonNetwork.Instantiate()에 의해 플레이어 캐릭터 a를 A에 생성.

    - 4> 클라이언트 B가 룸에 접속 -> 클라이언트 B에 자동으로 a가 생성됨.

    - 5> B에서 GameManager Start() 실행.

    - 6> B가 PhotonNetwork.Instantiate()에 의해 플레이어 캐릭터 b를 A와 B에 생성.

     

     

    19.7 적 생성기 포팅

        19.7.1 EnemySpawner 스크립트

        19.7.2 웨이브 정보 동기화

            - 적 생성은 호스트의 로컬에서만 실행됨. 다른 클라이언트는 호스트가 생성한 적 게임 오브젝트의 복제본을 네트워크를 통해서 전달 받음.

            - enemies 리스트에 생성된 적을 등록하는 절차는 호스트의 EnemySpawner에서만 실행됨.

        19.7.3 Setup() 원격 실행

        19.7.4 적 사망 이벤트

            - onDeath 이벤트에 이벤트 리스너에 등록하는 코드는 호스트에서만 실행됨. 

            - 적 리스트에서 해당 리스트 제거, 사망한 적을 10초 뒤 파괴, 게임 매니저 100점 추가하는 처리는 호스트에서만 실행.

            - 파괴되는 처리는 다른 클라이언트에 자동 반영되지 않음. PhotonNetwork.Destroy() 사용. 이 함수는 지연시간을 받지 않음.

     

        19.7.5 직렬화와 역직렬화

            - PUN은 RPC로 원격 실행할 메서드에 함께 첨부할 수 있는 입력 타입에 제약이 있음.

            - RPC를 통해 다른 클라이언트로 전송 가능한 대표적인 타입으로는 byte, bool, int, float, string, Vector3, Quaternion이 있음.

            - 기존에 RPC에서 지원하지 않던 타입을 직업 지원하도록 정의하는 것이 가능.

            - PhotonPeer.RegisterType() 메서드를 실행하고, 원하는 타입을 명시하고, 어떻게 직렬화/역직렬화할지 명시.

            - PhotonPeer.RegisterType(타입, 번호, 직렬화 메서드, 역직렬화 메서드);

            - 커스텀 타입은 255개까지 등록가능하며, 각각의 타입은 고유 번호를 할당받아야 함.

     

     void Awake() {

        PhotonPeer.RegisterType(typeof(Color), 128, ColorSerialization.SerializeColor, ColorSerialization.DeserializeColor);

    }

     

    19.8 완성본 테스트

        - 먼저 참가하는 측이 룸을 생성하므로 호스트 역할을 맡게 됨.

     

    19.9 마치며.

        - 동기화할 게임 오브젝트는 Photon View 컴포넌트를 가지고 있어야 함.

        - Photon View 컴포넌트의 Observed Components 리스트에는 관측 및 동기화할 컴포넌트가 등록됨.

        - Photon Transform View 컴포넌트와 Photon Animator View 컴포넌트는 위치와 애니메이션을 Photon View 컴포넌트를 사용해 동기화함.

        - MonoBehaviourPun을 상속한 스크립트는 photonView 프로퍼티를 사용해 자신의 게임 오브젝트에 추가된 Photon View 컴포넌트에 접근할 수 있음.

        - RPC를 통해 원격 실행할 메서드에는 [PunRPC] 소겅이 선언되어야 함.

        - photonView.RPC() 메서드로 [PunRPC] 선언된 메서드를 다른 클라이언트에서 원격 실행함.

        - IPunObservable 인터페이스를 상속한 컴포넌트는 Photon View 컴포넌트를 사용해 동기화될 수 있음.

        - IPunObservable 인터페이스를 상속하면 OnPhotonSerializeView() 메서드를 구현해야 함.

        - OnSerializeView() 메서드에서 stream.IsWriting을 이용해 현재 스트림이 송신(쓰기) 모드인지 수신(읽기) 모드인지 검사할 수 있음.

        - 로컬 오브젝트에서는 stream.IsWriting 값이 true. 이떄는 SendNext() 메서드로 값을 보냄.

        - 리모트 오브젝트에서는 stream.IsWriting 값이 false. 이때는 ReceiveNext() 메서드로 값을 받음.

        - 모든 클라이언트에서 동일하게 게임 오브젝트가 생성되게 하려면 PhotonNetwork.Instantiate() 메서드를 사용.

        - PhotonNetwork.Instantiate() 메서드로 생성할 프리팹은 Resources 폴더에 있어야함.

        - 모든 클라이언트에서 동일하게 게임 오브젝트가 파괴되려면 PhotonNetwork.Destroy() 메서드를 사용.

        - 오브젝트를 네트워크 통신을 통해 주고받으려면 오브젝트를 바이트 형태로 변환해야 함.

        - 직렬화는 오브젝트를 바이트 데이터로 변환.

        - 역직렬화는 바이트 데이터를 원본 오브젝트로 변환.

     

     

     

     

    댓글

Designed by Tistory.