Overview
概述
Quantum廣泛接受各種玩家實體的概念。所有實體在模擬的角度看來都是相同的。因此,當我們在此文檔中提到「玩家」,我們指玩家控制的實體。
玩家識別
玩家可透過兩種方法被識別:
- 他們的玩家索引;及,
- 他們的玩家參照。
玩家索引指派
Quantum player index
由伺服器基於Session.Join()
訊息到達的順序來指派。這不可與Photon帳號混淆,其基於玩家加入Photon房間的順序。無法在Photon玩家上設定「希望的Quantum帳號」。
請注意: 在中斷連線的情形,我們保證客戶端獲得同樣的玩家索引,前提是 他以相同的 客戶端帳號 重新連線;這無論他們的Photon帳號是什麼——public static QuantumRunner StartGame(String clientId, Int32 playerCount, StartParameters param)
。
玩家索引 vs 玩家參照
PlayerRef
是在Quantum ECS中針對player index
的包裝函式。PlayerRef
是以1起始,而player index
以0起始。原因是為了方便,default(PlayerRef)
將傳回一個「空/無效」玩家參照架構。
自動投放運算子將投放一個Integer
到一個玩家參照,且可以從玩家參照投放回來。
- 預設(玩家參照),內部為一個0,意思是沒有人
- 玩家參照,內部為一個1,如同玩家索引0
- 玩家參照,內部為一個2,如同玩家索引1
Photon帳號
您可以透過Frame
API識別一個玩家的相應的Photon帳號:
Frame.PlayerToActorId(PlayerRef player)
轉換一個Quantum玩家參照到一個執行者帳號(Photon客戶端帳號);或,Frame.ActorIdToAllPlayers(Int32 actorId)
是前面方法的相反流程。
舉例而言,如果您計畫透過PhotonPlayer.Nickname
顯示玩家名稱,請使用這個方法。
重要: Photon帳號與Quantum模擬沒有關聯。
加入遊戲
當遊戲啟動,下列事件按序列發生:
QuantumRunner.Session.Join()
發送加入請求與期望的玩家計數到伺服器。- 請求由伺服器接收,並在伺服器驗證,並且將確認發送回使用者。如果附在請求的資訊無效,請求將被拒絕。
- 啟動遊戲訊息由客戶端接收。
- 玩家現在可以發送及接收輸入。
- (選擇性) - 在延遲加入的情形,客戶端可接收一個快照——重新同步;在這個情況下,在等待快照的同時,第4步將不會發送輸入。
- (選擇性) - 現在可以使用
SendPlayerData
。在遊戲階段中(在遊戲啟動時和/或在遊戲階段之中時),可以按照需要的次數使用它。每次調用SendPlayerData
,它發送一個RuntimePlayer
的已序列化的版本到伺服器,之後附加到一個刷新輸入設定確認,並且因此確定性地觸發信號。

如需更多相關的設定檔案的資訊,請參照操作手冊的 設定檔案 文檔。
發送玩家資料
QuantumGame.SendPlayerData(RuntimePlayer features)
是一個資料路徑,以確定性地插入一個特殊的資料種類(已序列化的運行階段玩家)到輸入串流。雖然一般在遊戲啟動時調用SendPlayerData
,以設定所有玩家;如果資料需要更新時,也可以在遊戲階段時調用它。
在啟動之後,加入Quantum遊戲,CallbackGameStarted
回調啟動。在這個時刻,各個玩家可以調用SendPlayerData
方法,在其他人的模擬中新增為玩家。調用這個將大大地簡化延遲加入玩家的流程。
C#
public class MyCallbacks : MonoBehaviour {
private void OnEnable() {
QuantumCallback.Subscribe<CallbackGameStarted>(this, OnGameStart);
}
private void OnGameStart(CallbackGameStarted callback) {
// paused on Start means waiting for Snapshot
if (callback.Game.Session.IsPaused) return;
// It needs to be sent for each local player.
foreach (var lp in callback.Game.GetLocalPlayers()) {
Debug.Log("CustomCallbacks - sending player: " + lp);
callback.Game.SendPlayerData(lp, new Quantum.RuntimePlayer { });
}
}
}
玩家實體在本機模式中被具現化,但在多重玩家模式中沒有
最有可能的情況是,沒有針對各個玩家執行QuantumGame.SendPlayerData()
。如果您正在使用示範選單以啟動遊戲,新增指令碼CustomCallbacks.cs
到選單場景的任何位置。
玩家已連線系統
為了追蹤玩家連線到Quantum遊戲階段的情況,,使用輸入及連線旗標。PlayerConnectedSystem
自動進行程序,並且如果玩家已經連線到遊戲階段或從遊戲階段中斷連線時,將通知模擬。為了使用系統,它必須被新增到SystemSetup
。
C#
public static class SystemSetup {
public static SystemBase[] CreateSystems(RuntimeConfig gameConfig, SimulationConfig simulationConfig) {
return new SystemBase[] {
// pre-defined core systems
...
new PlayerConnectedSystem(),
// custom systems
...
}
}
}
為了接收連線及中斷連線回調,必須在系統中執行ISignalOnPlayerConnected
及ISignalOnPlayerDisconnected
。
ISignalOnPlayerDataSet
在系統中執行ISignalOnPlayerDataSet
,將讓您可以存取public void OnPlayerDataSet(Frame f, PlayerRef playerRef)
。每次調用OnPlayerDataSet
,一個已序列化的RuntimePlayer
是一個特定的刷新輸入的一部分。
運行階段玩家
類別RuntimePlayer
用於保存玩家特定的資訊,比如選定的角色。為了讓RuntimePlayer
滿足您的自訂需求,您需要執行序列化方法——在資產連結的情形,GUID需要被序列化。
RuntimePlayer
是一個在quantum.code
專案中的部分類別。為了協助升級SDK和未來的校訂,您的自訂執行方式要在RuntimePlayer.User.cs
中完成。在這裡您可新增您希望針對各個玩家而特定的參數,並且可在SerializeUserData
方法中執行他們的序列化。結果將類似於此:
C#
namespace Quantum {
partial class RuntimePlayer {
public AssetRefCharacterSpec CharacterSpec;
partial void SerializeUserData(BitStream stream)
{
stream.Serialize(ref CharacterSpec.Id.Value);
}
}
}
在運行階段存取
以玩家的PlayerRef
查詢Frame.GetPlayerData()
,可擷取與玩家關聯的RuntimePlayer
資產。
C#
public void OnPlayerDataSet(Frame f, PlayerRef player){
var data = f.GetPlayerData(player);
}
初始化一個玩家實體
在模擬時的任何時刻,可初始化由玩家控制的實體。一個常見的方法是在玩家連線(ISignalOnPlayerConnected
)和/或收到玩家資料(ISignalOnPlayerDataSet
)時初始化它。
ISignalOnPlayerConnected
:玩家實體可以使用模擬或資產資料庫中已有的任何資訊進行初始化。ISignalOnPlayerDataSet
:可以使用與玩家的RuntimePlayer
特定資訊相關的資訊來初始化玩家實體。這對於選定角色模型/皮膚或庫存卸载等事情來說很方便。
模擬 vs 檢視
首先需要澄清幾件事:
- 從模擬的視角(Quantum),玩家控制的實體是具有玩家輸入的實體。它不知道是本機或遠端玩家。
- 從檢視的視角(Unity),我們在本機客戶端上輪詢來自玩家的輸入。
回顧一下,在模擬中沒有所謂「本機」或「遠端」玩家;然而,在檢視中,玩家是「本機」或非「本機」。
C#
Photon.Deterministic.DeterministicGameMode.Local
Photon.Deterministic.DeterministicGameMode.Multiplayer
Photon.Deterministic.DeterministicGameMode.Replay
玩家數量最大值
最大玩家計數需要預先知道,因為它定義了在記憶體區塊中針對各個幀需要分配多少空間。預設最大玩家數量是6。
為了更改它,新增下列行到您任何的qtn
——檔案中:
#define PLAYER_COUNT 8
#pragma max_players PLAYER_COUNT
define
作為一個定義,並且可在DSL中被使用(例如,以玩家計數來分配陣列)。pragma
實際上定義模擬能夠處理的玩家數量。
本機玩家
Quantum在檢視中提供API,以檢查玩家是否是本機玩家:
QuantumRunner.Default.Game.Session.IsLocalPlayer(int player)
;及,QuantumRunner.Default.Game.PlayerIsLocal(PlayerRef playerRef)
。
多重本機玩家
QuantumRunner.Default.Game.GetLocalPlayers()
傳回一個陣列,這個陣列對於每個客戶端來說是獨一無二的,並且代表了針對玩家的索引,這個索引是您的本機機器在Quantum模擬中所控制。
- 如果只有一名本機玩家,它傳回一個索引。如果多個玩家在同一個本機機器控制上,陣列將有本機玩家計數的長度。
- 這些就是傳送到
QuantumInput.Instance.PollInput(int player)
之中的同樣的索引。 - 索引由伺服器定義(除非它是本機遊戲)。
- 索引永遠在[0, PlayerCount-1]的範圍之中。
PlayerCount
代表在對戰中的所有玩家計數。它被傳送到QuantumRunner.StartGame
之中。 - 索引值是任意性的(針對此工作階段,在0到最大玩家的範圍內),並且取決於多重玩家連線和中斷連線的順序,及他們的訊息到達伺服器的時間。
- 如果一個本機機器有超過一名玩家,值不一定是連續性的。
- 當重新加入遊戲,只要您以相同的GUID調用
Session.Join()
,而且房間從您中斷連線後還沒有被新的玩家填入,就可以為您分配相同的玩家索引。
從上述功能使用本機玩家索引以發送運行階段玩家資料:QuantumGame.SendPlayerData(int player, RuntimePlayer data)
。在一個機器上針對所有玩家這樣做。