連接服務器,房間訪問和創建
我們首先來看這篇教程的核心,連接Photon Cloud服務器和加入房間,或者需要的話創建一個。
- 創建一個新的場景,保存為Launcher.unity
- 創建新的C#腳本Launcher
- 創建新的GameObject,命名為Launcher
- 把C#腳本添加到Launcher對象上
- 按照下面的內容編輯C#腳本
- 記得保存
using UnityEngine;
namespace Com.MyCompany.MyGame
{
public class Launcher : MonoBehaviour
{
#region Public Variables
#endregion
#region Private Variables
/// <summary>
/// This client's version number. Users are separated from each other by gameversion (which allows you to make breaking changes).
/// </summary>
string _gameVersion = "1";
#endregion
#region MonoBehaviour CallBacks
/// <summary>
/// MonoBehaviour method called on GameObject by Unity during early initialization phase.
/// </summary>
void Awake()
{
// #NotImportant
// Force Full LogLevel
PhotonNetwork.logLevel = PhotonLogLevel.Full;
// #Critical
// we don't join the lobby. There is no need to join a lobby to get the list of rooms.
PhotonNetwork.autoJoinLobby = false;
// #Critical
// this makes sure we can use PhotonNetwork.LoadLevel() on the master client and all clients in the same room sync their level automatically
PhotonNetwork.automaticallySyncScene = true;
}
/// <summary>
/// MonoBehaviour method called on GameObject by Unity during initialization phase.
/// </summary>
void Start()
{
Connect();
}
#endregion
#region Public Methods
/// <summary>
/// Start the connection process.
/// - If already connected, we attempt joining a random room
/// - if not yet connected, Connect this application instance to Photon Cloud Network
/// </summary>
public void Connect()
{
// we check if we are connected or not, we join if we are , else we initiate the connection to the server.
if (PhotonNetwork.connected)
{
// #Critical we need at this point to attempt joining a Random Room. If it fails, we'll get notified in OnPhotonRandomJoinFailed() and we'll create one.
PhotonNetwork.JoinRandomRoom();
}else{
// #Critical, we must first and foremost connect to Photon Online Server.
PhotonNetwork.ConnectUsingSettings(_gameVersion);
}
}
#endregion
}
}
讓我們看看代碼中都做了什么,先從Unity內容開始,然后再看PUN的內容。
-
Namespace
雖然不是強制性的,為腳本提供適當的命名空間可防止與其他Assets和開發人員發生沖突。 如果另一個開發者創建一個類Launcher怎么辦? Unity編譯器會報錯,你們必須重命名這個的類。 如果沖突來自您從資源商店下載的Assets,這可能很棘手。 現在,Launcher類實際上是在Com.MyCompany.MyGame.launcher這個命名空間下,別人不太可能使用與我們這個完全相同的命名空間,因為你擁有這個域名,所以使用域名逆序作為命名空間,可以使您的工作安全,組織良好。 Com.MyCompany.MyGame應該被替換為你自己的逆序域名和游戲名稱,應該遵從這個良好的約定。
-
MonoBehaviour類
注意,我們使用MonoBehaviour派生類,它可以將我們的類轉換為Unity組件,然后可以放到GameObject或Prefab上。 擴展MonoBehaviour的類可以訪問許多非常重要的方法和屬性。 在這里,我們將使用兩個回調方法,Awake()和Start()。
-
PhotonNetwork.logLevel
在開發過程中,特別是如果這是你第一次使用PUN時,我們將要求PUN在Unity控制臺上盡可能多地記錄日志,以便我們準確地知道發生了什么。 您將更加自信,并且當事情按預期工作時,將此LogLevel切換到Informational。
-
PhotonNetwork.autoJoinLobby
在Awake()期間,我們將PhotonNetwork.autoJoinLobby設置為false,因為我們不需要大廳功能,我們只需要獲取現有房間的列表。 強制設置通常是一個好主意,因為在同一個項目中,你可能有另一個場景,實際上想要自動加入大廳,所以在同一個項目內,在不同的方法中間切換的話,這兩個情況下都會沒有問題。
-
PhotonNetwork.ConnectUsingSettings()
在Start()期間,使用我們的公共函數connect()連接到PUN ,調用了PhotonNetwork.ConnectUsingSettings()。 注意_gameVersion變量表示你的游戲版本。 您應該將其保留為“1”,直到您需要對已經處于活動狀態的項目進行大的修改。 這里要記住的重要信息是,PhotonNetwork.ConnectUsingSettings()是您的游戲的聯網和連接到Photon Cloud的起點。
-
PhotonNetwork.automaticallySyncScene
我們的游戲將有一個基于玩家數量可調整的競技場,并確保加載的場景對每個連接的玩家是相同的,我們將利用Photon提供的非常方便的功能:PhotonNetwork.automaticallySyncScene。當該變量設置為true的時候,MasterClient可以調用PhotonNetwork.LoadLevel(),此時所有連接的玩家都會自動的加載同樣的Level。
到這里,您可以保存啟動場景并點擊播放。 你應該在Unity控制臺中看到好多條日志。特別是“已連接到masterserver”這條日志,表明我們現在已經連接并準備好加入一個房間。
編碼時的良好習慣是總是測試失敗的可能性。 這里我們假定計算機已連接到互聯網,但如果計算機未連接到互聯網會發生什么呢?讓我們來看看。 關閉計算機的網絡并啟動場景。 你應該看到在Unity控制臺錯誤“Connect() to 'ns.exitgames.com' failed:System.Net.Sockets.SocketException:No such host is known”。
理想情況下,我們的腳本應該知道這個問題,并對這些情況作出反應。并且無論什么情況或問題可能出現,都要能夠積極響應。
我們現在處理這兩種情況,并通知我們的Launcher腳本,我們到底有沒有連接上PUN服務器。 這將是對PUN Callbacks的完美介紹。
PUN CallBacks
PUN的回調非常靈活,并提供了三種非常不同的實現。 讓我們學習所有的三種方法,然后根據情況選擇使用最適合的一個。
"magic" methods
當使用常規的MonoBehaviour的時候,可以簡單創建一個私有方法:
void OnConnectedToMaster()
{
Debug.Log("DemoAnimator/Launcher: OnConnectedToMaster() was called by PUN");
}
這挺神奇的,因為任何MonoBehaviour可以實現該方法或任何其他消息從PUN。 它遵循與Unity發送給MonoBehaviours(如Awake()或Start())主要方法相同的原則。 但是我們不會使用這個,因為如果你拼錯這些“Magic”的方法,不會通知你出了錯誤,所以這是一個非常快速和實際的實現,但只有當知道確切的每個方法的名稱,并且你非常熟悉,非常在行調試技術,才能快速找到這些類型的問題。
使用IPunCallbacks和IPunObservable接口
PUN提供了兩個C#接口,IPunCallbacks和IPunObservable,你可以在類中實現。
這是一個非常安全的方法,以確保一個類符合所有的接口,但強制開發人員實現所有的接口聲明。 最好的腳本編輯器將使這個任務非常容易,如上面使用MonoDevelop時所示。 然而,腳本可能最終有很多不做任何事情的方法,但是為了編譯器通過必須都實現。 所以你的腳本可以大量使用所有或大多數Pun特性。
后面的教程中,做數據序列化的時候還會使用到IPunObservable。
使用Photon.PunBehaviour
上一個部分,是我們將經常使用的技術,是最方便的一個。 我們還可以從Photon.PunBehaviour繼承類,而不是創建一個從MonoBehaviour派生的類,因為它暴露了特定的屬性和虛擬方法,供我們在方便時使用和覆蓋。 這點非常實用,因為我們可以確定我們沒有任何打字錯誤,我們不需要實現所有的方法。
注意:當重寫方法時,大多數腳本編輯器將默認實現一個基類調用并自動填充,但在我們的例子中不需要,因此作為Photon.PunBehaviour的一般規則,不會調用基類方法。
注意:當重寫方法時,另一個很大的好處是,您通過簡單地懸停在方法名稱上從幫助中獲益。
好的,讓我們實現以下OnConnectedToMaster()和OnDisconnectedFromPhoton()這兩個PUN回調。
編輯Launcher腳本
-
把基類改成Photon.PunBehaviour
public class Launcher : Photon.PunBehaviour {
-
在類的末尾添加下面兩個方法,為了清晰請寫到這個區塊Photon.PunBehaviour CallBacks里面
#region Photon.PunBehaviour CallBacks public override void OnConnectedToMaster() { Debug.Log("DemoAnimator/Launcher: OnConnectedToMaster() was called by PUN"); } public override void OnDisconnectedFromPhoton() { Debug.LogWarning("DemoAnimator/Launcher: OnDisconnectedFromPhoton() was called by PUN"); } #endregion
保存腳本
現在,如果我們在有或沒有互聯網的情況下啟動這個場景,我們可以采取適當的步驟通知玩家是否進行下一步。我們將在下一節中討論構建UI。現在我們將處理成功的連接:
因此,我們在OnConnectedToMaster()方法中追加以下調用:
// #Critical: The first we try to do is to join a potential existing room. If there is, good, else, we'll be called back with OnPhotonRandomJoinFailed()
PhotonNetwork.JoinRandomRoom();
正如注釋所說,我們需要被告知嘗試加入一個隨機房間是否失敗,在這種情況下,我們需要實際創建一個房間,所以我們在我們的腳本中實現OnPhotonRandomJoinFailed這個PUN回調。創建一個房間使用PhotonNetwork.CreateRoom(),你已經猜到,當我們成功地加入一個房間時使用PUN回調OnJoinedRoom()。
public override void OnPhotonRandomJoinFailed (object[] codeAndMsg)
{
Debug.Log("DemoAnimator/Launcher:OnPhotonRandomJoinFailed() was called by PUN. No random room available, so we create one.\nCalling: PhotonNetwork.CreateRoom(null, new RoomOptions() {maxPlayers = 4}, null);");
// #Critical: we failed to join a random room, maybe none exists or they are all full. No worries, we create a new room.
PhotonNetwork.CreateRoom(null, new RoomOptions() { maxPlayers = 4 }, null);
}
public override void OnJoinedRoom()
{
Debug.Log("DemoAnimator/Launcher: OnJoinedRoom() called by PUN. Now this client is in a room.");
}
現在如果你運行場景,并且你應該遵循連接到PUN連續的邏輯,嘗試加入現有房間,否則創建房間并加入新創建的房間。
到這里,盡管我們現在已經介紹了連接和加入一個房間的關鍵方面,但是有一些事情不是很方便,遲早藥解決它們。 這些不是真正與學習PUN有關,但從總體角度來說很重要。
在Unity Inspector中暴露變量
MonoBehaviours會自動將他們的公共屬性暴露給Unity Inspector。這是Unity中一個非常重要的概念,在我們的例子中,我們將修改我們定義LogLevel的方式,并創建一個公共變量,以便我們可以在不觸及代碼本身的情況下進行設置。
/// <summary>
/// The PUN loglevel.
/// </summary>
public PhotonLogLevel Loglevel = PhotonLogLevel.Informational;
以及修改Awake():
// #NotImportant
// Force LogLevel
PhotonNetwork.logLevel = Loglevel;
所以,現在我們不強制腳本是一個特定類型的LogLevel,我們只需要在Unity Inspector中設置它,然后點擊運行,不需要打開腳本,編輯它,保存它,等待Unity重新編譯并最終運行。 這種方式更有效率和靈活性。
我們也會這樣做,每個房間的玩家數量最多。 在代碼中硬編碼不是最好的做法,而是讓它作為一個公共變量,以便我們以后可以確定和調整這個數字,而不需要重新編譯。
在類聲明的開頭,Public Variables代碼段中加入:
/// <summary>
/// The maximum number of players per room. When a room is full, it can't be joined by new players, and so new room will be created.
/// </summary>
[Tooltip("The maximum number of players per room. When a room is full, it can't be joined by new players, and so new room will be created")]
public byte MaxPlayersPerRoom = 4;
然后修改PhotonNetwork.CreateRoom()調用的地方來使用這個公共變量。
// #Critical: we failed to join a random room, maybe none exists or they are all full. No worries, we create a new room.
PhotonNetwork.CreateRoom(null, new RoomOptions() { maxPlayers = MaxPlayersPerRoom }, null);
在Unity Inspector中效果如下: