よくある質問
質問内容の多くは、Discordの#quantum-sdk-v2
や#beginner-questions
で開発者から受けた質問になります。Discordの検索機能を使用して、詳細な情報を得ることもできます。
QuantumのUnityフレームワーク
デモメニューからゲーム開始すると、Gameシーンがロードされないのはなぜですか?
AppIdがサーバーから拒否された時は、適切なエラーメッセージが表示されません。正しいAppIdが作成できているか確認してください。
- ダッシュボードからQuantumのAppIdを作成する
- 「新しくアプリを作成する」をクリックする
- Photonの種別をPhoton Quantumにする
- 各フィールドを記入する
- 画面下の「作成する」を押す
Quantumのゲームが開始するまで1秒の遅延があるのはなぜですか?
Deterministic Config
アセットのRoom Wait Time (seconds)
を確認して、調整しましょう。Room Wait Timeは、ping値の変動の対処に使用されます。
セッション開始時にすべてのクライアントの同期を行うために、常に設定された時間分が使用されます。1秒に設定すれば、常に1秒待機します。
注意: シーンのロード時間を同期するために使用しないでください!これを調整したい場合は、Quantumセッションを開始する前にシーンをロードし、Photon Realtimeから直接調整してください。そうすれば、値を安全に0にできます。
ns.exitgames.comに接続を試みるとタイムアウトするのはなぜですか?
詳細を取得するためにPhotonServerSettings
のNetwork Logging
からログレベルを上げてください。大抵の場合、UDPがブロックされたことが原因です。
この状況を調査する方法については、Photon Realtimeのドキュメントの「接続が切断された場合の調査」ページをご覧ください。
フリーズしたゲームをデバッグする方法はありますか?
Project Settings > Player > Scripting Define Symbols
にQUANTUM_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)
ポーズやブレークポイントのデバッグ後に、ゲームのシミュレーションが早くなるのはなぜですか?
デフォルトでは、時間は内部的に計測され、シミュレーションの停止を補いません。SimulationConfig
のDeltaTimeType
をEngineDeltaTime
に変更すると、ゲームプレイはポーズ後に通常速度で再開します。ただしこれを変更すると、すべてのクライアントがその設定を使用することになるため、デバッグのみで使用する際には望ましくないかもしれません。とはいえ、カメラ操作が激しいようなゲーム(例:フライトシミュレーション)では、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が必要です)
インポート時の三角形カリングを将来提供予定で、これがもう一つの回避策になります。

シーンのロードに時間がかかると、タイムアウトで切断してしまうのはなぜですか?
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から開始するのはなぜですか?
シミュレーション開始時に、ロールバック可能なフレーム数が割り当てられます。この数は、DeterministicConfig
のRollback Window
設定と同じ値になります。
同一フレーム数でUpdate()が複数回呼び出されるのはなぜですか?
Quantumをオンラインで実行中、ロールバックによってシステムのUpdate()
が同一フレームで複数回呼び出されます。リモートプレイヤーの入力の予測ミスを検知すると、そのフレームの正しい入力データでシミュレーションを再実行して、決定論的な状態に復帰します。
フレームが検証済みかどうかはframe.IsVerified
からチェックすることができます。Quantumをオフラインモードで実行中は、ロールバックが発生しないため複数回呼び出されることはありません。
FP.MaxValueとFP.UseableMaxの違いは何ですか?
固定小数点演算では、64-bitの値の16+16bitのみを使用します。これにより、オーバーフローのチェックが不要になる分だけ、一部の演算が高速になります。FP.MinValue
とFP.MaxValue
は64bitすべてを使用したもので、計算で使用すべきではありません。かわりにFP.UseableMax
とFP.UseableMin
を使用してください(例えば、距離の値をFP
の最小値で初期化する際など)。
注意: FP
が表現できる値は、-32,768 ~ 32,768(-2¹⁵ ~ 2¹⁵)です。
新しい構造体へのポインタが古いデータを指すのなぜですか?
new
もdefault
も使用していない場合、ループ内での構造体のポインタは同じスタックポインタを返すため、古いデータを含みます。
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()
を使用して新しいルームへ接続する - アプリのリスタート、接続エラー、プレイヤー参加待ちなどの接続問題を軽減する
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を使用できます。
- QuantumのUnityフレームワーク
- デモメニューからゲーム開始すると、Gameシーンがロードされないのはなぜですか?
- Quantumのゲームが開始するまで1秒の遅延があるのはなぜですか?
- ns.exitgames.comに接続を試みるとタイムアウトするのはなぜですか?
- フリーズしたゲームをデバッグする方法はありますか?
- Unityエディタでゲームを実行中、ネットワークの遅延をシミュレートする方法はありますか?
- ポーズやブレークポイントのデバッグ後に、ゲームのシミュレーションが早くなるのはなぜですか?
- ジオメトリ内部に孤立したナビメッシュが生成されるのはなぜですか?
- シーンのロードに時間がかかると、タイムアウトで切断してしまうのはなぜですか?
- bracket nesting level exceeded maximumでIL2CPPのコンパイルが失敗します
- Quantumのシミュレーション開発
- シミュレーションがフレーム60から開始するのはなぜですか?
- 同一フレーム数でUpdate()が複数回呼び出されるのはなぜですか?
- FP.MaxValueとFP.UseableMaxの違いは何ですか?
- 新しい構造体へのポインタが古いデータを指すのなぜですか?
- シミュレーションの同期がズレるのはなぜですか?
- 同じクライアントの新しいQuantumセッションに、Photonのルームを再利用できますか?
- QuantumアセットでDSLが生成した共用体を使用できますか?
- 請求