Photon Unity Networking  v1.81
English | 日本語 | 한국
일반 문서

Photon, 가입, 호스팅 옵션과 시작방법에 대하여 개략적인 개요를 보여 줍니다.

[목차]

Photon

유니티 내장 네트워킹과는 달리, PUN 은 룸, 매치메이킹과 룸 안에서 플레이어간 대화 기능을 제공 하는 전용 서버에 항상 접속 합니다. 내부적으로는 Photon 유니티 네트워킹은 하나 이상의 서버를 사용합니다: 몇 개의 "게임 서버" 들은 실제 룸을 운영하며 "마스터 서버"는 룸과 매치 플레이어들의 상태를 추적하게 됩니다.

서버 측에는 두 가지의 옵션이 있습니다.

Exit Games 클라우드

Exit Games 클라우드는 Exit Games 에 완전하게 관리되며 호스트 되고 로드 밸런싱된 Photon 서버를 제공하는 서비스 입니다. 무료로 체험해 보실 수 있으며 상업용 가입 비용은 정말 저렴 합니다.

서비스는 고정된 로직으로 수행되기 때문에 서버측 게임 로직을 전혀 구현 할 필요가 없습니다. 대신 클라이언트는 인증을 받아야 합니다.

클라이언트들은 게임 타이틀과 “game version”에 관련된 “application id”로 분리 됩니다. 이것을 통해 플레이어들은 다른 개발자 또는 이전 게임과 절대 충돌이 발생 하지 않습니다.

에셋 스토어에서 서브스크립션 구매

에셋 스토어에서 Photon 클라우드 서브스크립션 패키지를 구매하셨다면 아래의 단계를 따라주세요:

Photon Server SDK

Photon 클라우드 서비스의 대안으로 나만의 서버를 운영하며 “Load Balancing” C# 솔루션을 기반으로 서버측 로직을 개발 할 수 있습니다. 이렇게 하면 서버 로직을 완전하게 제어 할 수 있습니다.

Photon Server SDK 는 다음 링크에서 다운로드 받을 수 있습니다: https://www.photonengine.com/ko-kr/OnPremise/Download

서버 시작하기: doc.exitgames.com/en/onpremise/current/getting-started/photon-server-in-5min

Photon Unity Networking - 첫 번째 단계

PUN 을 임포트 했을 때, "Wizard" 윈도우가 팝업 될 것입니다. 클라우드 등록을 위해 이메일 주소를 입력 하거나 기존 계정의 AppID 를 입력하여 이 단계를 건너 뛰던지 "self hosted" Photon 을 선택하여 서버의 주소를 입력 합니다.

이렇게 하면 프로젝트내의 클라우드 서비스 또는 내 Photon 서버에 환경설정 파일을 생성하게 됩니다:PhotonServerSettings

PUN 은 상당수의 파일로 구성되어 있으며 이 중 가장 중요한 것은 PhotonNetwork 입니다. 이 클래스에는 필요한 모든 함수들과 변수들이 있습니다. 커스텀 요구사항이 있다면 언제라도 소스 파일을 변경 할 수 있습니다 - 이 플러그인은 결국 Photon 플러그인의 구현인 것 입니다.

UnityScript 에서 PUN 을 사용하기 위해서는 "PhotonNetwork" 와 "UtilityScripts" 두개 폴더 모두 Assets\ 로 이동 시키시면 됩니다.

이 API 가 어떻게 동작하는지 확인하기 위해 몇 개의 예제가 있습니다.

마스터 서버와 로비

PUN 은 항상 한 대의 마스터 서버와 여러 대의 게임서버를 사용 합니다. 마스터 서버는 여러 게임 서버에서 수행되고 있는 게임들을 관리하며 룸 생성 또는 룸에 참여할 때 게임 서버 주소를 제공해 줍니다. 마스터 서버는 여러 대의 게임서버에서 현재 진행중인 게임들을 관리하고 룸에 참여 또는 생성할 때 게임 서버의 주소를 제공 해 줍니다. PUN(클라이언트)은 전달된 주소의 게임서버로 자동으로 전환 됩니다.

개별 경기는 룸으로 알려져 있습니다. 서로 독립적이며 이름으로 식별됩니다. 룸들은 하나 또는 여러개의 로비로 묶여집니다. 로비는 매치메이킹에서 옵션 사항입니다. 명시적으로 커스텀 로비를 사용하지 않으면 PUN 은 모든 룸에 대하여 단일 로비를 사용하게 될 것 입니다.

기본적으로 PUN 은 접속 후 디폴트 로비에 참여할 것 입니다. 이 로비는 클라이언트에게 존재하고 있는 룸의 목록을 전송하여 플레이어가 룸의 이름 또는 룸의 프로퍼티를 통해 룸을 고를 수 있도록 합니다. PhotonNetwork.GetRoomList() 메소드를 이용하여 현재 룸 목록을 받습니다. 이 리스트는 트래픽을 낮게 유지 하기 위해서 일정 간격으로 갱신됩니다.

클라이언트는 룸에 참여하거나 생성하기 위해서 로비에 반드시 참여할 필요는 없습니다. 클라이언트에게 룸 목록을 보여주고 싶지 않으면 연결전에 PhotonNetwork.autoJoinLobby = false 로 설정하면 로비를 거치지 않습니다.

게임에서 필요에 따라 룸 목록을 관리할 하나 이상의 로비를 사용할 수 있습니다. PhotonNetwork.JoinLobby 는 특정 로비에 참여하도록 해주는 메소드 입니다. 클라이언트에서 룸 리스트를 구성할 수 있으며 - 서버는 룸 리스트를 추적할 것입니다. 이름과 타입이 같으면 TypedLobby 는 모든 클라이언트에게 동일 합니다.

클라이언트는 항상 하나의 로비에만 존재하며 로비에 있는 동안 생성된 룸은 이 로비와 관계가 있을 것 입니다. 로비들이 여러개가 있다는 의미는 클라이언트에게 룸 목록내의 룸 개수가 적어지게 되므로 클라이언트에게 좋습니다. 룸 목록에는 제한이 없습니다.

JoinRoom, JoinRandomRoom 과 CreateRoom 메소드의 파라미터를 통하여 룸에 참여 하지않고 로비를 고를 수 있도록 할 수 있습니다.

로비내의 플레이어들은 서로 알수가 없으며 데이터(혼잡해 질 때의 이슈를 방지 하기 위해)를 전송할 수 도 없습니다.

서버들은 모두 전용 머신에서 수행되고 있습니다 - 플레이어-호스트된 ‘서버’와 같은 것은 없습니다. API에서 서버 관리에 대한 귀찮은 작업은 내부적으로 다 알아서 처리하고 있으므로 서버 관리에 대해 전혀 신경 쓸 필요가 없습니다.

위 코드는 PhotonNetwork 기능을 이용하기 위한 필수 사항 입니다. 클라이언트 게임버전을 설정하고 PhotonServerSettings 에 저장되어 있는 설정-마법사에서 설정해 놓은 값을 이용 합니다. 설정 마법사는 나만의 Photon 서버를 호스트 할 때 도 사용 될 수 있습니다. 다른 방식으로는 Connect() 를 사용하여 PhotonServerSettings 파일의 내용을 무시할 수 있습니다.

버져닝

Photon 의 로드밴런싱 로직은 appID 를 이용하여 플레이어를 서로 분리 합니다.

하나의 AppId 내에서 "connect" 메소드(파라미터로)에 설정되는 게임 버전" 문자열로 클라이언트/플레이어를 고의로 분리시킬 수 있습니다.

노트: 서로 다른 버전의 Photon Unity Networking 호환성을 보장 할 수는 없으므로 게임 버전에 PUN 버전을 추가 합니다. 게임버전에 "_" + PhotonNetwork.versionPUN 를 추가합니다.

게임 생성 및 참여

다음에 해야 할 것은 룸에 참여하거나 생성을 하는것 입니다. 다음의 코드는 일부 필요 함수들입니다:

//Join a room
//Create this room.
// Fails if it already exists and calls: OnPhotonCreateGameFailed
//Tries to join any random game:
//Fails if there are no matching games: OnPhotonRandomJoinFailed

현재 진행 중인 게임들의 목록은 마스터 서버의 로비에서 제공됩니다. 다른 룸 처럼 참여할 수 있으나 룸의 목록만 제공하고 갱신 합니다. PhotonNetwork 플러그인은 접속 후에 자동으로 로비에 참여할 것 입니다. 룸에 참여하고 있을 때는 룸 리스트가 더 이상 갱신되지 않을 것 입니다.

룸들의 목록을 표시(로배내에서):

foreach (RoomInfo game in PhotonNetwork.GetRoomList())
{
GUILayout.Label(game.name + " " + game.playerCount + "/" + game.maxPlayers);
}

다른 방식으로 게임은 무작위 매치메이킹을 사용 할 수 있습니다: 자동으로 아무 룸에 참여를 시도하고 플레이어가 참여할 수 있는 방이 없을 때 실패 합니다. 이런 경우에는 이름 없이 룸을 생성하고 무작위로 다른 플레이어가 참여 할 때 까지 대기 합니다.

고급 매치메이킹 & 룸 프로퍼티

모두 무작위 매치메이킹을 하는것은 플레이어가 항상 재미있게 즐길 수 있는 것은 아닙니다. 때로는 특정 맵 또는 2대2 경기를 하고 싶을수도 있습니다.

Photon 클라우드와 로드 밸런싱에서는 임의의 룸 프로퍼티를 설정하여 JoinRandom 에서 필터로 이용 할 수 있습니다.

룸 프로퍼티와 로비

룸 내의 모든 플레이어들은 룸 프로퍼티들이 동기화되고 현재의 맵, 라운드, 시작시간 등을 추적할 때 매우 유용하게 사용됩니다. 룸 프로퍼티는 문자열 키를 가지고 있는 해시테이블로 처리 됩니다. 짧은 키를 더 많이 사용 합니다.

선택된 프로퍼티들을 로비로 포워딩 할 수 있습니다. 리스팅 하고 무작위 매치메이킹에도 사용 할 수 있습니다. 모든 룸 프로퍼티들이 로비에서 유용하지는 않으므로 룸 생성시 로비에서 사용할 프로퍼티들을 정의 합니다.

Hashtable roomProps = new Hashtable() { { "map", 1 } };
string[] roomPropsInLobby = { "map", "ai" };
RoomOptions roomOptions = new RoomOptions() { customRoomProperties = roomProps, customRoomPropertiesForLobby = roomPropsInLobby }
CreateRoom(roomName, roomOptions, TypedLobby.Default)

"ai"는 아직 키가 아니라는 것을 주목하세요. Room.SetCustomProperties 를 통하여 게임에서 설정되기 전까지는 로비에서 나타나지 않을 것 입니다. "map" 또는 "ai" 의 값을 변경했을 때 약간 지연된 후에 로비에서 갱신됩니다.

리스트를 가능한 짧게 하여 클라이언트가 리스트를 로딩할 때 성능상에 문제가 없도록 해주시기 바랍니다.

무작위 참여시의 룸 프로퍼티 필터링

JoinRandom 에서 필요한 룸 프로퍼티들과 최대 플레이어 값을 해시테이블로 전달 할 수 있습니다. 이것들은 서버가 "피팅" 룸을 선택 할 때 필터로서 작동합니다.

Hashtable expectedCustomRoomProperties = new Hashtable() { { "map", 1 } };
JoinRandomRoom(expectedCustomRoomProperties, 4);

더 많은 프로퍼티 필터를 전달 하게 되면 룸이 매칭되는 확률이 낮아 지게 됩니다. 옵션을 제한하는 것이 더 좋습니다.

로비에서 알수 없는 프로퍼티는 필터 하지 않도록 하시기 바랍니다(위를 참고 하세요).

MonoBehaviour 콜백

PUN 은 "연결" 또는 "게임에 참가" 와 같은 상태 변화를 인지 하기 위하여 몇 개의 콜백을 사용 합니다. 해주어야 할 부분은 MonoBehaviour 안에서 맞는 메소드를 구현하고 이벤트가 발생 했을 때 호출 해 주는 것 입니다.

사용할 수 있는 콜백에 대한 개요는 Photon.PunBehaviour 에서 보는 것이 좋습니다. MonoBehaviour 대신에 PunBehaviour 스크립트를 작성한다면 각 콜백을 쉽게 오버라이드 할 수 있습니다. IDE 에서 "override" 입력하면 콜백 리스트가 나오게 되므로 코딩할 때 쉽게 찾을 수 있을 것 입니다.

여기에서는 게임 룸 설정에 대한 기본을 다루고 있습니다. 다음 단계는 게임 내의 커뮤니케이션을 다룹니다.

룸 안으로 메시지 전송

룸 안에서는 연결된 다른 플레이어들에게 네트워크 메시지를 전송 할 수 있습니다. 또한 나중에 접속 할 플레이어(예를 들어 플레이어를 스폰하는 것)에게 전송할 수 있도록 버퍼화된 메시지도 보낼 수 있습니다.

메시지는 두 가지 방법을 사용하여 전송 할 수 있습니다. RPC 또는 PhotonView 프로퍼티 OnSerializePhotonView 를 이용하는 방식입니다. 하지만 여기에 더 많은 네트워크 작용이 있습니다. OnPhotonInstantiate 또는 OnPhotonPlayerConnected 등과 같은 특정한 네트워크 이벤트(예,OnPhotonInstantiate, OnPhotonPlayerConnected)에 대한 콜백을 Listen 할 수 있고 이러한 이벤트(PhotonNetwork.Instantiate)를 트리거 시킬 수 있습니다. 앞으로 이 주제들에 대해서 다룰 것이니 너무 혼란 스러워 하지 마세요.

PUN 에서 그룹 사용하기

그룹은 PhotonView 에서 변경될 때 동기화 되지 않습니다. 모든 클라이언트들에게 동일한 그룹내에 PhotonView를 유지할지는 필요에 의해 개발자가 결정합니다. 몇 개 클라이언트의 동일한 PhotonViwe에 서로 다른 그룹 번호를 사용하는 것은 일관성이 없어지는 원인이 됩니다.

일부 네트워크 메시지는 수신측에서만 수신자 그룹에 대하여 체크 됩니다. 다시말해:

이에대한 기술적 원인: Photon 서버는 메시지에 관심 그룹만을 지원 하는데 이 관심 그룹은 캐시되지 않으며 특정한 액터를 목표로 삼지 않습니다. 앞으로 이 사항은 변경 될 수 있습니다.

PhotonView

PhotonView는 메시지 전송을 위한 스크립트 컴포넌트 입니다(RPC와 OnSerializePhotonView). 게임오브젝트에 PhotonView 를 붙여 주세요. PhotonView는 유니티의 NetworkView 와 매우 유사하다는 점을 주목해주세요.

메시지 전송과 마음대로 다른 PhotonView 들의 인스턴스 생성/할당을 위하여 게임에서는 항상 최소 한개의 PhotonView 가 있어야 합니다.

게임오브젝트에 PhotonView 를 추가하기 위해서는 게임오브젝트를 선택하고 “Components/Miscellaneous/Photon View” 를 사용합니다.

doc-photonview.jpg
Photon View

Transform 관찰

PhotonView 의 Observe 프로퍼티에 트랜스폼을 추가하면 플레이어들의 위치, 회전과 확대비율 또는 이 모든 것을 조합한 정보를 동기화 할 수 있도록 선택 할 수 있습니다.프로토타이핑 또는 규모가 작은 게임에 매우 크게 도움이 됩니다. 노트 : 관찰하고 있는 값이 변경 되면 모든 관찰 값이 전송 됩니다 - 변경된 단일 값만이 아닙니다. 또한 변경된 사항은 평활화 되거나 보간 되지 않습니다.

MonoBehaviour 관찰

PhotonView 는 MonoBehaviour를 관찰하도록 설정 될 수 있습니다. 이 경우에는 스크립트의 OnPhotonSerializeView 메소드가 호출 될 것 입니다. 이 메소드는 스크립트가 로컬 플레이어에 의해 제어 되는지에 따라 객체의 상태를 쓰고 읽기 위해 호출 됩니다.

아래 간단한 몇 줄 안되는 코드로 캐릭터 상태의 동기화를 어떻게 추가 하는지 보여주고 있습니다.

{
if (stream.isWriting)
{
//We own this player: send the others our data
stream.SendNext((int)controllerScript._characterState);
stream.SendNext(transform.position);
stream.SendNext(transform.rotation);
}
else
{
//Network player, receive data
controllerScript._characterState = (CharacterState)(int)stream.ReceiveNext();
correctPlayerPos = (Vector3)stream.ReceiveNext();
correctPlayerRot = (Quaternion)stream.ReceiveNext();
}
}

“ReliableDeltaCompressed”를 보내고 있다면 동일한 순서로 스트림에 데이터를 항상 써야 한다는 것을 주의 해 주세요. PhotonStream 에 데이터를 쓰지 않으면 업데이트는 전송되지 않습니다. 잠시 멈출 때 유용하게 사용할 수 있습니다. 이제부터 다른 방식으로 통신하는 방법인 RPC 입니다.

원격 프로시져 호출

Remote Procedure Calls (RPC) 은 이름이 의미하는 것과 같이 동일한 룸에 있는 원격 클라이언트를 호출 하는 메소드 입니다. MonoBehaviour 메소드의 원격 호출을 가능하게 하려면 [PunRPC] 속성을 적용해야 합니다. 표시된 함수를 호출하기 위해서는 동일 게임오브젝트의 PhotonView 인스턴스가 필요합니다.

void ChatMessage(string a, string b)
{
Debug.Log("ChatMessage " + a + " " + b);
}

스크립트에서 메소드를 호출 하기 위해서는 PhotonView 객체에 접근해야 합니다. 대상 메소드를 직접 호출 하는 대신에 PhotonView.RPC() 를 호출하고 호출 할 메소드 이름을 알려 줍니다:일반적인 모든 MonoBehaviour 또는 GameObject 는 PhotonView 컴포넌트에 접근하고 여기에 있는 RPC를 호출하기 위해 PhotonView.Get(this) 를 사용할 수 있습니다.

PhotonView photonView = PhotonView.Get(this);
photonView.RPC("ChatMessage", PhotonTargets.All, "jup", "and jup!");

따라서 직접 대상 메소드를 호출하는 대신에 PhotonView 의 RPC() 를 호출 합니다. 플레이어가 메소드를 호출 할 메소드 이름을 제공하고 파라미터의 목록을 제공 합니다.

주의: RPC() 에서 사용하는 파라미터 목록은 필요한 파라미터의 개수와 일치 해야 합니다! 수신 클라이언트가 일치하는 메소드를 찾을 수 없으면 에러를 기록 하게 됩니다. 이 규칙의 한 가지 예외사항이 있습니다:RPC 메소드의 마지막 파라미터는 PhotonMessageInfo 타입이 될 수 있느데 각 호출에 대한 컨택스틀 제공 하게 될 것 입니다.

void ChatMessage(string a, string b, PhotonMessageInfo info)
{
Debug.Log(String.Format("Info: {0} {1} {2}", info.sender, info.photonView, info.timestamp));
}

RPC의 타이밍과 레벨 로딩

RPC는 특정 PhotonViews에서 호출 되며 수신측에서 매칭되는 하나의 클라이언트가 목표 지점 입니다. 만약 원격 클라이언트가 일치하는 PhotonView 를 알 수 없으면 그 RPC 는 손실 됩니다.

이 때문에 RPC 손실에 대한 전형적인 원인은 클라이언트가 새로운 씬(scene)을 로드하고 레벨을 설정 할 때 입니다. 한 클라이언트가 더 빠르게 로드 되었거나 룸에 더 오랜 시간 동안 있었고 다른 클라이인트가 아직 로드하지 못한 객체들에 대해서 중요한 RPC 들을 전송했을 경우 입니다. 동일한 현상은 RPC 가 버퍼 되었을 때도 발생합니다.

해결 방법은 신이 로드되는 동안 메시지큐를 잠시 멈추는 것 입니다. 아래의 코드는 어떻게 이것을 처리하는지 보여 줍니다:

private IEnumerator MoveToGameScene()
{
// Temporary disable processing of futher network messages
Application.LoadLevel(levelName);
}

선택적으로 PhotonNetwork.LoadLevel 을 사용할 수 있습니다. 이것을 사용하면 임시적으로 메시지큐를 사용할 수 없도록 하게 됩니다.

메시지 큐를 사용할 수 없도록하면 큐가 잠금해제되기 전까지 송수신 메시지의 지연이 발생됩니다. 확실히 계속 할 준비가 될 때 큐를 잠금 해제 하는 것이 중요 합니다.

이전에 로드되었던 씬에 속한 RPC 들이나 아직 도착하지 않은 것들은 이제 폐기 될 것 입니다. 하지만 RPC로 두 씬 사이를 끝내도록 정의할 수 있습니다.

다양한 주제들

네트워크 객체의 인스턴스 생성

모든 게임에서 플레이어에 하나 이상의 객체들을 생성 할 필요가 있습니다. 객체 생성의 다양한 옵션이 아래에 설명되어 있습니다.

PhotonNetwork.Instantiate

PUN에서는 PhotonNetwork.Instantiate 메소드에 시작위치, 회전정보 그리고 프리팹 이름을 전달하여 네트워크 객체를 자동적으로 스폰 할 수 있습니다. 필요사항 : 실행 시 로드를 위해 프리팹은 resources/ 폴더에 있어야 하며 PhotonView 컴포넌트가 있어야 합니다. 웹 플레이어 주의사항 : resources 폴더 내에 있는 모든 것은 기본적으로 첫 번째 신에서 스트림 될 것입니다. 웹 플레이어 설정의 “First streamed level”을 사용하여 resources 폴더의 에셋을 사용하는 첫 번째 레벨을 지정할 수 있습니다. 이렇게 지정해 놓으면 resources 폴더 에셋을 사용하지 않는 프리 로더와 메인메뉴는 속도가 느려지지 않습니다.

void SpawnMyPlayerEverywhere()
{
PhotonNetwork.Instantiate(“MyPrefabName”, new Vector3(0,0,0), Quaternion.identity, 0);
//The last argument is an optional group number, feel free to ignore it for now.
}

더 많이 제어 하기: 수동 인스턴스 생성

네트워크를 통한 객체 생성을 resources 폴더에 의존하고 싶지 않다면 이 섹션의 마지막에 나오는 예제 처럼 수동으로 생성해야 합니다.

수동 생성의 가장 큰 이유는 스트리밍 웹 플레이어에 어떤 것이 다운로드 되었는지에 대하여 제어를 하려고 하는 것입니다. 유니티의 스트리밍과 resources 폴더의 상세 내용은 여기에서 보실 수 있습니다.

수동으로 스폰한다면 직접 PhotonViewID를 할당 해야 하며 이러한 viewID 는 네트워크 경로를 통해 올바른 게임오브젝트/스크립트에 전달하기 위한 키가 됩니다. 새로운 객체를 소유하고 스폰하려는 플레이어는 PhotonNetwork.AllocateViewID() 를 사용하여 새로운 viewID 를 할당 해 주어야 합니다. 이 PhotonViewID는 모든 다른 플레이어들에게 이미 설정된(예를 들어 기존 신의 PhotonView) PhotonView 를 사용하여 전송되어야 합니다. 인스턴스 생성을 위한 RPC는 버퍼화 되어야 한다는 것을 명심 하세요 : 나중에 접속한 클라이언트도 역시 스폰 명령을 받아야 합니다. 객체 스폰을 위해 사용한 RPC 메시지는 원하는 프리팹의 레퍼런스가 필요하며 유니티의 GameObject.Instantiate 를 사용하여 이에 대한 객체를 생성합니다. 마지막으로 모든 PhotonView 들에게 PhotonViewID 를 할당하여 이 프리팹이 추가된 PhotonView들을 설정해야 합니다.

void SpawnMyPlayerEverywhere()
{
//Manually allocate PhotonViewID
PhotonViewID id1 = PhotonNetwork.AllocateViewID();
photonView.RPC("SpawnOnNetwork", PhotonTargets.AllBuffered, transform.position,
transform.rotation, id1, PhotonNetwork.player);
}
public Transform playerPrefab; //set this in the inspector
void SpawnOnNetwork(Vector3 pos, Quaternion rot, PhotonViewID id1, PhotonPlayer np)
{
Transform newPlayer = Instantiate(playerPrefab, pos, rot) as Transform;
//Set the PhotonView
PhotonView[] nViews = go.GetComponentsInChildren<PhotonView>();
nViews[0].viewID = id1;
}

네트워크 객체들을 로드 하기 위해 에셋 번들을 사용하기 원하면 에셋번들 로딩 코드를 추가하고 예제의 “playerPrefab”을 에셋 번들에 있는 프리팹으로 교체만 해주면 됩니다.

오프라인 모드

오프라인 모드는 멀티플레이어 게임 코드를 싱글 플레이어 게임 모드에서도 코드를 재사용 할 수 있는 기능 입니다.

Mike Hergaarden: M2H 포탈에서는 멀티플레이어 기능을 완전히 제거하기 위해서 우리의 게임을 몇 번이나 리빌드 해야 했습니다. 게다가 동일한 코드로 싱글과 멀티 플레이어를 사용할 수 있어 이 자체 만으로도 작업 시간을 상당히 줄여 주게 됩니다.

싱글 플레이어에서 사용하고 원하는 가장 일반적인 기능들은 RPC 전송과 PhotonNetwork.Instantiate 를 사용하는것 입니다. 오프라인 모드의 주 목적은 PhotonNetwork 기능을 사용할 때 접속되지 않은 경우 널 참조와 기타 에러들을 발생하지 않게 하는 것 입니다. 주의 할 점은 게임 설정에서 싱글플레이어 게임으로 실행 한다는 것으로 게임 실행동안에 모든 코드를 재사용 해야 한다는 것 입니다.

PhtotonNetwork 가 의도적으로 에러를 식별하기 위해 수동으로 오프라인모드를 사용할 필요가 있습니다. 오프라인 모드를 사용하게 하는 것은 매우 간단 합니다:

이제 연결 없이도 오류 없이 멀티 플레이어 메소드를 재 사용 할 수 있습니다. 더군다나 전혀 오버헤드가 없습니다. 아래는 오프라인 모드에서 발생한 PhotonNetwork 함수와 변수들의 결과입니다:

PhotonNetwork.player The player ID is always -1 PhotonNetwork.playerName Works as expected. PhotonNetwork.playerList Contains only the local player PhotonNetwork.otherPlayers Always empty PhotonNetwork.time returns Environment.TickCount or a more precise timer if enabled; PhotonNetwork.isMasterClient Always true PhotonNetwork.AllocateViewID() Works as expected. PhotonNetwork.Instantiate Works as expected PhotonNetwork.Destroy Works as expected. PhotonNetwork.RemoveRPCs/RemoveRPCsInGroup/SetReceivingEnabled/SetSendingEnabled/SetLevelPrefix While these make no sense in Singleplayer, they will not hurt either. PhotonView.RPC Works as expected.

위에서 보인 것 이외의 다른 메소드를 사용한다면 예상치 않은 결과가 나올 수 있으며 아무것도 하지 않을 수 도 있다는 것을 주의 하세요. 예를 들면 PhotonNetwork.room 은 명백하게 null 을 리턴할 것 입니다. 시작을 싱글 플레이어 게임으로 하고 나중에 멀티 플레이어로 확장하려고 의도 한다면 1 플레이어 게임 호스팅을 고려하는 것이 좋을 것 입니다. 이렇게 하면 오프라인 모드에서 버퍼화된 RPC와 인스턴스 생성 호출을 연결 후에 자동으로 수행하는 것이 아니라 유지 할 것입니다.

오프라인 모드를 종료 하려면 PhotonNetwork.offlineMode = false; 설정 또는 Connect() 메소드를 호출 하면 됩니다.

제약사항

뷰와 플레이어

성능상의 이유로 PhotonNetwork API 는 플레이어당 최대 1000 개의 PhotonView 와 최대 2,147,483 명의 플레이어까지 제한 하고 있습니다.(이렇게 해도 귀하의 하드웨어에서 지원할 수 있는 것보다 높습니다!) 최대 허용된 플레이어내에서 플레이어당 더 많은 PhotonView 를 쉽게 허용할 수 있습니다. 아래처럼 작업 하면 됩니다: PhotonView 는 모든 네트워크 메시지에 대해서 viewID 를 제출합니다. viewID 는 정수형로써 player ID 와 플레이어의 view ID 로 구성되어 있습니다. 정수의 최대 값은 2,147,483,647 이고 MAX_VIEW_IDS(1000) 로 나누면 2백만명 이상의 플레이어가 참여할 수 있습니다. 보시다시피 MAX_VIEW_IDS 수를 감소하여 플레이어 숫자를 쉽게 늘릴 수 있습니다. 다른 한편 최대 플레이어수를 감소시키고 플레이어에게 더 많은 VIEW_IDS를 부여 할 수 도 있습니다. 대부분의 게임에서는 플레이어당 view ID 는 몇 개 이상이 절대 필요하지 않다는 것에 주의 해주세요(캐릭터당 하나 또는 두개...이게 보통일 것입니다). 만약 더 많이 필요하다면 무엇인가 잘못 하고 있을 수 도 있습니다! PhotonView와 ID를 모든 총알에 할당 하는 것은 매우 비효율적입니다. 대신 플레이어 또는 무기의 PhotonView에 발사 된 탄환을 추적 하시기 바랍니다.

int 를 shot (-32,768 ~ 32,768) 값으로 줄임으로써 대역폭 향상을 시킬 수 있는 사항이 있습니다. MAX_VIEW_IDS 를 32로 설정하여 1023명의 플레이어까지 지원 할 수 있습니다. “//LIMITS NETWORKVIEWS&PLAYERS” 를 검색하여 int viewID 의 모든 어커런스를 찾아 봅니다. 추가적으로 현재 API는 uint/ushort 를 사용하지 않고 양의 정수만을 사용하고 있습니다. 단순함을 위해서 이렇게 사용하고 viewID 의 사용은 대부분의 경우에 있어서 성능에 큰 이슈가 되지 않습니다.

그룹과 범위

PhotonNetwork 플러그인은 실 네트워크 그룹을 지원하지 않습니다. 위의 "PUN에서 그룹 사용하기"를 참고 하세요.

Unity의 “범위” 기능은 구현되지 않았습니다.

피드백

귀하의 피드백은 매우 소중하게 여기고 있으며 이 솔루션은 계속 발전되고 있습니다. 기능을 잘 모르겠거나, 누락 되었던지 또는 잘 동작 하지 않는 경우 저희에게 포럼(forum.exitgames.com)을 통해 알려 주시면 됩니다.

자주 묻는 질문

게임오브젝트 하나에 여러개의 PhotonView 를 사용할 수 있나요? 이유는요?

네. 그렇게 사용할 수 있습니다. 2개 이상의 타겟을 관찰할 필요가 있으면 여러개의 PhotonView 가 필요할 것 입니다. PhtonView 당 하나만 관찰할 수 있습니다. RPC 에서는 하나의 PhotonView 만 필요할 것이고 이미 무엇인가를 관찰하고 있는 동일한 PhotonView 가 될 수 있습니다. RPC는 절대로 관찰되고 있는 타겟과 같이 없어지지 않습니다.

UnityScript / Javascript 를 사용할 수 있나요?

UnityScript에서 PUN 을 사용하려면 "PhotonNetwork" 와 "UtilityScripts" 폴더를 Assets\ 로 이동시킵니다. 이제 PUN 은 UnityScript 전에 컴파일 하고 이에 따라 일반적인 UnityScript 코드를 사용 할 수 있습니다.