This document is about: QUANTUM 3
SWITCH TO

概要

はじめに

Quantum はプレイヤーエンティティの概念に対して無関心です。シミュレーションの目から見ると、すべてのエンティティは同じです。したがって、このドキュメントで「プレイヤー」と言うとき、私たちが指しているのは プレイヤーが制御するエンティティ です。

プレイヤーの識別

プレイヤーは 2 つの方法で識別できます:

  • プレイヤーインデックス
  • PlayerRef

プレイヤーインデックスの割り当て

Quantum の player index は、QuantumGame.AddPlayer() 操作がサーバーに届く順序に基づいてサーバーによって割り当てられます。これは、Photon/Actor Id とは混同しないでください。Photon ルームに参加したプレイヤーの順序に基づいています。

「Desired Quantum Id」を設定することはできません。

注意: 切断が発生した場合、再接続時にクライアントが同じ ClientId を持っている場合、同じプレイヤーインデックスを取得することが保証されています。その際、Photon Id に関係なく - public static QuantumRunner StartGame(String clientId, Int32 playerCount, StartParameters param)

プレイヤーインデックスと PlayerRef

PlayerRef は Quantum ECS 内の player index のラッパーです。PlayerRef は 1 ベースですが、player index は 0 から始まります。これは、default(PlayerRef) が便利さのために "null/invalid" プレイヤー参照構造体を返すためです。

  • default(PlayerRef) は内部的に 0 で、NOBODY を意味します。
  • PlayerRef は内部的に 1 で、プレイヤーインデックス 0 と等しいです。
  • PlayerRef は内部的に 2 で、プレイヤーインデックス 1 と等しいです。

自動キャストオペレーターにより、IntegerPlayerRef に、逆も同様に変換できます。

C#

// DeterministicCommand GetPlayerCommand(PlayerRef player);
for (int p = 0; p < f.PlayerCount; p++) {
  var command = f.GetPlayerCommand(p);
}

プレイヤースロット

Quantum は 1 つのゲームクライアント内で複数のローカルプレイヤーを実行することをサポートします。0 ベースのプレイヤースロットは、すべてのローカルプレイヤーを指します。

いくつかの API では、プレイヤースロットを選択することが明示的に必要であり、ローカルプレイヤーが 1 人のみ使用されている場合は、プレイヤースロットは 0 になります。

「複数のローカルプレイヤー」の章で詳細を読みます。

ローカルプレイヤーの確認

Quantum は、View 内でプレイヤーがローカルかどうかを確認するために、2 つの API を提供しています:

  • QuantumRunner.Default.Game.Session.IsLocalPlayer(int player)
  • QuantumRunner.Default.Game.PlayerIsLocal(PlayerRef playerRef)

ローカルプレイヤーの取得

QuantumRunner.Default.Game.GetLocalPlayers() は、各クライアントでユニークな配列を返し、ローカルクライアントが Quantum シミュレーション内で制御するグローバルプレイヤーインデックスを表します。

QuantumRunner.Default.Game.GetLocalPlayerSlots() は、各クライアントでユニークな配列を返し、ローカルクライアントが Quantum シミュレーション内で制御するプレイヤースロットを表します。

  • このメソッドは、ローカルプレイヤーが 1 人だけの場合、1 つのインデックスを返します。複数のプレイヤーが同じローカルマシン上にいる場合、配列はローカルプレイヤー数の長さになります。
  • ゲームに再参加する際、同じ Quantum SessionRunner.Arguments.ClientId でセッションを開始すると、同じプレイヤーインデックスがクライアントに割り当てられます。

Photon Id

プレイヤーの Photon Id に対応するものは、Frame API を介して識別できます:

  • Frame.PlayerToActorId(PlayerRef player) は Quantum PlayerRef を ActorId(Photon クライアント ID)に変換します。または、
  • Frame.ActorIdToAllPlayers(Int32 actorId) は前のメソッドの逆プロセスです。

重要: Photon Id は Quantum シミュレーションには無関係です。

ゲームを開始する

SessionRunner.Start() を呼び出した後、以下の開始プロトコルシーケンスが発生します。

  1. サーバーに SessionConfig と RuntimeConfig を添付した StartRequest が送信されます。
  2. リクエストはサーバーで受信され、検証され、選択された設定を含む SimulationStart 確認がクライアントに返送されます。リクエストに添付された情報が無効な場合、リクエストは PluginDisconnect メッセージで拒否されます。
  3. クライアントは SimulationStart ゲームメッセージを受け取ります。クライアントにはまだプレイヤーが存在しません。すべてのクライアントは観客としてのみ開始し、入力やコマンドを送信することはできません。
  4. (オプション) - 遅延参加の場合、クライアントはスナップショット再同期を受け取るかもしれません。
  5. QuantumGame.AddPlayer() を使用して Quantum プレイヤーを登録し、個々の RuntimePlayer 設定を送信します。
  6. サーバーは成功した参加者を入力にエンコードし、すべてのクライアントが同じティックで処理され、ISignalOnPlayerAdded シグナルが呼び出されます。失敗した試行には OnLocalPlayerAddFailed コールバックで対応されます。
Config Sequence Diagram
シーケンスダイアグラム

関与する構成ファイルの詳細については、構成ファイル マニュアルを参照してください。

プレイヤーの追加と削除

クライアントがプレイヤーを追加する前に、ゲームが開始されるのを待つ必要があります。これは 2 つの方法で行うことができます。

A) 非同期形式の SessionRunner.StartAsync を使用し、戻り値を待ちます。

C#

// この呼び出しは接続ロジックが完了したら戻ります(必要に応じてスナップショットを受信)
var runner = (QuantumRunner)await SessionRunner.StartAsync(sessionRunnerArguments);

// オンラインシミュレーションにプレイヤーを追加
var runtimePlayer = new RuntimePlayer { PlayerNickname = "whiskeyjack29" };
runner.Game.AddPlayer(runtimePlayer);

B) QuantumAddRuntimePlayers.cs または QuantumRunnerLocalDebug.cs スクリプトに示されているように、CallbackGameStarted に登録します。

C#

public class QuantumAddRuntimePlayers : QuantumMonoBehaviour {
    public RuntimePlayer[] Players;

    public void Awake() {
        QuantumCallback.Subscribe(this, (CallbackGameStarted c) => OnGameStarted(c.Game, c.IsResync), game => game == QuantumRunner.Default.Game);
    }

    public void OnGameStarted(QuantumGame game, bool isResync) {
        for (int i = 0; i < Players.Length; i++) {
            game.AddPlayer(i, Players[i]);
        }
    }
}

AddPlayer() は、スロットごとに一度だけ呼び出すことができます。何らかのエラーが発生した場合、サーバーは AddPlayerFailed プロトコルイベントを送信します(以下のコールバックを参照)。

操作によってウェブリクエストが発生するため、サードパーティのバックエンドを保護するためのレート制限があります。

クライアントが要求できるプレイヤーの総数は、ウェブフックまたは Photon ダッシュボードパラメータ MaxPlayerSlots によって制限されます。

プレイヤー関連の操作を送信する API は次のとおりです:

C#

class QuantumGame {
  // プレイヤーデータをサーバーに送信し、プレイヤーを追加するリクエストを送る(プレイヤースロット0を使用)
  void AddPlayer(RuntimePlayer data);
  // 特定のプレイヤースロットに対してプレイヤーデータをサーバーに送信する
  void AddPlayer(int playerSlot, RuntimePlayer data);
  // プレイヤースロット0を削除するリクエストを送信する
  void RemovePlayer();
  // 特定のプレイヤーを削除する
  void RemovePlayer(int playerSlot);
  // このクライアントに属するすべてのプレイヤーを削除する
  void RemoveAllPlayers();
}

次の Quantum コールバックはビューでリストされ、使用できます。

C#

CallbackLocalPlayerAddConfirmed {
  public Frame Frame;
  public int PlayerSlot;
  public PlayerRef Player;
}

CallbackLocalPlayerAddFailed {
  public int PlayerSlot;
  public string Message;
}

CallbackLocalPlayerRemoveConfirmed {
  public Frame Frame;
  public int PlayerSlot;
  public PlayerRef Player;
}

CallbackLocalPlayerRemoveFailed {
  public int PlayerSlot;
  public string Message;
}

// 例
QuantumCallback.Subscribe(this, (CallbackLocalPlayerAddConfirmed c) => OnLocalPlayerAddConfirmed(c));
private void OnLocalPlayerAddConfirmed(CallbackLocalPlayerAddConfirmed c) { }

次の Quantum シグナルはシミュレーションでリストされ、使用されます。プレイヤースロットに関する情報は、ローカルプレイヤーにのみ利用可能な情報です。

C#

// このプレイヤー ref がプレイヤーに初めて割り当てられたとき。
 // firstTime が false の場合は、プレイヤー ref が別のプレイヤーによって再利用されています。
ISignalOnPlayerAdded(Frame f, PlayerRef player, bool firstTime)
ISignalOnPlayerRemoved
(Frame f, PlayerRef player)

PlayerConnectedSystem

PlayerConnectedSystem は、Input & Connection Flags を使用してプレイヤーのオンライン接続状態を追跡する別の方法です。これを SystemsConfig に追加して使用します。

接続および切断の Quantum システムコールバックを受け取るには、ISignalOnPlayerConnected および ISignalOnPlayerDisconnected を実装する必要があります。

RuntimePlayer

クラス RuntimePlayer は、選択されたキャラクターなど、プレイヤー固有の情報を保持します。

このクラスは、サーバーに送信されたときと受信されたときに JSON シリアル化されます。

RuntimePlayerquantum.code プロジェクト内の部分クラスであり、カスタム実装は RuntimePlayer.User.cs に記述される必要があります。データを追加する際に JSON シリアル化の互換性を考慮してください。

ランタイムでのアクセス

任意のプレイヤーに関連付けられた RuntimePlayer アセットは、その PlayerRefFrame.GetPlayerData() をクエリすることで取得できます。

C#

public void OnPlayerDataSet(Frame f, PlayerRef player){
  var data = f.GetPlayerData(player);
}

プレイヤーエンティティの初期化

プレイヤーによって制御されるエンティティは、シミュレーションの任意の時点で初期化できます。一般的なアプローチは、プレイヤーが接続したとき(ISignalOnPlayerConnected)やプレイヤーデータが受信されたとき(ISignalOnPlayerAdded)に初期化することです。

  • ISignalOnPlayerConnected:プレイヤーエンティティは、シミュレーションまたはアセットデータベース内で既に利用可能な情報で初期化できます。
  • ISignalOnPlayerAdded:プレイヤーエンティティは、プレイヤーの RuntimePlayer に関連付けられた情報で初期化できます。これは、選択したキャラクターモデルやインベントリのロードなどに便利です。

シミュレーションとビュー

まず、いくつかの明確化を行います:

  1. シミュレーションの視点(Quantum)から見ると、プレイヤーが制御するエンティティはプレイヤー入力を持つエンティティです。ローカルプレイヤーやリモートプレイヤーの概念は知りません。
  2. ビューの視点(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 は、シミュレーションが処理できるプレイヤーの数を実際に定義します。
Back to top