This document is about: QUANTUM 3
SWITCH TO

よくある質問

質問内容の多くは、Discordの#quantum-sdk-v2#beginner-questionsで開発者から受けた質問になります。Discordの検索機能を使用して、詳細な情報を得ることもできます。

QuantumのUnityフレームワーク

デモメニューからゲーム開始すると、Gameシーンがロードされないのはなぜですか?

AppIdがサーバーから拒否された時は、適切なエラーメッセージが表示されません。正しいAppIdが作成できているか確認してください。

  1. ダッシュボードからQuantumのAppIdを作成する
  2. 「新しくアプリを作成する」をクリックする
  3. Photonの種別をPhoton Quantumにする
  4. 各フィールドを記入する
  5. 画面下の「作成する」を押す

Quantumのゲームが開始するまで1秒の遅延があるのはなぜですか?

Deterministic ConfigアセットのRoom Wait Time (seconds)を確認して、調整しましょう。Room Wait Timeは、ping値の変動の対処に使用されます。
セッション開始時にすべてのクライアントの同期を行うために、常に設定された時間分が使用されます。1秒に設定すれば、常に1秒待機します。

注意: シーンのロード時間を同期するために使用しないでください!これを調整したい場合は、Quantumセッションを開始する前にシーンをロードし、Photon Realtimeから直接調整してください。そうすれば、値を安全に0にできます。

ns.exitgames.comに接続を試みるとタイムアウトするのはなぜですか?

詳細を取得するためにPhotonServerSettingsNetwork Loggingからログレベルを上げてください。大抵の場合、UDPがブロックされたことが原因です。
この状況を調査する方法については、Photon Realtimeのドキュメントの「接続が切断された場合の調査」ページをご覧ください。

フリーズしたゲームをデバッグする方法はありますか?

Project Settings > Player > Scripting Define SymbolsQUANTUM_STALL_WATCHER_ENABLED定義を追加すると、Updateループを監視するスレッドを立ち上げるスクリプトが有効になります。ストールが検出される(例:Updateの呼び出しに数秒以上かかる)と、クラッシュログが作成されます。生成されたログにはすべてのスレッドのコールスタックが含まれるので、シミュレーションのフリーズをデバッグする際に役立つでしょう。

Unityエディタでゲームを実行中、ネットワークの遅延をシミュレートする方法はありますか?

Quantumのパフォーマンスプロファイラーには、遅延シミュレーションが搭載されています。

アドオン | Profilerからダウンロードできます。

または、例えばClumsy(Windows)のような外部のネットワーク遅延ツールを使用した上で、ゲームサーバーのポートをフィルタリングしてください。

  • UDP 5056
  • TCP 4531
Clumsy Filter: (udp.DstPort == 5056 or udp.SrcPort == 5056) or (tcp.DstPort == 4531 or tcp.SrcPort == 4531)

ポーズやブレークポイントのデバッグ後に、ゲームのシミュレーションが早くなるのはなぜですか?

デフォルトでは、時間は内部的に計測され、シミュレーションの停止を補いません。SimulationConfigDeltaTimeTypeEngineDeltaTimeに変更すると、ゲームプレイはポーズ後に通常速度で再開します。ただしこれを変更すると、すべてのクライアントがその設定を使用することになるため、デバッグのみで使用する際には望ましくないかもしれません。とはいえ、カメラ操作が激しいようなゲーム(例:フライトシミュレーション)では、EngineDeltaTimeの設定が役に立つでしょう。

C#

public enum SimulationUpdateTime {
    Default = 0,                        // internal clock
    EngineDeltaTime = 1,                // Time.deltaTime (Unity)
    EngineUnscaledDeltaTime = 2         // Time.unscaledDeltaTime
}

ジオメトリ内部に孤立したナビメッシュが生成されるのはなぜですか?

これはUnityによって修正されました。(以下のポストをご覧ください:forum.unity.com/threads/nav-generating-inside-non-walkable-objects

NavmeshModifierVolumeによる回避策で問題を軽減できます。(NavMeshComponentsが必要です)

インポート時の三角形カリングを将来提供予定で、これがもう一つの回避策になります。

Navmesh Island

シーンのロードに時間がかかると、タイムアウトで切断してしまうのはなぜですか?

Unityのシーンをロードする時は、たとえLoadSceneAsyncを使用していても、シーンの大きさや複雑さに応じてメインスレッドがしばらくフリーズすることがあります。フリーズ中に通信が行われないことで、タイムアウトを原因とする切断エラーが起こる結果になります。

これを防ぐために、ConnectionHandlerクラスのAPIを使用できます。セットアップ方法と使用方法は以下の通りです。

  • ConnectionHandlerコンポーネントが追加されたゲームオブジェクトがなければ、追加する。

  • コンポーネントには、KeepAliveInBackgroundフィールドがあり、接続を維持する時間を増やすことができます。値の単位はミリ秒です。

  • QuantumLoadBalancingClientの参照を渡します。静的なgetterのUIMainがあるので、それを使用できます。その後、StartFallbackSendAckThreadを開始します。サンプルのスニペットは以下の通りです。

C#

    // Before starting loading the scene
    if (_connectionHandler != null)
    {
      _connectionHandler.Client = UIMain.Client;
      _connectionHandler.StartFallbackSendAckThread();
    }

bracket nesting level exceeded maximumでIL2CPPのコンパイルが失敗します

IL2CPPのコンパイルでは、以下のエラーが投げられます。

bracket nesting level exceeded maximum

これは、例えばRuntimeConfigや、DSL生成コンポーネントのGetHashCode()メソッドのような、大きなクラスや構造体で発生します。

この問題の回避策は、大きなデータを小さな構造体に分けることです。

Quantumのシミュレーション開発

シミュレーションがフレーム60から開始するのはなぜですか?

シミュレーション開始時に、ロールバック可能なフレーム数が割り当てられます。この数は、DeterministicConfigRollback Window設定と同じ値になります。

同一フレーム数でUpdate()が複数回呼び出されるのはなぜですか?

Quantumをオンラインで実行中、ロールバックによってシステムのUpdate()が同一フレームで複数回呼び出されます。リモートプレイヤーの入力の予測ミスを検知すると、そのフレームの正しい入力データでシミュレーションを再実行して、決定論的な状態に復帰します。
フレームが検証済みかどうかはframe.IsVerifiedからチェックすることができます。Quantumをオフラインモードで実行中は、ロールバックが発生しないため複数回呼び出されることはありません。

FP.MaxValueとFP.UseableMaxの違いは何ですか?

固定小数点演算では、64-bitの値の16+16bitのみを使用します。これにより、オーバーフローのチェックが不要になる分だけ、一部の演算が高速になります。FP.MinValueFP.MaxValueは64bitすべてを使用したもので、計算で使用すべきではありません。かわりにFP.UseableMaxFP.UseableMinを使用してください(例えば、距離の値をFPの最小値で初期化する際など)。

注意: FPが表現できる値は、-32,768 ~ 32,768(-2¹⁵ ~ 2¹⁵)です。

新しい構造体へのポインタが古いデータを指すのなぜですか?

newdefaultも使用していない場合、ループ内での構造体のポインタは同じスタックポインタを返すため、古いデータを含みます。

C#

struct Bar {
    public bool Foo;
}

static unsafe void Main(string[] args) {
    for (int i = 0; i < 2; i++) {

        Bar bar;
        //Bar bar = default(Bar); // <---- Fixes the stale data
       
        Bar* barPt = &bar;
        if (barPt->Foo)
            Console.WriteLine("Stuff and Things");
  
        barPt->Foo = true;
    }

    Console.ReadKey();
}

シミュレーションの同期がズレるのはなぜですか?

DeterministicConfig.ChecksumIntervalが0より大きい場合、検証済みフレームのチェックサムが計算されてサーバーに送られた後に、他のクライアントが送信したチェックサムと比較されます。

よくある原因としては、

Quantumのデータアセットへの書き込み

C#

var characterSpecAsset = frame.FindAsset<CharacterSpec>("WhiteFacedBarghast");
characterSpecAsset.RemainigLifetime = 21;

絶対にQuantumのアセットに書き込みしないでください。これらは読み取り専用のデータです。

UnityのスレッドからQuantumへの書き込み

QuantumのFrameで公開されているあらゆるデータは、Unityのスクリプトからは読み取り専用でアクセスしてください。シミュレーションに影響を与えるのは、入力とコマンドのみにしてください。

データのキャッシュ

以下のコードは、シミュレーションがロールバックした際に同期ズレを引き起こします。

C#

public class CleaningSystem : SystemBase {
    public Boolean HasShoweredToday;    // <----- Error
    public override void Update(Frame f) {
        if (!HasShoweredToday && f.Global->ElapsedTime > 100) {
            Shower();
            HasShoweredToday = true;
        }
    }
}

かわりに、FrameかEntityComponentにデータを保存してください。

C#

// Frame
unsafe partial class Frame {
    public Boolean HasShoweredToday;
    partial void CopyFromUser(Frame frame) {
        // Implement copy of the custom parameters.
    }
}

public class CleaningSystem : SystemBase {
    public override void Update(Frame f) {
        if (!f.HasShoweredToday && f.Global->ElapsedTime > 100) {
            Shower();
            f.HasShoweredToday = true;
        }
    }
}

浮動小数点演算

シミュレーション中でのfloatの使用を控えて、固定小数点演算のみを使用してください。

FP.FromFloat_UNSAFE()の操作には注意です。同一の端末のオフラインでアセット生成に使用する分には問題ありませんが、異なるプラットフォームでは異なる結果が返ることに留意してください。もし、実行時に浮動小数点数のインポートが必要で、整数や固定小数点数を使用できない(例:ダウンロードしたデータ)場合は、文字列から固定小数点数へ変換してください。

AssetObject.Loaded()中に作成されたデータ

AssetObject.Loaded()は、ロード中にアセットごとに一度だけ呼び出されます。このタイミングで、アセットのメンバーに計算したデータを保存するだけならまったく問題はありません。 注意: サーバー上でシミュレーションを実行している場合は、一つのアセットが共有されます。

もし、アセットをResourcesからロードして、Unityエディタをリスタートするか実行時にUnity DBをリセットした場合、Unityアセットはアンロードされないことに留意してください。

C#

public partial class FooAsset {
  public Foo Settings;
  public int RawData;
  
  [NonSerialized]
  public List<int> Bar = new List<int>();

  public override AssetObject AssetObject => Settings;

    public override void Loaded(IResourceManager resourceManager, Native.Allocator allocator)
    {
      base.Loaded(resourceManager, allocator);
    // This will break on the second run (see above) because Bar needs to be reset by either Bar.Clear() or Bar = new List<int>()
    Bar.Add(RawData);
  }
}

同じクライアントの新しいQuantumセッションに、Photonのルームを再利用できますか?

いいえ、別のQuantumセッションにルームを再利用すべきではありません。

ただし、ルーム内のプレイヤー(または、その一部)を保持して、シミュレーションを柔軟にリスタートすることはできます。「Stumble Guys」で3つのステージを進行する方法は以下の通りです。

  • ラウンド終了後もQuantumセッションを保持する
  • ラウンド開始の処理を行うコードを、ゲームプレイのシステム(例:ゲームのステートマシン)に追加する
  • 決定論的にQuantumのシステムを無効にして、新しいQuantumのMapをロードする
  • すべてのQuantumのEntityをリセットするか破棄して、ゲームビューもリセットする
  • 負けたプレイヤーの接続を維持して観戦できるようにするが、ゲームには何の影響を与えないようにする

代替案として、すべてのプレイヤーのルームを切り替えることもできます。しかしこれはサーバー移動を含み、分散システムの様々な点で失敗する可能性があるので、ルーム移動は最終手段として、シミュレーションの柔軟なリスタートの方が推奨されます。

  • クライアント間で新しいルームのIDを(ルームプロパティやQuantumのコマンドなどを使用して)共有するか、新しいルームを決定論的に作成する
  • 各クライアントはQuantumセッションを停止してLeaveRoom()を実行するが切断はしない
  • すべてのクライアントがJoinOrCreate()を使用して新しいルームへ接続する
  • アプリのリスタート、接続エラー、プレイヤー参加待ちなどの接続問題を軽減する

Photon Realtime マッチメイキングガイド

QuantumアセットでDSLが生成した共用体を使用できますか?

直接はできません。Unityはフィールドのオーバーラップをサポートしないためです。

かわりに<UnionName>_Prototypeを、ドロワーを持つUnityのシリアライズ可能な値に使用できます。

以下のようにして、共用体に変換してください。

C#

UnionName result = default;
prototype.Materialize(frame, ref result, default);

請求

学生、趣味でおこなっているディベロッパー、インディー向けの割引はありますか?

弊社の製品にはすべて、無料プランとワンショットのプランがあります。
また弊社は通常、Unityアセットストアのセールに参加し、また当選者にはクーポンを提供しています。

1つのPhotonアプリケーションに、複数の100CCUプランを組み合わせることはできますか?

いいえ。
100CCUプランは1つのAppIDにつき1回のみ適用でき、複数の100CCUプランを使用することはできません。
複数のPUN+アセットシートを購入している場合には、各AppIDに対して個別の無料100CCUを適用する必要があります。
1つのアプリに対してさらに多くのCCUが必要な場合、次の上位プランは500CCUです。
月額または年額プランをご利用の場合には、その月額/年額プランのCCUに加えて、12ヶ月間有効の100CCUを使用できます。

Back to top