RPCs 와 RaiseEvent
다른 Photon 패키지를 제외하고 PUN과 PUN2의 특징 중 하나는 Remote Procedure Calls
(RPCs)를 지원한다는 것 입니다.
원격 프로시져 호출(Remote Procedure Calls)
원격 프로시져 호출은 이름 그대로 (같은 룸에 있는)원격 클라이언트에 있는 메소드를 호출 하는 것 입니다.
메소드에 대해서 원격 호출을 사용하려면 [PunRPC]
속성을 적용 해야 합니다.
C#
[PunRPC]
void ChatMessage(string a, string b)
{
Debug.Log(string.Format("ChatMessage {0} {1}", a, b));
}
RPC로 표기된 함수를 호출 하기 위해서 PhotonView
컴포넌트가 필요 합니다. 호출 예:
C#
PhotonView photonView = PhotonView.Get(this);
photonView.RPC("ChatMessage", RpcTarget.All, "jup", "and jup!");
프로 팁: 스크립트가 MonoBehaviourPun
이라면 this.photonView.RPC()
를 사용 할 수 있습니다.
그래서 호출하려는 메소드를 직접 호출하는 것 대신에 PhotonView 의 RPC()
에 호출 하려는 메소드 이름을 제공하여 호출 합니다.
타켓들과 파라미터들
PhotonView는 RPC 의 "타켓"과 같습니다:
모든 클라이언트들은 특정 PhotonView 가 있는 네트워크 게임 오브젝트의 메소드만을 실행 합니다.
만약 특정 객체를 타격했다고 한다면 "ApplyDamage" RPC 를 호출하여 수신 클라이언트가 동일한 객체에 피해를 적용시킬 것입니다!
복수개의 파라미터를 추가 할 수 있습니다(PUN 이 복수개 파라미터를 처리하기 위하여 직렬화를 제공합니다).
복수개의 파라미터를 추가 했을 때 메소드와 호출은 동일한 파라미터를 가지고 있어야 합니다.
만약 수신 클라이언트가 일치하는 메소드를 찾지 못하면 에러가 기록 됩니다.
이 규칙에 대해 한가지의 예외 사항이 있습니다:
RPC 메소드의 마지막 파라미터는 PhotonMessageInfo
타입이 될 수 있는데 개별 호출에 대한 컨텍스트(context)를 제공 할 것입니다.
호출할 때 PhotonMessageInfo 를 설정하지 않습니다.
C#
[PunRPC]
void ChatMessage(string a, string b, PhotonMessageInfo info)
{
// the photonView.RPC() call is the same as without the info parameter.
// the info.Sender is the player who called the RPC.
Debug.Log(string.Format("Info: {0} {1} {2}", info.Sender, info.photonView, info.timestamp));
}
PhotonMessageInfo
의 sender 와 PhotonView의 "targetting" 으로 추가 매개 변수 없이 누군가 쏘는 것을 구현 할 수 있습니다.
누가 쏘았고 무엇이 맞았는지 알게 됩니다.
타켓,버퍼링과 순서
어떤 클라이언트가 RPC 를 실행할지 정의 할 수 있습니다. RpcTarget
의 값을 사용하여 RPC를 실행할 클라이언트를 정의 합니다.
대부분의 경우에 All
로 설정하여 모든 클라이언트가 RPC 호출할 수 있도록 합니다.
때로는 Others
로 일부분만 할 수 있도록 정의 하기도 합니다.
RpcTarget
은 Buffered
로 끝나는 몇 개의 값이 있습니다.
서버는 이러한 RPC들을 기억하고 있으며 새로운 플레이어가 참여했을 때 이전에 발생했던 것 까지 포함하여 RPC 를 얻습니다.
이러한 것을 사용할 때는 긴 버퍼 때문에 참여시간이 느려질 수 있다는 것에 주의 하시기 바랍니다.
RpcTarget
은 ViaServer
로 끝나는 값이 있습니다.
일반적으로 전송 클라이언트가 RPC를 실행해야만 할 때 직접 하게 됩니다 - 서버를 통해 RPC 전송 없이.
이렇게 하면 로컬에서 메소드를 호출 할 때 지연이 없기 때문에 이벤트의 순서에 영향이 있습니다.
ViaServer
는 "All" 숏컷을 사용할 수 없도록 합니다.
RPC가 순서대로 실행되어야 할 때 매우 흥미로운 것입니다:
RPC는 서버를 통하여 전송되고 모든 수신 클라이언트에 의해서 동일 순서로 실행됩니다.
순서는 서버에 도착하는 순서 입니다.
예제:레이싱 게임에서 "완주"를 AllViaServer
로서 전송 할 수 있습니다.
첫번째 "완주" RPC 호출은 누가 이겼는지 알려 줍니다.
이후의 "완주" 호출은 누가 몇위인지 알려줄 것입니다.
RPC 이름의 숏컷
문자열은 네트워크를 통한 전송에서 가장 비효율적이기 때문에 PUN은 문자열을 줄여서 전송하는 트릭을 사용 합니다.
PUN은 에디터에 있는 RPC를 감지하고 목록을 컴파일 합니다.
각 메소드 이름은 리스트를 통해 ID를 얻고 RPC를 이름으로 호출 할 때 PUN은 아무도 모르게 ID를 보낼 것 입니다.
숏컷 때문에 게임의 다른 빌드들은 RPC에 대해서 동일한 ID를 사용할 수 없습니다.
이것이 문제가 된다면 숏컷을 사용할 수 없게 설정 할 수 있습니다.
동일 빌드의 클라이언트 경우에는 문제가 되지 않습니다.
RPC 목록은 PhotonServerSettings
를 통해서 저장되고 관리됩니다.
RPC 호출이 프로젝트의 다른 빌드로 인하여 이상이 발생하게 되면 RPC 목록을 검토 하세요.
Get HashCode 버튼은 해시코드를 계산하여 프로젝트 폴더를 쉽게 비교 할 수 있습니다.
필요하다면 리스트를 클리어(clear)할 수 있으며(Clear RPCs 버튼) Refresh RPC List 버튼을 클릭하여 리스트를 갱신할 수 있습니다.
RPC의 타이밍과 로딩 레벨
RPC는 특정 PhotonViews에서 호출 되며 수신측에서 매칭되는 하나의 클라이언트가 목표 지점 입니다.
만약 원격 클라이언트가 로드되지 않았거나 아직 매칭되는 PhotonView 생성을 하지 못했으면 RPC 는 손실 됩니다!
이 때문에 RPC 손실에 대한 전형적인 원인은 클라이언트가 새로운 신(scene)을 로드 할 때 입니다. 하나의 클라이언트는 새로운 게임오브젝트들과 함께 신을 로드 했으나 다른 클라이언트들이 신을 로드 하지 못했다면 이 클라이언트들은 RPC를 알 수가 없습니다(동일 신을 로드 하기 전까지)
PUN은 이것을 관리 할 수 있습니다. 접속 전에 PhotonNetwork.automaticallySyncScene = true
로 설정하고 룸의 마스터 클라이언트에게 PhotonNetwork.LoadLevel()
를 사용하시면 됩니다. 이 방식으로 하나의 클라이언트가 룸내의 모든 클라이언트들이 로드해야할 레벨을 정의합니다.
RPC 손실을 방지 하기 위해서 클라이언트는 수신 되는 메시지 처리를 멈출 수 있습니다(LoadLevel이 해줍니다).
RPC의 전송 실패를 방지하기 위해 클라이언트가 보내 오는 RPC의 실행을 중지 할 수 있습니다 (즉 LoadLevel이 할 것입니다). 신을 로드 하라는 RPC 요청을 받았을 때 내용이 모두 초기화 되기 전까지 즉시 IsMessageQueueRunning = false
를 설정 합니다.
예제:
C#
private IEnumerator MoveToGameScene()
{
// Temporary disable processing of futher network messages
PhotonNetwork.IsMessageQueueRunning = false;
Application.LoadLevel(levelName);
}
메시지 큐를 사용하지 않도록 설정 해놓으면 큐가 잠금 해제 될 때까지는 메시지의 송수신이 지연 될 것입니다.
다음의 단계로 진행 할 때 큐를 잠금 해제하는 것은 매우 중요합니다.
RaiseEvent
일부 경우에서 RPC가 원하는 기능이 아닐 수 도 있습니다.
RPC는 PhotonView와 호출할 메소드가 필요 합니다 .
PhotonNetwork.RaiseEvent
로 자신만의 이벤트를 구성할 수 있으며 네트워크 객체에 관계 없이 이벤트들을 전송할 수 있습니다. 코드와 내용을 구성하여 전송 합니다:
이벤트는 고유 식별자인 이벤트 코드를 사용하여 묘사합니다. 사진에서 이 이벤트 코드는 최대 256개의 다른 이벤트를 허용하는 바이트 값으로 설명됩니다. 그러나 이 중 일부는 이미 Photon 자체에서 사용되고 있으므로 사용자 지정 이벤트에 모두 사용할 수는 없습니다. 내장 이벤트를 모두 제외해도 최대 200개의 사용자 지정 이벤트 코드를 사용할 수 있습니다 [0..199]. 다음 예제에서는 여러 유닛이 특정 위치로 이동하도록 하는 이벤트가 있습니다.
C#
byte evCode = 0; // Custom Event 0: Used as "MoveUnitsToTargetPosition" event
object[] content = new object[] { new Vector3(10.0f, 2.0f, 5.0f), 1, 2, 5, 10 }; // Array contains the target position and the IDs of the selected units
RaiseEventOptions raiseEventOptions = new RaiseEventOptions { Receivers = ReceiverGroup.All }; // You would have to set the Receivers to All in order to receive this event on the local client as well
SendOptions sendOptions = new SendOptions { Reliability = true };
PhotonNetwork.RaiseEvent(evCode, content, raiseEventOptions, sendOptions);
여기서 어떤 작업을 하고 있는지 자세히 알아보겠습니다. 이 경우 이벤트를 발생시키기 전에 이벤트 코드를 정의하고 있습니다. 사용자 정의 이벤트가 여러 개 있는 경우 다음과 같이 사용된 클래스에서 정의하는 것이 좋습니다:
C#
private readonly byte MoveUnitsToTargetPositionEvent = 0;
RaiseEvent 기능을 호출할 때도 이 기능을 사용할 수 있습니다.
C#
PhotonNetwork.RaiseEvent(MoveUnitsToTargetPositionEvent, content, raiseEventOptions, sendOptions);
PUN은 어떤 종류의 컨텐츠도 직렬화할 수 있습니다. 이 경우 이 예에서는 서로 다른 유형이 있기 때문에 객체의 배열을 사용합니다.
세 번째 파라미터는 RaiseEventOptions에 대해 설명합니다. 예를 들어, 이러한 옵션을 사용하여 서버에 이벤트를 캐시할지, 이 이벤트를 수신할 클라이언트를 선택하거나 이 이벤트를 전달할 관심 그룹을 선택할 수 있습니다. 이러한 옵션을 직접 정의하는 대신 기본값인 RaiseEventOptions를 적용하는 null
을 사용할 수도 있습니다. 발신자도 이 이벤트를 수신하기를 원하므로 수신자를 ReceiverGroup.All
으로 설정하고 있습니다. 이 페이지 하단에 RaiseEventOptions 에 대한 자세한 정보가 있습니다.
마지막 파라미터는 SendOptions에 대해 설명합니다. 이 옵션을 사용하면 예를 들어 이 이벤트가 신뢰할 수 있는지 신뢰할 수 없는지 여부를 선택하거나 메시지를 암호화해야 하는지 여부를 선택할 수 있습니다. 예제에서, 단지 우리의 이벤트가 신뢰할 수 있는 것으로 보내지는 것을 확실히 하고 싶습니다.
IOnEventCallback 콜백
커스텀 이벤트를 받기 위해, 우리는 두 가지 다른 가능성을 가지고 있습니다. 첫 번째는 IOnEventCallback
인터페이스를 구현하는 것입니다. 이를 구현할 때는 OnEvent
콜백 핸들러를 추가해야 합니다. 이 핸들러의 함수는 다음과 같습니다:
C#
public void OnEvent(EventData photonEvent)
{
// Do something
}
이 핸들러를 올바르게 등록하기 위해 Unity의 OnEnable 및 OnDisable 함수를 사용할 수 있습니다. 또한 콜백 핸들러를 올바르게 추가 및 제거하고 있으며 이 특정 핸들러와 관련된 어떠한 문제에도 부딪히지 않도록 하는 방법을 통해 콜백 핸들러를 올바르게 추가 및 제거할 수 있습니다.
C#
public void OnEnable()
{
PhotonNetwork.AddCallbackTarget(this);
}
public void OnDisable()
{
PhotonNetwork.RemoveCallbackTarget(this);
}
수신된 정보로 작업을 수행하려면, OnEvent
함수가 다음과 비슷해 보일 수 있습니다.
C#
public void OnEvent(EventData photonEvent)
{
byte eventCode = photonEvent.Code;
if (eventCode == MoveUnitsToTargetPositionEvent)
{
object[] data = (object[])photonEvent.CustomData;
Vector3 targetPosition = (Vector3)data[0];
for (int index = 1; index < data.Length; ++index)
{
int unitId = (int)data[index];
UnitList[unitId].TargetPosition = targetPosition;
}
}
}
먼저 수신된 이벤트 코드가 이전에 설정한 코드와 일치하는지 확인합니다. 일치한다면, 우리는 이전에 보낸 형식에 이벤트 내용을 캐스팅하고 있습니다. 이것은 예제에서의 객체 배열입니다. 이후 해당 배열에서 대상 위치를 가져옵니다. 이 위치는 앞서 콘텐츠에 추가한 첫 번째 개체입니다. 아시다시피, 해당 배열에 바이트 값이 남아 있기 때문에, 루프를 사용하여 나머지 데이터를 반복할 수 있습니다. 이 배열의 각 바이트 값에 대해 고유 식별자를 얻고 이를 사용하여 UnitList(기본적으로 목록 또는 딕셔너리와 같은 단위를 포함하는 구조)에서 특정 단위를 가져와 새 목표 위치를 적용합니다.
LoadBalancingClient.EventReceived
사용자 지정 이벤트를 수신하는 두 번째 방법은 이벤트가 수신될 때마다 호출되는 방법을 등록하는 것입니다. 이를 위해 이전처럼 Unity의 OnEnable 및 OnDisable 함수를 사용할 수 있습니다.
C#
public void OnEnable()
{
PhotonNetwork.NetworkingClient.EventReceived += OnEvent;
}
public void OnDisable()
{
PhotonNetwork.NetworkingClient.EventReceived -= OnEvent;
}
참고: OnEvent 기능 자체는 IOnEventCallback 콜백 섹션과 동일하므로 여기서는 이 기능을 다시 다루지 않습니다.
내부적으로, PUN은 또한 거의 모든 통신을 위해 RaiseEvent를 사용합니다.
Raise 이벤트 옵션
이벤트가 버퍼링 등의 경우 RaiseEventOptions
파라미터로 어떤 클라이언트가 이벤트를 받을지 정의할 수 있습니다.
수신자 그룹
"수신자 그룹"은 이벤트를 수신하는 사용자를 정의하는 한 가지 방법입니다.
이 옵션은 RaiseEventOptions
을 통해 사용할 수 있습니다.
정의된 그룹은 세 가지입니다.
- "Others" : 동일한 룸에 참여하고 있는 모든 다른 활성 엑터.
- "All" : 동일한 룸에 참여하고 있는 모든 활성 엑터.
- "MasterClient": 룸 내에서 Master Client로 지정된 것.
관심 그룹
"관심 그룹"은 이벤트를 수신하는 사용자를 정의하는 대체 방법입니다.
글로벌 그룹 0을 사용하여 모든 클라이언트에 대한 이벤트를 생성할 수 있습니다. 또한 0이 아닌 특정 그룹을 사용하여 특정 그룹에 대한 이벤트만 생성할 수 있습니다. 특정 그룹에 전송된 이벤트를 수신하려면 클라이언트가 먼저 이 그룹에 가입해야 합니다. 여기에서 관심 그룹의 자세한 내용을 볼 수 있습니다.
대상 액터
"대상 액터"는 이벤트를 받는 사람을 정의하는 세 번째 방법입니다.
이렇게 하면 고유한 액터 번호를 사용하여 선택할 수 있는 이벤트를 하나 이상의 특정 클라이언트에게 이벤트를 발생 시킬 수 있습니다.
캐싱 옵션
가장 재미있는 옵션은 이벤트 캐시/버퍼링입니다.
PUN은 인스턴스화를 위해 이 기능을 사용하며, 새로운 (참여)플레이어가 룸에 있기 전부터 발생한 이벤트를 받아야 할 때 유용할 수 있습니다.
RaiseEventOptions.EventCaching
에는 세개의 중요한 옵션이 있습니다:
AddToRoomCache
, AddToRoomCacheGlobal
그리고 RemoveFromRoomCache
입니다.
이벤트에서 해시 테이블로 전송할 때 최고로 잘 동작합니다.
EventCaching.AddToRoomCache
로 RaiseEvent를 호출하는 것은 ,이벤트가 서버의 캐시에 놓여지게 되는 것 입니다.
즉, 나중에 합류하는 모든 플레이어들도 이 이벤트를 받게 됩니다.
새 플레이어는 서버에 도착한 순서대로 캐시된 이벤트를 받습니다.
플레이어가 떠나면 캐시된 이벤트가 자동으로 캐시에서 제거됩니다.
특정 이벤트에 대해 이 문제를 방지하려면 EventCaching.AddToRoomCacheGlobal
로 이벤트를 호출합니다.
그러면 이벤트가 "룸의 이벤트 캐시"로 전환됩니다.
캐쉬에 이벤트를 많이 넣으면 방에 들어가면 새로운 플레이어가 많은 메시지를 받게 됩니다.
이 작업은 더 많은 이벤트가 발생할 경우 잠시 시간이 걸릴 수 있으므로 더 이상 관련이 없는 것들을 정리해야 합니다. 이 정리작업은 EventCaching.RemoveFromRoomCache
로 수행됩니다.
RemoveFromRoomCache
를 사용하면 RaiseEvent의 이벤트 코드가 필터로 사용됩니다.
따라서 일부 이벤트를 설정하는 대신 이벤트의 모든 인스턴스를 제거할 수 있습니다.
세부적인 제어를 위해 이벤트의 내용을 필터링에 사용할 수 있습니다.
이를 위해 해시테이블을 컨텐츠 유형으로 사용해야 합니다.
키/값 쌍을 설정하여 특정 이벤트를 식별할 수 있으며 RemoveFromRoomCache
를 사용하여 이벤트를 발생시키면 해당 키/값 쌍만 내용 필터에 표시됩니다.
이러한 방식으로 개별 이벤트 또는 일부 개체 또는 턴 또는 기타 항목에 속하는 이벤트를 식별할 수 있습니다.
여기에서 이벤트 캐시 대한 자세한 내용을 읽어 보십시오.
전송 옵션
SendOptions
파라미터로, 이벤트가 신뢰할 수 있도록 전송되거나 암호화 할지를 정의 할 수 있습니다.
신뢰성
Reliability
옵션은 이벤트가 신뢰할 수 있는지 신뢰할 수 없게 전송되는지를 나타냅니다.
암호화
With the Encrypt
옵션을 통해, 이벤트가 전송전에 암호화되는지를 정의할 수 있습니다. 기본값은 이벤트가 암호화 되지 않는 것 입니다. 힌트: 암호화를 사용하기 전에, 접속이 되어야 합니다.