Photon에서의 이전 가이드

Photon(PUN2)으로 만든 멀티플레이를 GS2-Realtime의 sync / stateMachine으로 옮기는 절차와 대응표

이 페이지는 Photon PUN2로 만든 멀티플레이를 GS2-Realtime의 오브젝트 동기화(sync / stateMachine)로 옮기기 위한 절차와 대응표입니다. 각 API의 자세한 내용은 Unity 클라이언트를 참조해 주세요.

사고방식의 차이

관점Photon PUN2GS2-Realtime
룸 찾기Photon의 로비와 매치메이킹GS2-Matchmaking으로 멤버를 모으고, GS2-Realtime의 룸을 만든다
룸 접속PhotonNetwork.ConnectUsingSettings / JoinRoom발행된 IP 주소·포트·암호화 키로 RealtimeSession을 통해 접속한다
누가 값을 결정하는가각 클라이언트(마스터 클라이언트가 조정 역할)룸의 서버가 쓰기 권한과 RPC 허가를 검증한다. stateMachine에서는 게임 진행도 서버의 룰이 결정한다
동기화하는 데이터OnPhotonSerializeView로 자유로운 데이터를 전송한다오브젝트마다 최대 32개의 정수 필드. 위치·회전은 단위 간격으로 반올림하여 전송한다
통신UDP(reliable / unreliable)WebSocket(확실하게 전달할 것)과 UDP(정기적인 상태 갱신)
과금동시 접속 수룸 서버의 가동 시간과 통신량

가장 큰 차이는 룸의 서버가 권한을 검증할 수 있다는 점입니다. 다른 플레이어 오브젝트의 값은 변경할 수 없으며, 오브젝트 카탈로그를 설정하면 등록한 Prefab과 RPC만 통과하도록 검사하게 할 수 있습니다. 마스터 클라이언트에 맡기던 게임 진행(적의 HP, 득점, 승패)은 stateMachine 룰로 옮기면 클라이언트 개조의 영향을 받지 않게 됩니다.

대응표

컴포넌트

Photon PUN2GS2-Realtime
PhotonViewNetworkEntity
PhotonTransformViewNetworkTransform
IPunObservable / OnPhotonSerializeViewINetworkObservable(OnNetworkWrite / OnNetworkRead)
PhotonNetwork.PrefabPool / Resources의 PrefabPrefab Catalog와 NetworkWorld
씬에 배치한 PhotonView, InstantiateRoomObject룸이 만드는 공유 오브젝트(stateMachine 룰, 또는 조작 권한을 대여하는 오브젝트)

오브젝트

Photon PUN2GS2-Realtime
PhotonNetwork.Instantiate(name, pos, rot)world.Instantiate(prefabId, pos, rot)
PhotonNetwork.Destroy(go)entity.Destroy()
photonView.IsMineentity.IsMine
photonView.Ownerentity.OwnerId(접속 ID)
photonView.TransferOwnership(player)entity.TransferOwnership(connectionId)
photonView.RequestOwnership()(OwnershipOption.Takeover)entity.RequestOwnership()(오브젝트 카탈로그를 설정하는 경우 "ownership": "takeover")
PhotonNetwork.CleanupCacheOnLeave = false카탈로그에서 "keepOnLeave": true(마스터 클라이언트에게 인계)
photonView.Group / PhotonNetwork.SetInterestGroupsentity.SetGroup / world.SetInterestGroups, SubscribeGroup

RPC와 이벤트

Photon PUN2GS2-Realtime
[PunRPC][NetworkRpc]
photonView.RPC(name, RpcTarget.All, args)entity.Rpc(name, RpcTarget.All, args)
RpcTarget.Others / AllBuffered / OthersBuffered같은 이름의 RpcTarget
RpcTarget.AllViaServerRpcTarget.All(항상 서버를 경유한다)
RpcTarget.MasterCliententity.RpcTo(name, world.MasterClientId, args)
photonView.RPC(name, player, args)entity.RpcTo(name, connectionId, args)
PhotonMessageInfoNetworkRpcInfo(Sender는 호출자의 접속 ID)
PhotonNetwork.RaiseEventworld.RaiseEvent / RaiseEventTo(핸들러는 [NetworkEvent])
IOnEventCallback.OnEventworld.AddEventTarget으로 등록한 [NetworkEvent] 메서드, 또는 world.EventReceived

룸과 플레이어

Photon PUN2GS2-Realtime
PhotonNetwork.LocalPlayer.ActorNumbersession.MyConnectionId
PhotonNetwork.PlayerList, OnPlayerEnteredRoom / OnPlayerLeftRoomRealtimeSession의 OnJoinPlayer / OnLeavePlayer
PhotonNetwork.MasterClient / IsMasterClientworld.MasterClientId / IsMasterClient
PhotonNetwork.SetMasterClient / OnMasterClientSwitchedworld.SetMasterClient / MasterClientChanged
Room.SetCustomProperties / OnRoomPropertiesUpdateworld.SetRoomProperty / RoomPropertyChanged
Player.SetCustomProperties / OnPlayerPropertiesUpdateworld.SetPlayerProperty / PlayerPropertyChanged
PhotonNetwork.Timeworld.ServerTime
PhotonNetwork.SendRate / SerializationRateworld.SendRate
unreliable 전송RealtimeUdpChannel(정기적인 상태 갱신을 UDP로 전송한다)

이전 절차

  1. 룸 준비를 대체한다: Photon의 로비와 매치메이킹을 GS2-Matchmaking과 GS2-Realtime의 룸 생성으로 대체합니다. Realtime 네임스페이스는 serverType을 sync(게임 진행도 서버에서 결정한다면 stateMachine)로 합니다. 흐름은 GS2-Realtime 소개와 튜토리얼을 참조해 주세요.
  2. 접속을 대체한다: RealtimeSession으로 접속하고, NetworkWorld.Attach와 world.Receive를 연결합니다. 룸에 접속하기의 내용대로 작성합니다.
  3. Prefab을 대체한다: PhotonView를 NetworkEntity로, PhotonTransformView를 NetworkTransform으로 바꿉니다. Prefab Catalog를 만들고 Prefab ID를 할당하여 등록합니다.
  4. 동기화할 값을 대체한다: OnPhotonSerializeView로 전송하던 값은 동기화 스키마의 필드로 만들어 INetworkObservable로 읽고 씁니다. 값은 32개까지의 정수(float는 단위 간격으로 반올림)이므로, 문자열이나 배열은 RPC나 커스텀 프로퍼티로 전송합니다.
  5. RPC를 대체한다: [PunRPC]를 [NetworkRpc]로, photonView.RPC를 entity.Rpc로 바꿉니다. 다른 플레이어의 오브젝트에 대해 호출하는 RPC에는 AnyCaller = true를 붙입니다. 인수의 타입이 사용할 수 있는 타입에 포함되는지 확인합니다.
  6. 생성과 삭제를 대체한다: PhotonNetwork.Instantiate를 world.Instantiate로, PhotonNetwork.Destroy를 entity.Destroy()로 바꿉니다. 씬에 배치했던 오브젝트는 룸이 만드는 공유 오브젝트로 합니다.
  7. 검사하려면 오브젝트 카탈로그를 설정한다: 생성이나 RPC를 룸이 검사하게 하려면 Prefab Catalog 에셋을 우클릭하여 Copy Object Catalog JSON으로 생성한 JSON을 Realtime 네임스페이스의 syncSetting.objectCatalog에 설정합니다. RPC의 이름이나 Prefab을 바꾸면 다시 생성합니다.
  8. 이벤트·프로퍼티·마스터 클라이언트를 대체한다: 대응표대로 대체합니다.
  9. UDP를 사용한다: WebGL 이외에서는 RealtimeUdpChannel로 정기적인 상태 갱신을 UDP로 전송합니다.

코드 변경 예

RPC

// Photon PUN2
[PunRPC]
void Fire(Vector3 direction, PhotonMessageInfo info) { /* ... */ }
photonView.RPC(nameof(Fire), RpcTarget.Others, direction);

// GS2-Realtime
[NetworkRpc]
void Fire(Vector3 direction, NetworkRpcInfo info) { /* ... */ }
entity.Rpc(nameof(Fire), RpcTarget.Others, direction);

생성과 소유자 판정

// Photon PUN2
var player = PhotonNetwork.Instantiate("Player", spawnPoint, Quaternion.identity);
if (player.GetComponent<PhotonView>().IsMine) { /* 입력을 활성화한다 */ }

// GS2-Realtime(Prefab ID 1을 Prefab Catalog에 등록해 둔다)
var player = world.Instantiate(1, spawnPoint, Quaternion.identity);
if (player.IsMine) { /* 입력을 활성화한다 */ }

입력을 받는 스크립트는 NetworkEntity의 Controlled Behaviours에 등록하면 쓸 수 있는 동안에만 자동으로 활성화됩니다.

값의 동기화

// Photon PUN2
public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
{
    if (stream.IsWriting) stream.SendNext(health);
    else health = (float)stream.ReceiveNext();
}

// GS2-Realtime(동기화 스키마에 float의 Health 필드를 선언해 둔다)
public void OnNetworkWrite(NetworkEntity entity) => entity.SetFloat("Health", health);
public void OnNetworkRead(NetworkEntity entity, uint changed)
{
    if (entity.HasChanged("Health", changed)) health = entity.GetFloat("Health");
}

이벤트와 프로퍼티

// Photon PUN2
PhotonNetwork.RaiseEvent(GoalCode, team, new RaiseEventOptions { Receivers = ReceiverGroup.All }, SendOptions.SendReliable);
PhotonNetwork.CurrentRoom.SetCustomProperties(new Hashtable { { "stage", "forest" } });

// GS2-Realtime
world.RaiseEvent("Goal", RpcTarget.All, team);   // [NetworkEvent] void Goal(int team)를 등록해 둔다
world.SetRoomProperty("stage", "forest");

주의 사항

  • 확정은 비동기: world.Instantiate는 즉시 사용할 수 있지만, 룸이 거부하면 SpawnFailed가 호출되고 삭제됩니다. 커스텀 프로퍼티는 룸을 경유하여 전원에게 도착한 시점에 반영됩니다.
  • 값은 반올림된다: 위치는 0.01, 회전은 360/4096도의 단위 간격으로 전송합니다. 정밀도를 바꾸려면 동기화 스키마에서 지정합니다.
  • 권한은 서버가 검증한다: 소유자 이외의 쓰기는 거부됩니다. 오브젝트 카탈로그를 설정하면 등록하지 않은 Prefab과 RPC, 허가하지 않은 소유권 취득도 거부됩니다.
  • RPC 인수의 상한: 합계 4096바이트까지입니다. 큰 데이터는 나누어 전송합니다.
  • 전송 순서: UDP로 전송하는 정기적인 갱신과 WebSocket으로 전송하는 RPC나 이벤트 사이의 순서는 보장하지 않습니다. RPC 직전에 변경한 값은 RPC보다 먼저 도착합니다.
  • 룸 종료 조건: 참가자 전원으로부터 1분간 통신이 없으면 룸은 종료됩니다. 룸 종료 조건과의 관계를 참조해 주세요.
  • WebGL: UDP를 사용할 수 없으므로 모두 WebSocket으로 전송합니다.
  • Photon의 다른 제품: Photon Voice나 Photon Chat에 해당하는 기능은 GS2-Realtime에 포함되어 있지 않습니다. 텍스트 채팅에는 GS2-Chat을 사용할 수 있습니다.