This document is about: QUANTUM 2
SWITCH TO

Commands

概述

Quantum命令是通往Quantum標準API的一個輸入資料路徑。他們類似於Quantum輸入,但是 需要在每個刷新被發送。

Quantum命令完全可靠。伺服器將 總是 接受它們並且確認它,無論它們被發送的時間為何。這產生一個取捨;那就是本機客戶端,包含發送命令的客戶端,將無法預測模擬收到命令的那個刷新。雖然如果需要的話可以顯示視覺預測,但模擬將只會在伺服器已經確認命令作為刷新的一部分 之後 接收命令。

命令作為一個常規的C#類別被執行,其繼承於Photon.Deterministic.DeterministicCommand。它們可以含有任何可序列化的資料。

C#

using Photon.Deterministic;
namespace Quantum
{
    public class CommandSpawnEnemy : DeterministicCommand
    {
        public long enemyPrototypeGUID;

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

        public void Execute(Frame f)
        {
            var enemyPrototype = f.FindAsset<EntityPrototype>(enemyPrototypeGUID);
            enemyPrototype.Container.CreateEntity(f);
        }
    }
}

在模擬中的命令系統設定

為了使命令被Quantum發送,需要針對它們來建立一個系統,並且使其注意到可用的命令。

  • 在Quantum 2.1及之後版本中,命令需要被註冊在CommandSetup.User.cs中。
  • 在Quantum 2.0中,命令需要被註冊在CommandSetup.cs中。

確定性命令設定

命令需要被新增到CommandSetup.User.cs中的命令處理站,以在運行階段可用。

請注意: CommandSetup.Legacy.cs並不是直接在此設定中被使用;而是,它需要被保存於2.1中以符合相容性要求。

C#

// CommandSetup.User.cs

namespace Quantum {
  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>());
    }
  }
}

----------

// CommandSetup.Legacy.cs

using Photon.Deterministic;
namespace Quantum {
  public static class CommandSetup {
    public static DeterministicCommand[] CreateCommands(RuntimeConfig gameConfig, SimulationConfig simulationConfig) {
      return new null;
    }
  }
}

命令設定(只針對2.0!)

在Quantum 2.0中,命令需要在CommandSetup.cs中被註冊,以在運行階段可用。

請注意: 此系統在Quantum的較新版本中已過時;請參見DeterministicCommandSetup以取得更多資訊。

C#

using Photon.Deterministic;
namespace Quantum {
  public static class CommandSetup {
    public static DeterministicCommand[] CreateCommands(RuntimeConfig gameConfig, SimulationConfig simulationConfig) {
      return new DeterministicCommand[] {

        // user commands go here
        new CommandSpawnEnemy(),
      };
    }
  }
}

從檢視發送命令

可從Unity中的任何地方發送命令。

C#

using Quantum;
using UnityEngine;

public class UISpawnEnemy : MonoBehaviour
{
    [SerializeField] private EntityPrototypeAsset enemyPrototype = null;
    private PlayerRef _playerRef;

    public void Initialize(PlayerRef playerRef) {
        _playerRef = playerRef;
    }

    public void SpawnEnemy() {
        CommandSpawnEnemy command = new CommandSpawnEnemy()
        {
            enemyPrototypeGUID = enemyPrototype.Settings.Guid.Value,
        };
        QuantumRunner.Default.Game.SendCommand(command);
    }
}

從檢視發送複合命令

請注意: 這只在Quantum 2.1及之後的版本中才可用。

為了一次發送多個命令,只需建立一個CompoundCommand,並且在發送它之前新增各個獨立的DeterministicCommand到其上。

C#

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

    QuantumRunner.Default.Game.SendCommand(compound);

多載

SendCommand()有兩個多載。

C#

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

如果多位玩家由同一台機器控制,請指定玩家索引(PlayerRef)。只有一位本機玩家的遊戲,可以忽略玩家索引欄位。

從模擬輪詢命令

為了在模擬中接收及處理命令,請針對一個特定的玩家來輪詢幀:

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);
            }
        }
    }
}

如同任何其他的系統,處理命令輪詢及取用的系統,需要被包含在SystemSetup.cs之中

C#

namespace Quantum {
  public static class SystemSetup {
    public static SystemBase[] CreateSystems(RuntimeConfig gameConfig, SimulationConfig simulationConfig) {
      return new SystemBase[] {
        // pre-defined core systems
        [...]

        // user systems go here
        new PlayerCommandsSystem(),

      };
    }
  }
}

注意事項

API並不強制,也不執行針對命令的一個特定的回調機制或設計模式。這取決於開發者,以選擇如何使用、解譯和執行命令;舉例而言,透過將他們編碼到信號、使用一個責任鏈,或是執行命令執行,以作為一個方法。

針對集合的實例

清單

C#

using System.Collections.Generic;
using Photon.Deterministic;

namespace Quantum
{
    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#

using Photon.Deterministic;

namespace Quantum
{
    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;
            }
        }
    }
}

複合命令實例

每個刷新中,只有一個命令可以被附加到一個輸入串流。縱使一個客戶端在每個刷新中可以發送多個確定性命令,命令將不會在同一個刷新中到達模擬,而是將在接續的刷新中各自到達。為了繞過這個限制,您可以將多個確定性命令打包到一個單一的CompoundCommand中。

Quantum 2.1已經提供這個類別。並且針對先前的版本,它可以像這樣被新增:

C#

public class CompoundCommand : DeterministicCommand {
  public static DeterministicCommandSerializer CommandSerializer;
  public List<DeterministicCommand> Commands = new List<DeterministicCommand>();

  public override void Serialize(BitStream stream) {
    if (CommandSerializer == null) {
      CommandSerializer = new DeterministicCommandSerializer();
      CommandSerializer.RegisterPrototypes(CommandSetup.CreateCommands(null, null));
    }

    var count = Commands.Count;
    stream.Serialize(ref count);

    if (stream.Reading) {
      Commands.Clear();
    }

    for (var i = 0; i < count; i++) {
      if (stream.Reading) {
        CommandSerializer.ReadNext(stream, out var cmd);
        Commands.Add(cmd);
      } else {
        CommandSerializer.PackNext(stream, Commands[i]);
      }
    }
  }
}

為了接著分派已複合的命令:

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) {
        }
      }
  }
}
Back to top