This document is about: QUANTUM 2
SWITCH TO

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模擬沒有關聯。

加入遊戲

當遊戲啟動,下列事件按序列發生:

  1. QuantumRunner.Session.Join()發送加入請求與期望的玩家計數到伺服器。
  2. 請求由伺服器接收,並在伺服器驗證,並且將確認發送回使用者。如果附在請求的資訊無效,請求將被拒絕。
  3. 啟動遊戲訊息由客戶端接收。
  4. 玩家現在可以發送及接收輸入。
  5. (選擇性) - 在延遲加入的情形,客戶端可接收一個快照——重新同步;在這個情況下,在等待快照的同時,第4步將不會發送輸入。
  6. (選擇性) - 現在可以使用SendPlayerData。在遊戲階段中(在遊戲啟動時和/或在遊戲階段之中時),可以按照需要的次數使用它。每次調用SendPlayerData,它發送一個RuntimePlayer的已序列化的版本到伺服器,之後附加到一個刷新輸入設定確認,並且因此確定性地觸發信號。
Config Sequence Diagram
序列圖表

如需更多相關的設定檔案的資訊,請參照操作手冊的 設定檔案 文檔。

發送玩家資料

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
      ...
    }
  }
}

為了接收連線及中斷連線回調,必須在系統中執行ISignalOnPlayerConnectedISignalOnPlayerDisconnected

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 檢視

首先需要澄清幾件事:

  1. 從模擬的視角(Quantum),玩家控制的實體是具有玩家輸入的實體。它不知道是本機或遠端玩家。
  2. 從檢視的視角(Unity),我們在本機客戶端上輪詢來自玩家的輸入。

回顧一下,在模擬中沒有所謂「本機」或「遠端」玩家;然而,在檢視中,玩家是「本機」或非「本機」。

C#

Photon.Deterministic.DeterministicGameMode.Local
Photon.Deterministic.DeterministicGameMode.Multiplayer
Photon.Deterministic.DeterministicGameMode.Replay

玩家數量最大值

最大玩家計數需要預先知道,因為它定義了在記憶體區塊中針對各個幀需要分配多少空間。預設最大玩家數量是6。 為了更改它,新增下列行到您任何的qtn——檔案中:

Unknown

#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)。在一個機器上針對所有玩家這樣做。

Back to top