This document is about: QUANTUM 3
SWITCH TO

入力

はじめに

入力は、Quantumコアアーキテクチャの重要なコンポーネントになります。決定論的ネットワーキングライブラリでは、システムの出力は与えられた入力によって完全に決定されます。つまり、ネットワーク内のすべてのクライアントの入力が同じであれば、出力も同じになります。

DSLでの定義

入力は、任意のDSLファイルで定義できます。例えば、移動方向とジャンプボタンを持つ入力構造体は、次のようになります。

Qtn

input
{
    button Jump;
    FPVector3 Direction;
}

サーバーは、ティックにおける完全なセット(すべてのプレイヤー入力)を確定し、それらをまとめて送信します。そのため、構造体はできるだけ最小サイズに抑えてください。

コマンド

決定論的コマンドは、Quantumの別の入力方法です。任意のデータとサイズを持つことができるため、「アイテムを購入する」「どこかにテレポートする」などの特殊な入力に最適です。

Unityでのポーリング

入力をQuantumシミュレーションへ送信するために、Unity内で入力をポーリングしてください。これを行うには、ゲームプレイシーンのMonoBehaviourPollInputコールバックを購読してください。

C#

  private void OnEnable()
  {
    QuantumCallback.Subscribe(this, (CallbackPollInput callback) => PollInput(callback));
  }

そして、コールバック内で入力ソースを読み込んで、入力構造体へデータを渡します。

C#

  public void PollInput(CallbackPollInput callback)
   {
    Quantum.Input i = new Quantum.Input();

    var direction = new Vector3();
    direction.x = UnityEngine.Input.GetAxisRaw("Horizontal");
    direction.y = UnityEngine.Input.GetAxisRaw("Vertical");

    i.Jump = UnityEngine.Input.GetKey(KeyCode.Space);

    // 固定小数点数に変換する
    i.Direction = direction.ToFPVector3();

    callback.SetInput(i, DeterministicInputFlags.Repeatable);
  }

備考:ここでは、浮動小数点数から固定小数点数への変換が、シミュレーションへ共有される前に行われているため、決定論的です。

最適化

Quantum 3は入力をデルタ圧縮するものの、帯域幅の最適化のためにInputの生データを出来るだけコンパクトにするのが、一般的なグッドプラクティスです。以下にいくつかの最適化方法を示します。

ボタン

キー入力の表現には、ブール型や類似のデータ型ではなく、DSLの入力定義でButton型を使用します。これはインスタンスごとに1ビットしか使用しないため、可能な限り使用することが望ましいです。ネットワーク上では1ビットしか使用しませんが、ローカルではもう少し多くのゲームステートを保持します。1ビットは現在のフレームでボタンが押されたかどうかのみを表現し、残りの情報はローカルで計算されます。
ボタンは以下のように定義されます。

Qtn

input
{
    button Jump;
}

Unityスクリプトからボタンの値をポーリングする際の重要な点は、「現在のボタンの状態(現在のフレームで押されているかどうか)」をポーリングすることです。これによって、Quantumは内部プロパティを自動的に設定して、ユーザーはシミュレーションコード上WasPressedIsDownWasReleasedなどの特定の状態をポーリングできます。
つまり、UnityではGetKeyUp()/GetKeyDown()などの特定の状態を設定する必要はありません。これらを使用すると、UnityとQuantumとの実行レートの違いから、いくつかの状態が失われ、入力の反応性が悪くなる問題が発生する可能性があります。

したがって、入力構造体のbutton値を設定する際は、以下のように常に現在のボタンの状態をポーリングするようにしてください。

C#

// Unity内でプレイヤー入力をポーリングする
input.Jump = UnityEngine.Input.GetKey(KeyCode.Space);

ボタンの状態は、Quantumシミュレーションコードからも更新できます。ユーザーが入力構造体とbutton型を使用してボットエンティティも更新する場合など、非プレイヤーエンティティのボタン押下をシミュレートするのに非常に便利です。これを実現するには、以下のようにシミュレーションコードでボタンの状態を毎フレーム設定する必要があります。

C#

// Quantumコード内
input.button.Update(frame, value);

そうすると、特定の状態(PressedDownReleased)も内部に生成されます。ボタンの状態を毎フレーム更新しないと、これら状態が誤って設定されることになります。

方向のエンコード

典型的には、移動は方向ベクトルで表されることが多く、DSLファイルで以下のように定義されます。

Qtn

input
{
    FPVector2 Direction;
}

しかし、FPVector2は2つのFPから構成されていて、16バイトのデータになります。同じルームに多数のクライアントがいる場合、送信データ量は大きくなる可能性があります。
これを最適化する方法の1つは、Input構造体を拡張して、方向ベクトルをベクトルではなくByteにエンコードして送信することです。実装の一例は次の通りです。

まず、通常通りに入力を定義し、方向のFPVector2Byteに置き換えて、エンコードされた値を保存するようにします。

Qtn

input
{
    Byte EncodedDirection;
}

次に、コンポーネントの拡張と同じ方法で入力構造体を拡張します。(機能の追加を参照)

C#

namespace Quantum
{
    partial struct Input
    {
        public FPVector2 Direction
        {
            get
            {
                if (EncodedDirection == default) 
                    return default;
                
                Int32 angle = ((Int32)EncodedDirection - 1) * 2;
            
                return FPVector2.Rotate(FPVector2.Up, angle * FP.Deg2Rad);
            }
            set
            {
                if (value == default)
                {
                    EncodedDirection = default;
                        return;
                }
               
                var angle = FPVector2.RadiansSigned(FPVector2.Up, value) * FP.Rad2Deg;
                
                angle = (((angle + 360) % 360) / 2) + 1;
            
                EncodedDirection = (Byte) (angle.AsInt);
            }
        }
    }
}

この実装によって、使用感は以前と同じままで、16バイトが1バイトになります。これは、Directionプロパティを利用して、EncodedDirectionの値を自動的にエンコード/デコードしています。

Back to top