This document is about: QUANTUM 3
SWITCH TO

コマンド

はじめに

Quantumコマンドは、入力を使用してデータをQuantumシミュレーションに送信する代替手段です。コマンドはQuantum入力に似ていますが、毎回送信する必要はなく、特定の状況でトリガーされることができます。

Quantumコマンドは完全に信頼性があります。デフォルトでは、サーバーは常にそれらを受け入れ、確認します。送信された時間に関係なく、コマンドはローカルで予測されたフレームに組み込まれ、ローカルマシン上で迅速に実行されます。しかし、リモートクライアントにとってはトレードオフがあります。リモートクライアントはコマンドがシミュレーションに受信されるティックを予測できないため、コマンドが受信されるまでに遅延が発生します。

コマンドは、Photon.Deterministic.DeterministicCommandを継承した通常のC#クラスとして実装されています。これらのクラスは、シリアライズ可能な任意のデータを含むことができます。

C#

namespace Quantum
{
  using Photon.Deterministic;

  public class CommandSpawnEnemy : DeterministicCommand
  {
    public AssetRefEntityPrototype EnemyPrototype;

    public override void Serialize(BitStream stream)
    {
      stream.Serialize(ref EnemyPrototype);
    }

    public void Execute(Frame f)
    {
      f.Create(EnemyPrototype);
    }
  }
}

シミュレーションにおけるコマンドの設定

コマンドクラスが定義されたので、次にそれをDeterministicCommandSetupのファクトリに登録する必要があります。Assets/QuantumUser/Simulationへ移動し、スクリプト CommandSetup.User.cs を開いてください。次のように、希望するコマンドをファクトリに追加します。

C#

// CommandSetup.User.cs

namespace Quantum {
  using System.Collections.Generic;
  using Photon.Deterministic;

  public static partial class DeterministicCommandSetup {
    static partial void AddCommandFactoriesUser(ICollection<IDeterministicCommandFactory> factories, RuntimeConfig gameConfig, SimulationConfig simulationConfig) {
      // user commands go here
      // new instances will be created when a FooCommand is received (de-serialized)
      factories.Add(new FooCommand());

      // BazCommand instances will be acquired from/disposed back to a pool automatically
      factories.Add(new DeterministicCommandPool<BazCommand>());
    }
  }
}

ビューからのコマンドの送信

コマンドはUnity内のどこからでも送信することができます。

C#

namespace Quantum
{
  using UnityEngine;

  public class EnemySpawnerUI : MonoBehaviour
  {
    [SerializeField] private AssetRefEntityPrototype _enemyPrototype;

    public void SpawnEnemy()
    {
      CommandSpawnEnemy command = new CommandSpawnEnemy()
      {
        EnemyPrototype = _enemyPrototype,
      };
      QuantumRunner.Default.Game.SendCommand(command);
    }
  }
}

オーバーロード

SendCommand()には2つのオーバーロードがあります。

C#

void SendCommand(DeterministicCommand command);
void SendCommand(Int32 player, DeterministicCommand command);

複数のプレイヤーが同じマシンから操作される場合は、プレイヤーインデックス(PlayerRef)を指定してください。ローカルプレイヤーが1人だけのゲームでは、プレイヤーインデックスフィールドを無視しても問題ありません。

シミュレーションからのコマンドのポーリング

シミュレーション内でコマンドを受信して処理するには、特定のプレイヤーのフレームをポーリングします。

C#

using Photon.Deterministic;
namespace Quantum
{
    public class PlayerCommandsSystem : SystemMainThread
    {
        public override void Update(Frame f)
        {
            for (int i = 0; i < f.PlayerCount; i++)
            {
                 var command = f.GetPlayerCommand(i) as CommandSpawnEnemy;
                 command?.Execute(f);
            }
        }
    }
}

注意

APIは、コマンドに対して特定のコールバックメカニズムやデザインパターンを強制したり実装したりしません。コマンドをどのように消費、解釈、実行するかは開発者の判断に委ねられています。例えば、シグナルにエンコードしたり、責任のチェーンを使用したり、コマンド実行をそれ自体のメソッドとして実装したりすることができます。

コレクションの例

リスト

C#

namespace Quantum
{
    using System.Collections.Generic;
    using Photon.Deterministic;

    public class ExampleCommand : DeterministicCommand
    {
        public List<EntityRef> Entities = new List<EntityRef>();

        public override void Serialize(BitStream stream)
        {
            var count = Entities.Count;
            stream.Serialize(ref count);
            if (stream.Writing)
            {
                foreach (var e in Entities)
                {
                    var copy = e;
                    stream.Serialize(ref copy.Index);
                    stream.Serialize(ref copy.Version);
                }
            }
            else
            {
                for (int i = 0; i < count; i++)
                {
                    EntityRef readEntity = default;
                    stream.Serialize(ref readEntity.Index);
                    stream.Serialize(ref readEntity.Version);
                    Entities.Add(readEntity);
                }   
            }
        }
    }
}

配列

C#

namespace Quantum
{
    using Photon.Deterministic;

    public class ExampleCommand : DeterministicCommand
    {
        public EntityRef[] Entities;

        public override void Serialize(BitStream stream)
        {
            stream.SerializeArrayLength(ref Entities);
            for (int i = 0; i < Cars.Length; i++)
            {
                EntityRef e = Entities[i];
                stream.Serialize(ref e.Index);
                stream.Serialize(ref e.Version);
                Entities[i] = e;
            }
        }
    }
}

複合コマンドの例

1ティックあたり1つのコマンドしか入力ストリームに添付できません。クライアントは1ティックあたり複数の決定論的コマンドを送信できますが、コマンドは同じティックでシミュレーションに到達するのではなく、連続するティックで別々に到着します。この制限を回避するために、SDKが提供するCompoundCommandに複数の決定論的コマンドをパックすることが可能です。

ビューから複合コマンドをインスタンス化して送信する方法:

C#

    var compound = new Quantum.Core.CompoundCommand();
    compound.Commands.Add(new FooCommand());
    compound.Commands.Add(new BazCommand());

    QuantumRunner.Default.Game.SendCommand(compound);

複合コマンドのインターセプト:

C#

public override void Update(Frame f) {
  for (var i = 0; i < f.PlayerCount; i++) {
      var compoundCommand = f.GetPlayerCommand(i) as CompoundCommand;
      if (compoundCommand != null) {
        foreach (var cmd in compoundCommand.Commands) {
          // execute individual commands logic
        }
      }
  }
}
Back to top