1.2.2の追加機能
EntityPrefabViewUpdater
エンティティプレハブ(EntityPrefabRoot)に追加情報を設定することができ、これでゲームオブジェクトがいつインスタンス化され、またいつ EntityPrefabViewUpdater によって破壊されるかを管理できます。オブジェクトの大半は、現在の フレーム内に現れたときにエンティティのプレハブインスタンスを作成します。プレハブを作成するオブジェクトの大半は、エンティティが 現在 のフレームに現れたときにエンティティを実体化します。ただし、サーバーが認識した場合のみエンティティが現れることもあります。以下の2つから選択してください。
EntityPrefabCreateBehaviour.NonVerified
EntityPrefabCreateBehaviour.Verified
これでエンティティプレハブの破壊がOnGameDestroyed()
コールバックの間に起こるようになりました。もうフレームのアップデートの間に遅れません。
QuantumGameリファクタリング
QuantumGameリファクタリングをおこなっていた理由:
- シングルトン(QuantumGame.Instance,および QuantumRunner.Current)を混乱させることなく複数のシミュレーションを可能にするため。
- QuantumGameコールバックを繰り返して使いやすくするため。
- 記録APIをより直感的で使いやすく、Unityに縛られずに使えるようにするため。
変更点:
- QuantumGame をUnityからquantum.systemプロジェクトに移動しました。
- QuantumRunner を全てのQuantumオブジェクト(ランナー、セッション、ゲーム)のハブにしました。ランナーにはゲームを管理するセッションがあり、それのアップデートもおこないます。同時に複数のゲームシミュレーションのサポートをするため、ランナーは
QuantumRunner.ActiveRunners
経由でアクセス可能です。ただし、QuantumRunner.Default
から最初に受け取る唯一のランナーを使用することがほとんどです。
C#
// old
QuantumRunner.Current
// new
QuantumRunner.Default
// old
QuantumGame.Instance
// new
QuantumRunner.Default.Game
// old
if (QuantumGame.Instance != null)
// new
if (QuantumRunner.Default != null)
*記録(replays、input、checksums、snapshots)ツールをQuantumGameクラスに移動しました。
C#
public class QuantumGame : IDeterministicGame {
public InputProvider RecordedInputs { get; }
public ChecksumFile RecordedChecksums { get; }
public static void ExportDatabase(Stream stream, IReplaySerializer serializer, string folderpath, int navmeshSerializationBuffer);
public static void ExportRecordedChecksums(QuantumGame game, Stream stream, IReplaySerializer serializer);
public static void ExportRecordedReplay(QuantumGame game, Stream stream, IReplaySerializer serializer);
public ReplayFile GetRecordedReplay();
public Frame GetRecordedSnapshot(int frame);
public void StartRecordingChecksums();
public void StartRecordingInput();
public void StartRecordingSnapshots(float bufferSizeSec, int snapshotFrequencyPerSec);
public void StartVerifyingChecksums(ChecksumFile checksums);
}
QuantumGameとDeterministicSessionがアプリケーションに転送する、対応する全てのコールバックを含むクラス、
QuantumGameCallbacks
が追加されました。これはゲームの開始時に作成されるか、もしくはQuantumRunner.StartParameters
のパラメータとして受け渡されます。- QuantumGame (input、map loading)で実行されていたUnity関連のコードはコールバックに直接アタッチされるようになりました。以下のファイルを確認してください。
QuantumGameCallback_LocalInput.cs
QuantumGameCallback_MapLoading.cs
QuantumGameCallback_UnityCallbacks.cs
QuantumGameCallback_DebugDraw.cs
- MonoBehaviourしばりのQuantumCallbacksは継続して使用可能で、ここには新しいシグネチャを追加したりQuantumGame引数でコールバックを切り替えることができます。
C#
public abstract class QuantumCallbacks : MonoBehaviour {
// Example old callback
public virtual void OnGameStart() { }
// New and additional callback with game argument
public virtual void OnGameStart(QuantumGame game) { }
// Renamed from OnSnapshotLoaded to OnGameStartFromSnapshot
public virtual void OnGameStartFromSnapshot(QuantumGame game, int frameNumber) { }
// Renamed from OnMapChangeBegin to OnUnitySceneLoadBegin
public virtual void OnUnitySceneLoadBegin(QuantumGame game) { }
// Renamed from OnMapChangeDone to OnUnitySceneLoadDone
public virtual void OnUnitySceneLoadDone(QuantumGame game) { }
// New
public virtual void OnGameDestroyed(QuantumGame game) { }
// New
public virtual void OnSimulateFinished(QuantumGame game, Frame frame) { }
}
- もしくは新しいコールバックに直接登録することも可能です。
C#
public override void OnGameStart(QuantumGame game) {
game.Callbacks.OnUpdateView += OnUpdateView;
}
ゲーム引数を伴うイベントコールバック
Quantum eventsイベントには OnRaisedFromGame という追加のOnRaised Actionがあり、これはIDeterministicGameパラメータを導入します。こうすることで受信側はどのゲームからこのイベントが生成されたのか把握できます。QuantumGame.Instanceの使用を除去します。また、インスタント再生によってスポーンされたイベントの情報が提供されます。
C#
public abstract class EventPlayerKilled : EventBase {
public static Action<EventPlayerKilled> OnRaised;
public static Action<EventPlayerKilled, IDeterministicGame> OnRaisedFromGame;
}
C#
void Start() {
Quantum.EventPlayerKilled.OnRaisedFromGame += PlayerKilledFromGame;
}
void PlayerKilledFromGame(EventPlayerKilled obj, IDeterministicGame game) {
Debug.Log("Game Mode: " + ((QuantumGame)game).GameMode);
}
物理
次の変更は2Dおよび3D動体に適用されます。


ソルバーパラメーターの表示(緑)
以下の数値を表示してジッターを低下させました。
Penetration Allowance
は、あるオブジェクトが別のオブジェクトにどれだけ沈み込むかということを参照しているため、完全に押し出されずにジッターが発生します。Penetration Correction
は、1つの物理ループで2つの物体の分離がどの程度強要されているかのパーセンテージを参照します。Min Linear Integration
は、線速度をハードクランプしジッターを低下させます。デフォルトの値は0で、物体を静止させるのに問題がある場合のみ使用します。
物体を落ち着かせる (黄色)
スリープ機能には以下の2つのメリットがあります。
- スリープ中はその物体を取り巻く環境をチェックしないので、その分パフォーマンスが軽くなります。
- 物体に残った少量のジッターの処理で済みます。
キャラクターやNPC以外に動的物理オブジェクトがある場合は、通常であればこの機能を有効にしますが、個別にPhysicsMaterials
を与えれば、オブジェクトごとに無効にすることもできます。( 下部のDefaultPhysicsMaterial
でAllowSleeping
フィールドを参照してください。)デフォルトのマテルあるでは。この機能にチェックは入っていません。
動的オブジェクトがたくさんあるときは、UseIslandsForSleepDetection
も有効にします。これにより、連結する全ての物体が更新され、ワンステップでスリープモードに切り替えられます。こうしておかないと、オブジェクトは常に作用して動かし合ってしまいます。
LinearSleepTolerance
は1つのオブジェクトが動く距離を参照しており、AngularSleepToleranceInRad
はオブジェクトの回転速度を参照しています。オブジェクトの速度がSleepTimeSec
のしきい値よりも下回る場合には、スリープ中としてマークされます。
動体をもつQuantumエンティティのgizmoで、この機能のチェックができます。物体がスリープになっているときは、gizmoは紫色になっています。
既知の問題
- スリープ中の物体が他の物体の上にある場合、もしもその物体が下方向に移動しはじめたり、例えば消滅したりしても、上にあった物体は目覚めません。これを解決するためには、多くのデータをフレームステートに保存する必要がありますが、この段階では行いません。この件については、各フレームの「プラットフォーム」のコリジョンコールバックの最中に受信する物体に
DynamicBody.WakeUp()
を呼び出すか(スリープタイマーもリセットされます。)、DynamicScene.WakeUpBodies()
を使用して、あるエリアの全ての物体を起こすことで対処できます。
マルチスレッディング (ピンク)
IsMultithreaded を有効にすると以下の作用があります。
- ナローフェーズの並列処理
- AllowSleeping および UseIslandsForSleepDetection が有効である場合、ソルバーの並列処理
動体の負荷に応じて、2.5xから10xのパフォーマンス増加が見られました。動的オブジェクトが非常に少ない場合は、マルチスレッディングを有効化するとオーバーヘッドを作り出す原因になります。また気を付けてほしいのは、2番目の最適化についてはパフォーマンス増加がみられるのはアイランド(触れる物体のグラフ)が作成される場合のみだということです。
インスタント再生もしくはキルカム
何秒間か巻き戻り、記録済みのシミュレーションが再生され、元のゲームに戻ります。
この機能はUnityプロジェクトのソースコードで利用できます。QuantumInstantReplay.cs
および QuantumInstantReplayDemo.cs
を検索してください。QuantumInstantReplayDemo.cs
は、Unityインスペクター内で確認できるスタートロジックとストップロジックだけを処理します。インスタント再生クラスの機能の確認をしてゲーム内で使用でしたり、カスタムロジックの設計図として使用することもできます。そうすると、同時シミュレーションを使用してインスタント再生をするために、何が、何の理由で実行されるのかわかります。
QuantumGameクラスは、検証されたフレームをリングバッファにコピーすることで通常のゲームのインターバル内で最後のn秒間にわたりスナップショットをとります。再生をトリガーすると、QuantumInstantReplayクラスが開始地点として一番いいスナップショットを見つけ、その後希望の巻き戻し時間になるまで動作をシミュレーションします。このスナップショットをもって、当面の間、まるで通常のセッションのように実行される新しいシミュレーションと新しいQuantumRunnerが開始されます。
実演中のシミュレーションは通常通り、自身のランナーによってティックされている点に留意してください。サーバーへデータの送信もおこなわれます。これはもちろん、他のプレイヤーもいますのでゲームを止めてしまうわけにはいかないからです。つまり、同時に2つのランナーと2つのゲームが存在するということになります。
QuantumGame.Instance および QuantumRunner.Current は、実行中のゲームをポイントし続けます。
既知の問題
- UseBackgroundThread を有効にした状態(Mapがデータを共有しています)でこの機能を使用するとバグが起こることがあり、これについては早期の解決を目指しています。
- 正しい順番でQuantumシステムを初期化してください。はじめにPhysicsSystemPre、後でNavMeshAgentSystemです(以下を参照してください。)将来的に取り除くシステム間でまだ依存関係があります。
C#
public static class SystemSetup {
public static SystemBase[] CreateSystems(RuntimeConfig gameConfig, SimulationConfig simulationConfig) {
return new SystemBase[] {
// pre-defined core systems
new Core.PhysicsSystemPre(),
new Core.NavMeshAgentSystem(),
...
機能のテスト
- 記録入力でゲームを開始するようにしてください (
RuntimeConfig.RecordingFlags
)。 - ゲームシーンにQuantumInstantReplayDemo スクリプトをアタッチします。ランタイム中でもおこなえます。
- ゲームを実行し、Unityインスペクター内でデバッグボタンをトリガーしでインスタント再生を開始したり停止したりします。

Buffer Size Sec
: 数秒間でどれだけの履歴が記録されているか。Snapshot Frequency Per Second
: 1秒間に何枚のスナップショットが実行されるか。ここを調整すると希望のフレームに到達するまでに「スキップ」しなければならないフレームの数を減らすことができます。Replay Length Sec
: 開始が押されるまでにどれほどの時間がかかるか。