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#代碼示例:
?
上面的代碼是你需要連接并開始使用Photon功能的所有代碼。
ConnectUsingSettings?設置你的客戶端的游戲版本并使用一個由PUN設置向導寫入的配置文件,該配置文件保存在PhotonServerSettings里面。
匹配
接下來,你想加入現有的房間或創建自己的。下面的代碼顯示了啟動或加入游戲的可能方法調用。
?
PhotonNetwork.JoinRoom("someRoom");
//如果沒有開放的游戲就會失敗。錯誤回調: OnPhotonJoinRoomFailed
??
//嘗試加入任何隨機游戲:
PhotonNetwork.JoinRandomRoom();
//如果沒有開放的游戲就會失敗。錯誤回調: OnPhotonRandomJoinFailed
??
//創建名為"MyMatch"的房間。
PhotonNetwork.CreateRoom("MyMatch");
//如果名為"MyMatch"的房間已存在就會失敗并調用:OnPhotonCreateRoomFailed
好朋友常常想要一起玩游戲。如果他們可以交流(例如 使用Photon Chat, Facebook), 他們可以瞎編一個房間名并使用JoinOrCreateRoom方法。因為他們知道房間的名字,他們可以創建為他人不可見,像這樣:
C#代碼示例:
?
? ?? ???PhotonNetwork.JoinOrCreateRoom(nameEveryFriendKnows, roomOptions, ? TypedLobby.Default);
使用?JoinOrCreateRoom方法,如果房間不存在就會創建該房間。如果房間滿了,?OnPhotonJoinRoomFailed會被調用 (如果你在某個地方實現了這個回調函數)。
游戲
GameObjects可以被實例化為"networked GameObjects"。它們會有一個可以被識別的PhotonView組件和一個所有者(或控制者)。所有者會更新其他人。持續更新可以通過拖拽一個腳本到一個PhotonView的 Observed字段被發送。需要更新的腳本必須實現OnPhotonSerializeView像這樣:
?
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, 你也可以發送你自己的事件:
?
初始設置
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 Cloud和Best 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#代碼示例
上面的代碼是你需要連接并開始使用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#代碼示例:
?
{
? ?? ?GUILayout.Label(room.name + " " + room.playerCount + "/" + room.maxPlayers);
}
關于匹配的更多信息請參考Matchmaking And Room Properties。
回調函數
PhotonNetwork使用多個回調函數來讓你的游戲知道狀態的變化,如“已連接”或“已加入一個游戲”。像往常對Unity一樣,回調可在任何腳本里實現。
如果你的腳本擴展Photon.PunBehaviour,?你可以單獨重寫每個回調。在這種情況下,您不必調用基類實現。
C#代碼示例:
?
{
? ?? ?Debug.Log("OnJoinedRoom() called by PUN: " + PhotonNetwork.room.name);
}
你不需要擴展PunBehaviour。如果你在其本身身上實現它所有的回調函數也會起作用。它們也在枚舉PhotonNetworkingMessage中被列出和描述。
這包括建立游戲房間的基礎知識。接下來是游戲中的實際交流。
發消息
在一個房間里,你可以發送網絡信息給其他連接的玩家。此外,您還可以發送緩沖消息,也將被發送到未來連接的玩家(以玩家生成為例)。
發送消息可以使用兩種方法。無論是RPCs,還是通過在一個由PhotonView觀察的腳本里實現OnSerializePhotonView。
然而有更多的網絡互動。你可以監聽一些網絡事件的回調函數,如OnPhotonInstantiate或OnPhotonPlayerConnected,并且你可以觸發其中一些事件,如?PhotonNetwork.Instantiate。如果你被最后一段弄糊涂了,不要擔心,下一步我們會為這些主題逐個做解釋。
Photon視覺同步組件
PhotonView是一個用于發送消息(RPCs和OnSerializePhotonView)的腳本組件。你需要將PhotonView依附到游戲對象或預設上。請注意,PhotonView和Unity的NetworkView非常相似。
整個過程,你的游戲中需要至少一個PhotonView,才能發送消息和可選的實例化/分配其他的PhotonViews。
如圖下圖所示,添加一個PhotonView到一個游戲對象,只需選擇一個游戲對象并使用: "Components/Miscellaneous/Photon View"。
?
觀察Transform
如果你將一個Transform綁定到PhotonView的觀察屬性上,你可以選擇同步位置、旋轉和尺度或玩家的這些屬性組合。這可以極大的幫助制作原型或小游戲。注意:任何觀察到的值變化將發送所有觀察到的值-而不只是發生變化的那個單一值。此外,更新的值是不平滑的或插值。
觀察MonoBehaviour
PhotonView可以被設置來觀察MonoBehaviour。在這種情況下,腳本的OnPhotonSerializeView方法會被調用。此方法被調用來寫入對象的狀態并讀取它,這取決于腳本是否由本地玩家控制。
下面簡單的代碼展示了如何用幾行代碼來增加角色狀態同步:
C#代碼示例:
?
{
? ?? ?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]屬性標記。
?
void ChatMessage(string a, string b)
{
? ?? ?Debug.Log("ChatMessage " + a + " " + b);
}
要調用該方法,先訪問到目標對象的PhotonView組件。而不是直接調用目標方法,調用PhotonView.RPC()并提供想要調用的方法名稱:
?
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直到該內容被初始化。
例子:
?
{
? ?? ?// 加載關卡前臨時禁用進一步的網絡信息處理
? ?? ?PhotonNetwork.isMessageQueueRunning = false;
? ?? ?Application.LoadLevel(levelName);
}
禁用消息隊列將延遲傳入和傳出消息,直到隊列被解鎖。顯然,當你準備好要繼續的時候,打開隊列是非常重要的。
總結
以上是生活随笔為你收集整理的Photon多人游戏开发教程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 虚幻引擎学习之路:粒子系统篇(二)
- 下一篇: 在Unity中做一个淡入式的屏幕虚化