よくある質問
FAQの多くは開発者から#quantum-sdk
Discord chatに寄せられたものです。詳細のヘルプについては検索機能もご使用ください。
Quantum Unityフレームワーク
なぜデモメニューからゲームを開始するとゲームシーンが読み込まれないのですか?
AppIdが拒否された場合の適切なエラーメッセージがまだありません。以下の手順で正しくAppIdを作成したかどうか確認してください。:
Quantum Appの作成方法についての詳細はこちらから: Photon Quantum Appを作成する
- PhotonダッシュボードでQuantumのAppIdを作成する
- CREATE A NEW APPをクリックする
- Photon Typeを Photon Quantum に設定する
- 名前フィールドを入力してCREATEを押す
- 新しいQuantum appを確認してMANAGEをクリックする
- 画面下までスクロールし CREATE A NEW PLUGIN を押す
- SAVE を押す
なぜQuantumゲームの開始時に5秒間遅延するのでしょうか?
Deterministic Config
を確認してRoom Wait Time (seconds)
を少ない数字に設定してください。
Room Wait Timeはping時間の揺れに対応するために使用します。
全てのクライアントセッションを同期して開始できるよう、常に時間いっぱい使われます。設定値が1秒の場合、常に1秒間待ちます。
これはシーンの読み込み時間の同期には使用しません。連携させる場合は、Quantumセッションを開始する前にシーンを読み込んで、Photonに直接連携させます。そうすることで、安全に値を0に設定できます。
なぜns.exitgames.comへの接続時にタイムアウトしてしまうのですか?
PhotonServerSettingsでコネクションログレベルNetwork Logging
を増やして詳細を確認します。
よくあるタイムアウトの原因はUDPトラフィックがブロックされてしまうことです。
Photonが使用しているポートを開けるかどうかこちらで確認してください: Photon TCP and UDP Port Numbers
社内ネットワークでポートを開けない場合はお問い合わせください。
なぜマルチプレイヤーモードでゲームを実行している場合、プレイヤーエンティティがインスタンス化されないのでしょうか?(ローカルモードではインスタンス化されます)
おそらくQuantumGame.SendPlayerData()
が各プレイヤーに対して実行されていないのでしょう。
デモメニューを使用してゲームを開始している場合は、メニューシーンのどこかにスクリプトCustomCallbacks.cs
を追加してください。
開始後、OnGameStart
シグナルが各プレイヤーに発火してQuantumゲームに参加する際に、SendPlayerData
メソッドを呼び出し、各シミュレーションにおいてプレイヤーとして追加する必要があります。明示的に呼び出す理由は、こうすることで後から参加するプレイヤーが簡素化されるからです。
なぜQuantumエンティティの作成はこんなに面倒なのでしょう?
QuantumRunner.Default.Game.Frames.Current.GetEntity(entityRef)
基本的に、複数のシミュレーションは同時に実行できます(キルカム機能などが一例です)。このように機能を統合する場合、開発者がコードベースを再構成する必要のないよう柔軟性を保っておくため、ゲームとフレームへのデフォルトのアクセス方法は作業が多くなっています。
推奨する処理方法はこちらです:
- QuantumCallbacks.csは、引数として呼び出されたQuantumゲームオブジェクトをパスします。グローバルのかわりにこちらを使用します。
- DSL (EventBase)で作成したQuantumイベントもまたIDeterministicGameオブジェクトをパスします。残念ながら、多くの場合はQuantumGameへキャストしなければなりません。
- デフォルト ゲームと 現在の フレームを使用してポインタをつかむ
EntityRefs
のまとまりごとに拡張メソッドを作成します :
C#
namespace Quantum {
public unsafe static class EntityExtensions {
public static Ball* GetPtr(this EntityRefBall entityRef) {
return QuantumRunner.Default.Game.Frames.Current.GetBall(entityRef);
}
}
}
- オリジナルのグローバルショートカットを作成する:
C#
namespace Quantum {
public static class MyGame {
public static Frame CurrentFrame {
get {
return QuantumRunner.Default.Game.Frames.Current;
}
}
}
}
なぜNetworkPeerクラスから処理していないイベントの警告が送られてくるのでしょう?
イベント102および103ですね。
これらはクロック同期で、Session.Join()
が呼び出される前に入力メッセージを受信します。シミュレーションに参加した後、関連のあるメッセージがクライアントに届きます。
本質的には、既にルームに入室しているため、プラグインセッションを実行するとローカルのセッションを開始する前でもメッセージが送信されます。
なぜQuantumのイベントの順序が異なるのでしょうか?
quantum_system プロジェクトでQUANTUM_FLIP_EVENT_ORDER
のscripting define symbolを設定していない限り、QuantumはUnityのイベントを逆の順番で放出します。
こちらもご参照ください: Community Wiki/Quantum-Gotchas
自分のQuantumDLLソースコードをUnityアセットフォルダ内に移動することはできますか?
別々のDLLを使用したワークフローのサポートをお勧めしていますが、開発のプロセスをスピードアップすることで達成できます。
完成版のゲームアプリは常にリリースDLLで構築してください。そうしないと、生命線であるILパフォーマンスパッチが欠けてしまいます。
- Unity 2017.3のアセンブリ定義機能が必要
quantum state
プロジェクトの全てのコードファイルは、 quantum.state というアセンブリ定義の1つの下でバンドルされる必要があります。- アセンブリ定義は安全でないコードも許可するように設定します。
quantum system
プロジェクトに対して追加のasm defを持っている場合は、Quantumステートのasm defをリンクします。- Unityプロジェクトで
Scripting Runtime Version
を.NET 4.x Equivalent
に設定する。 - パイプラインのどこかしらにQuantum ECSのコード生成とUnityアセットスクリプトのコード生成(quantum.stateのビルド前イベントおよびquantum.systemsのビルド後イベント)を実行するのには次善策が必要です。
なぜ編集モードではQuantum AssetLinksを使えないのでしょう?
AssetLinksを フォロー する前にUnityDB.Init()
を呼び出してください。
安全に、ゲームの実行中でなくても複数回呼び出せます。
リンク上でインスタンスのゲッタを使用する場合、リンクに保管されたGUIDを使ってQuantumDBのQuantumアセットを読みだそうとします。
Unity Editorでゲームを実行している場合、ネットワークのレイテンシのシミュレーションに有効な方法はありますか?
接続している間に、ゲームオブジェクトにquantum_unity/Assets/Photon Unity Networking/Plugins/PhotonNetwork/PhotonLagSimulationGui.cs
スクリプトをアタッチしてください。
ゲームのプレイ中にsimulate
を切り替えてラグ、ジッタ、ロスを有効にします。
なぜ停止したりブレイクポイントをデバッギングするとゲームのシミュレーションが速く動作するようになるのですか?
時間はデフォルトで内部で測定され、シミュレーションの中断に対する埋め合わせは行いません。SimulationConfigのDeltaTimeType
がEngineDeltaTime
に変更されると、一時停止した後ゲームプレイが通常の速度で再開します。警告:これを変更すると、すべてのクライアントが設定を使用するようになりますが、デバッギングのためだけに使用する場合は望ましくない可能性があります。タイトなカメラコントロールを伴うゲーム(例:飛行シミュレーション)では、EngineDeltaTime
に設定することでメリットがあります。
C#
public enum SimulationUpdateTime {
Default = 0, // internal clock
EngineDeltaTime = 1, // Time.deltaTime (Unity)
EngineUnscaledDeltaTime = 2 // Time.unscaledDeltaTime
}
IL2CPPを使用しているとJson直列化が壊れてしまうのはなぜでしょう?
使用されていないコードは自動的に破棄されます。こちらを参照してくださいhttps://docs.unity3d.com/Manual/IL2CPP-BytecodeStripping.html。
PUNフォルダ内にすでにlink.xml
があります。プロジェクトに自分のlink.xml
を追加してカスタムライブラリを有効化できます。
QuantumでHTML5エクスポートは可能ですか?
できません。Memcopyは、WebGLでは速さがたりません。
こちらをサポートする予定はあり、
この問題への対策を常にチェックしています。
ローカルプレイヤーの取得方法を教えてください。
QuantumGame.GetLocalPlayers()
は、各クライアントで一意の配列を返し、Quantumのしょみゅレーションでローカルのマシンが制御しているプレイヤーのインデックスを表します。
- 通常返されるインデックスは1つですが、ローカルのマシンが制御するプレイヤー数が1人以上の場合、配列の長さはローカルプレイヤーの数となります。
QuantumInput.Instance.PollInput(int player)
にパスされたものと全く同じインデックスです。- (ローカルゲームでない限り)サーバーはこのインデックスを認識します。
- インデックスは常に[0, PlayerCount-1]の中に納まります。
PlayerCount
が表すのはマッチ内のプレイヤーの合計数です。これはQuantumGame.StartGame()
内にパスされ、その後DeterministicConfig
で設定されます。 - インデックスの値は任意(もちろん範囲内で)で、複数のプレイヤーの接続・切断・メッセージの到着の順序に依拠します。
- ローカルマシーンに複数のプレイヤーがいる場合、値は必ずしも連続になるとは限りません。
- 同じGUIDで
Session.Join()
呼び出しを行い、ルームが新しいプレイヤーで満員になっていなければ、ゲームに再参加する場合、同じプレイヤーインデックスを割り当てられる可能性があります。
上記の関数のローカルプレイヤーインデックスを使用してランタイムプレイヤーデータを送信します。: QuantumGame.SendPlayerData(int player, RuntimePlayer data)
。マシン上のすべてのプレイヤーに対してこれを行います。
(player_ref
メンバーのいる)エンティティにローカルプレイヤーと関連性があるか決定するのに、Session.IsLocalPlayer(int player)
が役立ちます。
Frame.PlayerToActorId(PlayerRef player)
とFrame.ActorIdToAllPlayers(Int32 actorId)
を使用して対応するPhoton Player IDを検索しプレイヤー名をPhotonPlayer.Nicknameなどを通して表示します。
PlayerRef
はプレイヤーインデックスとは対照的に、1ベースです。理由としては、default(PlayerRef)
が便宜上「null/無効」のプレイヤーref structを返すからです。PlayerRefにintをキャストする自動キャストオペレータがあります。
- default(PlayerRef)、内部的に0、 誰もいないことを意味します。
- PlayerRef、内部的に1の場合はプレイヤーインデックス0と同じです。
- PlayerRef, 内部的に2の場合はプレイヤーインデックス1と同じです。
IL2CPPでビルドを行う場合、JSONからDBをデ直列化するときに例外を修正するにはどうしたらいいですか?
Json.Net
は直列化した配列をデ直列化する際に一時的なリストコレクションを作成し、未使用のジェネリクスがIL2CPPから削除され、例外が発生します。
ExecutionEngineException: Attempting to call method 'System.Collections.Generic.List`1[[Photon.Deterministic.DeterministicTickInputSet, PhotonDeterministic, Version=1.2.4.0, Culture=neutral, PublicKeyToken=null]]::.cctor' for which no ahead of time (AOT) code was generated.
(...)
at Newtonsoft.Json.Serialization.JsonArrayContract.CreateTemporaryCollection()
Unity独自のlink.xml
やPreserveAttribute
では修正できず、唯一の解決策は以下の方法でUnityスクリプト内に明示的にリスト化することです。
C#
using System.Collections.Generic;
public class QuantumAOT {
public List<Photon.Deterministic.DeterministicTickInputSet> DeterministicTickInputSet;
public List<Photon.Deterministic.FP> FP;
public List<Photon.Deterministic.FPVector2> FPVector2;
public List<Photon.Deterministic.FPVector3> FPVector3;
public List<Quantum.AssetLink> AssetLink;
public List<Quantum.BuffDataLink> BuffDataLink;
public List<Quantum.Core.CCWTri> CCWTri;
public List<Quantum.FPAnimationCurve.Keyframe> Keyframe;
public List<Quantum.MapStaticCollider> MapStaticCollider;
public List<Quantum.MapStaticCollider3D> MapStaticCollider3D;
public List<Quantum.NavMeshLink> NavMeshLink;
public List<System.Byte> Byte;
public List<System.Int32> Int32;
}
Quantum 2.0では、自動的にこのファイルを生成しますが、古いバージョンのQuantumでは手動で行う必要があります。配列内に保存されたDB(下記をご覧ください:ネスト化されたAssetObjectsとフィールド)内で使用された全ての値のタイプは上記のファイルにある必要があります。
C#
namespace Quantum {
public struct Foo1 {
public int Bar;
}
public class Foo2 {
public int Bar;
}
partial class SomeAssetData {
// Needs List<Foo1> to be known
public Foo1[] Foo1;
// Does not need special attention for Quantum apart from Unity having problems serializing nestes value and reference types under IL2CPP :)
public Foo2[] Foo2;
}
}
Navmeshアイランドがジオメトリ内に生成されるのはなぜですか?
これはいずれUnityにより修正されます (次のフォーラム投稿をご確認ください: forum.unity.com/threads/nav-generating-inside-non-walkable-objects)
NavmeshModifierVolumeを使用した回避策でこれを軽減できます ( NavMeshComponentsを必要とします)。
インポートステップのトライアングルカリングも今後提供できます。

Quantumでシミュレーションを開発する
なぜシミュレーションはフレーム60で開始するのですか?
これは、シミュレーションの開始時に割り当てられるロールバックが可能なフレームの数です。この数はDeterministicConfig->Rollbackウィンドウで設定される値と一致します。
なぜUpdate()は同じフレーム数で複数回呼び出されるのですか?
ロールバックが生じた場合、同じフレーム数でUpdate()が複数回システム上で呼ばれます。これはリモートプレイヤーの入力に対する予測が不適切だと検知され、シミュレーションがフレームを正しい入力データで再実行し予測ステートに戻す必要のある時に生じます。
現在、内部のアーキテクチャの判定により、ローカルモードでの実行中にも発生しています。Quantumの今後のバージョンで解決される予定です。
Visual Studioでのポインタのデバッギング方法を教えてください。表示されているのはアドレスのみです。
- Unity 2018とVisual Studio 2017が必要
- Unityプロジェクトで
Scripting Runtime Version
を.NET 4.x Equivalent
に設定します。 - Quantum dllを
Debug
dllとして構築します。 - UnityにVisual StudioをVSメニューでDebug->Attach Unity Debuggerとアタッチし、正しいUnity Editorインスタンスを選択します。
- Visual StudioとUnity Editorの全てのインスタンスを閉じ、VisualStudioを起動してQuantum dllを再構築してから、Unityを開始してリロードしデバッガをアタッチします。
なぜDynamicHitsやコンポーネントバッファをイテレートする場合、これらのアロケーションが発生するのですか?
Quantum DSL(コード生成)で作成する全てのQuantumコンポーネントに対し、Frame
クラス内のゲッタがあり、次のような見た目になっています。:public Buffer<CollisionsFilter> GetAllCollisions(Boolean excludeCulled = false)
Bufferオブジェクトはキャッシュ、再利用され、作成および拡張されるのは必要な場合のみです。リストが使用されなくなったら、Buffer.Disposed() を作動させるためのプーリングを呼び出す必要があります。これはusing
シンタックスを使って、自動的に行われます:
C#
using (var collisionComponents = f.GetAllCollisions()) {
for (Int32 x = 0; x < collisionComponents.Count; x++) {
// ...
}
}
コリジョンチェックで取得するDynamicHits
についても同じです :
C#
using (var hits = f.Scene.OverlapCircle(i->Data.ClickToMove.Target, 1)) {
foreach (var hit in hits) {
// ...
}
}
FP.MaxValueとFP.UseableMaxの違いは何でしょうか?
修正後のpoint mathは64ビット値のうち16+16ビットのみ使用しています。これにより一部の計算が速くなりました。オーバーフローを確認する必要がなくなったからです。つまり、FP.MinValue
とFP.MaxValue
は64ビット全てを使用しており、計算のためには 決して 使用しません。替わりにFP.UseableMax
とFP.UseableMin
を使用してください(例:最小FP値を持つ距離変数のインスタンス化など)。
※ FPは-32,768から32,768 (-2¹⁵から2¹⁵)の値を表すことができます。
なぜOnCollisionStaticコールバックを受信しないのでしょうか?
パフォーマンスの理由から有効化はオプトインです。 SimulationConfig
を確認して、Physics->Raise Collision Event For Staticsを切り替えてください。
どのようにしてQuantumはプレイヤーが受けとるIDを決めるのですか?
Puantumのプレイヤーインデックス(QuantumのプレイヤーID、PlayerRef)はSession.Joinがサーバーに届いた順序に基づいています。言い換えれば任意です。
PhotonIDは(Photonプレイヤー、アクターID)もシーケンスベースですが、基づいているのはPhotonのルームに参加した順序です。
Photonプレイヤーに「希望のQuantum Id」を設定することは現段階では不可能です。
クライアントが再接続すると、新しいPhotonIDが作成されるものの、同じClientIdで接続したときにそのクライアントに同じQuantumインデックスが与えられることが保証されます。public static QuantumRunner StartGame(String clientId, Int32 playerCount, StartParameters param)
となります。
プレイヤー関連の設定をQuantumGame.SendPlayerData(RuntimePlayer機能)で送信します。
シミュレーションにプレイヤーを追加するにはどうすればいいですか?
各フレームのメモリチャンク内に割り当ての必要なスペースを定義するため、プレイヤーの最大数は前もって把握しておくことが不可欠です。
qtn
ファイルの1つに次の行を追加もしくは設定して、希望のプレイヤー数を設定してください。
#define PLAYER_COUNT 8
#pragma max_players PLAYER_COUNT
define
は DSL内で使用する定義(例えば配列にプレイヤー数を割り当てるなど)として作動します。pragma
は実際にシミュレーションが処理できるプレイヤー数を定義します。
なぜ新しい構造体へのポインタはデータの陳腐化を指し示すのでしょうか?
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("Wtf");
barPt->Foo = true;
}
Console.ReadKey();
}
なぜシミュレーションが非同期化してしまうのでしょうか?
DeterministicConfig.ChecksumInterval
が0より大きい場合、検証されたフレームのチェックサムが算出され、サーバーに送信されて他のクライアントから送られたチェックサムと比較されます。
非同期化をもたらす値についてはDesync Check Code in our Community Wikiを参照してください。
よくある原因:
- Quantumデータアセットへの書き込み
C#
c->CharacterSpec = DB.FindAsset<CharacterSpec>("WhiteFacedBarghast");
c->CharacterSpec.RemainigLifetime = 21;
Quantumアセットには何も書き込まないでください。ここには読み取り専用データがあります。
UnityスレッドからQuantumへの書き込み
Unityにある全てのスクリプトには、Quantumフレームと接している全てのものに対して読み取り専用権限が適用されています。入力・コマンドによるシミュレーションのみが影響します。
データのキャッシュ
C#
public class CleaningSystem : SystemBase {
public Boolean hasShoweredToday; // <----- Error
public override void Update(Frame f) {
if (!hasShoweredToday && f.Global->ElapsedTime > 100) {
Shower();
hasShoweredToday = true;
}
}
}
上記のコードは、シミュレーションがロールバックしたときに非同期化を行います。フレーム上もしくはEntityコンポーネント上の一時的でないデータを保存する代わりです。
カスタムフレームUserDataをコピーしていない
C#
unsafe partial class Frame {
public Boolean HasShoweredToday;
partial void CopyFromUser(Frame frame) {
// <----- Error
}
}
Example of How to Use Frame.User.csを参照してください。
浮動小数点演算
シミュレーション内で浮動小数点は使用せず、FP
数値演算のみ使用するようにしてください。
また、FP.FromFloat_UNSAFE()
は注意してご使用ください。1つのマシンでアセット生成のバランスをとるために「オフライン」で使用する分には問題ありません。ただし、プラットフォームが異なると返される結果も異なることがあります。ランタイム中に浮動小数点をインポートする必要があり、かつ整数やFPが使用できないとき(バランシングデータのダウンロードなど)は ストリングからFPへ 変換してください。
クロスプラットフォームチェックサム
2つの異なるプラットフォーム(PCとiOSなど)でゲームを実行する場合、チェックサムが一致するのはPhotonDeterministic.ChecksumCrossplatformDeterminism
が有効になっているときのみです。
Data Created During Asset.Loaded()
Assets.Loaded() is called once per asset during loading and it's totally fine to store calculate and store new data inside the asset members at this time (note that if you are running the simulation on the server all games will share this one asset).
If your assets are loaded from Resources and you are either restarting Unity Editor or resetting the UnityDB during runtime be aware that Unity does not unload the Unity asset.
C#
public partial class FooAsset : AssetBase {
public Foo Settings;
public int RawData;
[NonSerialized]
public List<int> Bar = new List<int>();
public override AssetObject AssetObject => Settings;
public override void Loaded() {
// 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セッションで同じクライアントのPhotonRoomを再利用できますか?
A) できません。推奨される対策は、ルームを切り替えることです。
- Photonを直接使用して、新しいルームのIDをクライアントに共有します(ルームプロパティなど)。
- quantumセッションを停止します。LeaveRoom()を行いますが、切断はしません。
- 度維持にすべてのクライアントが入室する場合の競合状態を回避するため、JoinOrCreate()を使用して、クライアント上で新しいルームに接続します。
B) できます。ゲームの再起動を行います。
- ゲームのラウンドが終了していても、Quantumセッションを実行したままにしておきます。
- (任意) その間、決定論的にQuantumシステムを無効にしておくこともできます。
- ゲームラウンドの再起動を処理するゲームプレイシステム(ゲームステートマシーンなど)にコードを追加します。
警告(ソリューションB):サーバーでシミュレーションを実行(カスタムQuantumプラグインスナップショット再接続)していなかったり、バディースナップショット(他のクライアントから送信された有効なステート)で(再)接続を軽くしていないと、後から参加したクライアントがすべてのラウンドのインプットを受け取ることになります。
Collision.OnEnter、OnStay、OnExitコールバックがないのはなぜでしょう?
パフォーマンスを向上させるため、フレーム間のデータ量を削減して節約します。
これらの機能を有効にする小さなアドオンがあります。 ComplexCollisions スニペットについて、直接弊社にお問い合わせください。
Back to top