1. 程式人生 > >【Unity3D】 Photon多人遊戲開發教程

【Unity3D】 Photon多人遊戲開發教程

一、前言

Photon Unity Networking(首字母縮寫PUN)是一個Unity多人遊戲外掛包。它提供了身份驗證選項、匹配,以及快速、可靠的通過我們的Photon後端實現的遊戲內通訊。

二、原文

三、正文

Photon多人遊戲開發教程
這裡寫圖片描述

PUN介紹

入門

Photon Unity Networking(首字母縮寫PUN)是一個Unity多人遊戲外掛包。它提供了身份驗證選項、匹配,以及快速、可靠的通過我們的Photon後端實現的遊戲內通訊。

PUN輸出幾乎所有Unity支援的平臺,且有兩種選項:
這裡寫圖片描述
注意:對於Unity 5,兩個PUN外掛包都含相同的檔案。你可以買PUN+ 來獲得60個月的100 CCU,但客戶端上仍使用PUN Free。
PUN、PUN+和UNet的對比


這裡寫圖片描述
一些必須的程式碼
要充分使用PUN,你將需要寫一些指令碼。本頁向你展示入門的最重要部分。

你也應該花一些時間來通過Marco Polo Tutorial。

連線

C#程式碼示例:

PhotonNetwork.ConnectUsingSettings("v4.2");

上面的程式碼是你需要連線並開始使用Photon功能的所有程式碼。

ConnectUsingSettings 設定你的客戶端的遊戲版本並使用一個由PUN設定嚮導寫入的配置檔案,該配置檔案儲存在PhotonServerSettings裡面。

匹配

接下來,你想加入現有的房間或建立自己的。下面的程式碼顯示了啟動或加入遊戲的可能方法呼叫。

//加入名為"someRoom"的房間
PhotonNetwork.JoinRoom("someRoom");
//如果沒有開放的遊戲就會失敗。錯誤回撥: OnPhotonJoinRoomFailed

//嘗試加入任何隨機遊戲:
PhotonNetwork.JoinRandomRoom();
//如果沒有開放的遊戲就會失敗。錯誤回撥: OnPhotonRandomJoinFailed

//建立名為"MyMatch"的房間。
PhotonNetwork.CreateRoom("MyMatch");
//如果名為"MyMatch"的房間已存在就會失敗並呼叫:OnPhotonCreateRoomFailed

好朋友常常想要一起玩遊戲。如果他們可以交流(例如 使用Photon Chat, Facebook), 他們可以瞎編一個房間名並使用JoinOrCreateRoom方法。因為他們知道房間的名字,他們可以建立為他人不可見,像這樣:

C#程式碼示例:

 RoomOptions roomOptions = new RoomOptions() { isVisible = false, maxPlayers = 4 };
 PhotonNetwork.JoinOrCreateRoom(nameEveryFriendKnows, roomOptions, TypedLobby.Default);

使用 JoinOrCreateRoom 方法,如果房間不存在就會建立該房間。如果房間滿了, OnPhotonJoinRoomFailed 會被呼叫 (如果你在某個地方實現了這個回撥函式)。

遊戲

GameObjects可以被例項化為”networked GameObjects”。它們會有一個可以被識別的PhotonView元件和一個所有者(或控制者)。所有者會更新其他人。持續更新可以通過拖拽一個指令碼到一個PhotonView的 Observed欄位被髮送。需要更新的指令碼必須實現OnPhotonSerializeView像這樣:

// 在一個"observed"[ "observed",被觀察的。] 腳本里:
public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
{
      if (stream.isWriting)
      {
          Vector3 pos = transform.localPosition;          stream.Serialize(ref pos);
      }
      else
      {
          Vector3 pos = Vector3.zero;
          stream.Serialize(ref pos);  // pos被填充。必須在某個地方使用
      }
}
客戶端可以為不見用的操作執行Remote Procedure Calls:
// 定義一個可以被其他客戶端呼叫的方法:
[PunRPC]
public void OnAwakeRPC(byte myParameter)
{
       //Debug.Log("RPC: 'OnAwakeRPC' Parameter: " + myParameter + " PhotonView: " + this.photonView);
}
// [...]
// 在別的某個地方呼叫該RPC
photonView.RPC("OnAwakeRPC", PhotonTargets.All, (byte)1);

獨立於GameObjects, 你也可以傳送你自己的事件:

PhotonNetwork.RaiseEvent((byte)eventCode, (object)eventContent,(bool)sendReliable, (RaiseEventOptions)options)

初始設定

Photon Unity Networking (PUN)真的很容易設定。把PUN匯入到一個新的專案中,然後PUN設定嚮導就會彈出來,如圖0-1所示。通過輸入一個郵箱地址來註冊一個新的(免費) Photon Cloud帳號,或者複製貼上一個已有的AppId到該欄位裡。打完收工。

如果你想要自己託管一個Photon伺服器,點選”skip”,然後像如下描述的那樣編輯 PhotonServerSettings
這裡寫圖片描述
要連線,你只需在你的程式碼中呼叫 PhotonNetwork.ConnectUsingSettings()。如果你需要更多的控制,詳見下面的 Connect Manually

Photon伺服器設定

設定嚮導會新增一個 PhotonServerSettings檔案到你的專案,用來儲存配置。如圖0-2所示,這也是去編輯伺服器設定的地方。
這裡寫圖片描述
你可以設定AppId、Photon Cloud Region和更多的。你的客戶端的Game Version是在程式碼裡被設定的。

要選擇的最重要的選項是託管型別。

託管型別

通過Hosting Type你選擇處理你遊戲的伺服器和其他配置。

Photon CloudBest Region都涉及到我們管理的雲服務。您可以選擇特定區域,也可以讓客戶選擇最佳ping區域。

如果你想在別的地方執行Photon伺服器,選擇Self Hosted。安裝程式如下。

或者,你的客戶可以在離線模式。

最佳託管區域

最佳區域模式將在應用首次啟動的時候ping所有已知區域。由於這需要一點時間,結果被儲存在PlayerPrefs。這會加快連線時間。

你可以設定哪些區域可以忽略。在更少的區域分發客戶端會導致剩餘區域的玩家更多。這在遊戲流行之前是有益的。

使用PhotonNetwork.OverrideBestCloudServer()來定義要使用的另一個區域。

自託管

如果你為iOS開發遊戲可以考慮閱讀 PUN and IPv6和how to setup Photon Server for IPv6。

如果你要自己託管一個Photon伺服器,你應在PhotonServerSettings裡面設定好它的地址和埠。當這些都被正確設定了,你可以在你的程式碼裡呼叫PhotonNetwork.ConnectUsingSettings()

確保您的客戶端可以到達輸入的地址。它可以是一個公共的、靜態的IP地址、主機名或在你的客戶端也使用的網路中的任何地址。

埠取決於所選協議,所以請確保這兩個欄位匹配。清除該欄位會將其重置為預設埠。

協議

這裡預設是(可靠的)UDP,但Photon還支援使用TCP以及將允許一個可靠的HTTP協議。

我們建議你堅持UDP。PUN+不支援TCP。WebGL匯出只能使用WebSockets。

客戶端設定

客戶端設定部分包含了每個專案應設定的幾個選項。

當你勾選Auto-Join Lobby時,PUN將在連線(或離開房間)時自動加入預設大廳。Photon的大廳提供當前房間的列表,這樣玩家可以選擇一個加入。這個預設是關閉的,因為更好的選擇是使用隨機匹配,就像所有的演示案例中使用的那樣。

啟用Enable Lobby Stats來從伺服器獲取大廳統計資訊。如果遊戲使用多個大廳,並且你想要向玩家展示每一個活動,則這個統計資訊會很有用。每個大廳,你都可以獲取這些屬性: name、type、room和playercount。詳見PhotonNetworking.LobbyStatistics!

這些設定在PUN v1.60版本引入。

遠端過程呼叫列表

Remote Procedure Calls使你可以在一個房間裡呼叫所有客戶端上的方法。PUN 將這些方法的列表儲存在PhotonServerSettings。對於最初的設定,這是不相關的。詳見Remote Procedure Calls。

手動連線

作為替代自動連線的PhotonNetwork.ConnectUsingSettings()方法你可以通過PhotonNetwork.ConnectToMaster()方法來手動連線你自己的Photon伺服器。當你託管付費Photon伺服器時這是有用的。

對於ConnectToMaster(),你需要提供一個masterServerAddress和一個port引數。地址可以是你的On-Premises DNS名稱或一個IP。它可以包括冒號後的埠(然後傳遞0作為埠)或您可以單獨通過埠。

ConnectToMaster()方法有更多的另外兩個引數 : “appID”和”gameVersion”。兩者都只與Photon Cloud有關,並且當你自己託管Photon伺服器時,可以設定為任何值。

對於Photon Cloud, 使用ConnectUsingSettings()方法。它涉及到我們的Name Server自動找到一個區域的主伺服器。

功能概述

內容提要

  • PUN外掛
  • 連線和主伺服器
  • 版本控制
  • 建立和加入遊戲
  • MonoBehaviour回撥函式
  • 在遊戲房間裡傳送訊息
  • Photon檢視元件
  • 觀察Transform
  • 觀察MonoBehaviour
  • 遠端過程呼叫
  • RPCs和載入關卡的時機

PUN

當你匯入PUN時,設定嚮導視窗會彈出來。如何設定請看匯入PUN與設定小節。

PUN由相當多的檔案組成, 然而只有一個是真正重要的: PhotonNetwork。這個類包含所有需要的函式和變數.。如果您有自定義要求,可以隨時修改原始檔。

要從UnityScript中使用PUN,你需要把 “PhotonNetwork”和”UtilityScripts” 資料夾移動到AssetsPlugins資料夾。

為了告訴你這個API如何工作,這裡有幾個例子。

連線

PhotonNetwork始終使用主伺服器和一個或多個遊戲伺服器。主伺服器管理當前可用的遊戲並進行匹配。一旦房間被發現或建立,實際的遊戲是在遊戲伺服器上完成的。

所有的伺服器都執行在專用的機器上,沒有所謂的玩家託管的伺服器。你不必費心記住該伺服器組織,PUN會為你處理它。

C#程式碼示例

PhotonNetwork.ConnectUsingSettings("v1.0");

上面的程式碼是你需要連線並開始使用Photon功能的所有程式碼。ConnectUsingSettings 設定你的客戶端的遊戲版本並使用一個由PUN設定嚮導寫入的配置檔案,該配置檔案儲存在PhotonServerSettings裡面。你也可以修改檔案PhotonServerSettings 屬性來連線到你自己的伺服器。或者,使用Connect()方法來忽略該PhotonServerSettings 檔案。

版本控制

Photon的負載均衡邏輯使用你的AppId來區分你的和他人的遊戲。玩家也會被遊戲版本分開,ConnectUsingSettings的引數(見上文)。通過這種方式,您可以釋出新功能的客戶端,而不破壞舊版本的遊戲。

由於我們不能保證不同PUN的版本之間相互相容,PUN把它自己的版本號新增到你的遊戲裡。更新PUN可能會從舊的版本中分離出新的客戶端,但不會打破老客戶端。

建立和加入遊戲

接下來,你想加入或建立一個房間。下面的程式碼展示了一些必要的函式:

//加入一個房間
PhotonNetwork.JoinRoom(roomName);  

//建立這個房間。
PhotonNetwork.CreateRoom(roomName);  
// 如果該房間已存在則會失敗並呼叫: OnPhotonCreateGameFailed 

//嘗試加入任何隨機遊戲:
PhotonNetwork.JoinRandomRoom();  
//如果沒有匹配的遊戲則會失敗並呼叫: OnPhotonRandomJoinFailed

在最好的情況下,您的遊戲使用隨機配對。JoinRandomRoom()將嘗試加入任何房間。如果該方法失敗了(沒有房間接受另一個玩家),只需建立一個新的房間,並等到其他玩家隨機加入它為止。

或者,您的客戶端可以獲得當前可用的房間列表。這是通過加入一個大廳來獲得的。

大廳自動傳送他們的房間列表到客戶端,並在時間間隔內更新(從而減少流量)。玩家不會看到對方,且無法溝通(以防止當您的遊戲繁忙時出問題)。

PhotonNetwork外掛可以在其連線時自動加入預設大廳。把PhotonServerSettings檔案裡的”Auto-Join Lobby”屬性開啟即可。

當你的客戶端在一個大廳裡時,房間列表會得到更新, 這些更新會快取。如果需要的話,你可以通過GetRoomList方法來每一幀訪問房間列表。

C#程式碼示例:

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

關於匹配的更多資訊請參考Matchmaking And Room Properties。

回撥函式

PhotonNetwork使用多個回撥函式來讓你的遊戲知道狀態的變化,如“已連線”或“已加入一個遊戲”。像往常對Unity一樣,回撥可在任何腳本里實現。

如果你的指令碼擴充套件Photon.PunBehaviour, 你可以單獨重寫每個回撥。在這種情況下,您不必呼叫基類實現。

C#程式碼示例:

public override void OnJoinedRoom()
{
      Debug.Log("OnJoinedRoom() called by PUN: " + PhotonNetwork.room.name);
}

你不需要擴充套件PunBehaviour。如果你在其本身身上實現它所有的回撥函式也會起作用。它們也在列舉PhotonNetworkingMessage中被列出和描述。

這包括建立遊戲房間的基礎知識。接下來是遊戲中的實際交流。

發訊息

在一個房間裡,你可以傳送網路資訊給其他連線的玩家。此外,您還可以傳送緩衝訊息,也將被髮送到未來連線的玩家(以玩家生成為例)。

傳送訊息可以使用兩種方法。無論是RPCs,還是通過在一個由PhotonView觀察的腳本里實現OnSerializePhotonView。

然而有更多的網路互動。你可以監聽一些網路事件的回撥函式,如OnPhotonInstantiateOnPhotonPlayerConnected,並且你可以觸發其中一些事件,如 PhotonNetwork.Instantiate。如果你被最後一段弄糊塗了,不要擔心,下一步我們會為這些主題逐個做解釋。

Photon視覺同步元件

PhotonView是一個用於傳送訊息(RPCs和OnSerializePhotonView)的指令碼元件。你需要將PhotonView依附到遊戲物件或預設上。請注意,PhotonView和Unity的NetworkView非常相似。

整個過程,你的遊戲中需要至少一個PhotonView,才能傳送訊息和可選的例項化/分配其他的PhotonViews。

如圖下圖所示,新增一個PhotonView到一個遊戲物件,只需選擇一個遊戲物件並使用: “Components/Miscellaneous/Photon View”。
這裡寫圖片描述

圖 0-1 Photon Cloud:Photon View

觀察Transform

如果你將一個Transform繫結到PhotonView的觀察屬性上,你可以選擇同步位置、旋轉和尺度或玩家的這些屬性組合。這可以極大的幫助製作原型或小遊戲。注意:任何觀察到的值變化將傳送所有觀察到的值-而不只是發生變化的那個單一值。此外,更新的值是不平滑的或插值。

觀察MonoBehaviour

PhotonView可以被設定來觀察MonoBehaviour。在這種情況下,指令碼的OnPhotonSerializeView方法會被呼叫。此方法被呼叫來寫入物件的狀態並讀取它,這取決於指令碼是否由本地玩家控制。

下面簡單的程式碼展示瞭如何用幾行程式碼來增加角色狀態同步:

C#程式碼示例:

void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
{
      if (stream.isWriting)
      {          //我們擁有這個玩家:把我們的資料傳送給別的玩家
          stream.SendNext((int)controllerScript._characterState);
          stream.SendNext(transform.position);
          stream.SendNext(transform.rotation);
      }      else
      {
          //網路玩家,接收資料
         controllerScript._characterState = (CharacterState)(int)stream.ReceiveNext();
         correctPlayerPos = (Vector3)stream.ReceiveNext();
         correctPlayerRot = (Quaternion)stream.ReceiveNext();
      }
}

觀察選項

Observe Option欄位讓你選擇更新如何傳送以及何時被髮送。該欄位還會影響到OnPhotonSerializeView被呼叫的頻率。

Off 顧名思義,關掉。如果該PhotonView被保留為RPCs限定時可以很有用。

Unreliable 更新如是被髮送,但可能會丟失。這個想法是,下一次更新很快到來,並提供所需的正確的/絕對的值。這對於位置和其他絕對資料來說是有利的,但對於像切換武器這樣觸發器來說是不好的。當用於同步的遊戲物件的位置,它會總是傳送更新,即使該遊戲物件停止運動(這是不好的)。

Unreliable on Change 將檢查每一個更新的更改。如果所有值與之前傳送的一樣,該更新將作為可靠的被髮送,然後所有者停止傳送更新直到事情再次發生變化。這對於那些可能會停止運動的以及暫時不會建立進一步更新的遊戲物件來說是有利的。例如那些在找到自己的位置後就不再移動的箱子。

Reliable Delta Compressed 將更新的每個值與它之前的值進行比較。未更改的值將跳過以保持低流量。接收端只需填入先前更新的值。任何你通過OnPhotonSerializeView寫入的都會自動進行檢查並以這種方式被壓縮。如果沒有改變, OnPhotonSerializeView不會在接收客戶端呼叫。該“可靠的”部分需要一些開銷,所以對於小的更新,應該考慮這些開銷。

現在開始,以另一種方式交流:RPCs。

遠端過程呼叫

Remote Procedure Calls (RPC)使你可以呼叫”networked GameObjects”上的方法,對由使用者輸入等觸發的不常用動作很有用。

一個RPC會被在同房間裡的每個玩家在相同的遊戲物件上被執行,所以你可以容易地觸發整個場景效果就像你可以修改某些GameObject

作為RPC被呼叫的方法必須在一個帶PhotonView元件的遊戲物件上。該方法自身必須要被[PunRPC]屬性標記。

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

要呼叫該方法,先訪問到目標物件的PhotonView元件。而不是直接呼叫目標方法,呼叫PhotonView.RPC()並提供想要呼叫的方法名稱:

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

你可以傳送一系列的引數,但它必須匹配該RPC方法的定義。

這些是最基本的。詳情請閱讀Remote Procedure Calls.

時機

RPCs在指定的PhotonViews上被呼叫,並總是以接收客戶端上的匹配者為目標。如果一個遠端客戶端還沒有載入或建立匹配的PhotonView,這個RPC就會丟失!

因此,丟失RPCs一個典型的原因就是當客戶端載入新場景的時候。它只需要一個已經載入有新遊戲物件的場景的客戶端,並且其他客戶端不能理解這個RPC(直到這些客戶端也載入了相同的場景)。

PUN可以幫你解決此問題。只需在你連線之前設定PhotonNetwork.automaticallySyncScene = true並在房間的主客戶端上使用 PhotonNetwork.LoadLevel()。這樣,一個客戶端定義了所有客戶端必須在房間/遊戲中載入的關卡。

客戶端可以停止執行接收到的訊息來防止RPCs丟失(這正是LoadLevel方法幫你做的)。當你得到一個RPC來載入一些場景,立即設定isMessageQueueRunning = false直到該內容被初始化。

例子:

private IEnumerator MoveToGameScene()
{
      // 載入關卡前臨時禁用進一步的網路資訊處理
      PhotonNetwork.isMessageQueueRunning = false;
      Application.LoadLevel(levelName);
}

禁用訊息佇列將延遲傳入和傳出訊息,直到佇列被解鎖。顯然,當你準備好要繼續的時候,開啟佇列是非常重要的。