ドキュメント

Unity Engine

Unity Engine

プレイヤーループのカスタマイズ

プレイヤーループをカスタマイズして、ループの各イテレーションで Unity がシステムを更新する順序を変更します。
読み終わるまでの所要時間 13 分最終更新 3日前

Unity ランタイムアプリケーションは、プレイヤーループと呼ばれる連続ループで実行されます。プレイヤーループの各イテレーション中に、Unity はレンダリング、物理演算シミュレーション、入力処理などのタスクを実行するためにさまざまなシステムを呼び出します。
プレイヤーループ中に更新されたさまざまなシステムとサブシステムは、定義されたデフォルトの順序で呼び出されます。この順序は、
PlayerLoop
API の静的メソッドを使用して表示およびカスタマイズできます。デフォルトまたは現在のプレイヤーループを取得して、独自のカスタムプレイヤーループシステムを挿入したり、既存のプレイヤーループシステムを削除、並べ替え、または置き換えたりすることができます。
プレイヤーループをカスタマイズする必要がある理由はいくつかあります。
  • カスタム更新マネージャーの一部として独自のマネージ更新を作成すると、Unity のネイティブイベント機能の代わりによりパフォーマンスの高い機能を使用できます。詳細については、Unity ブログの「10000 更新コール」を参照してください。
  • プレイヤー ループを特定のプラットフォームに合わせて最適化したい場合があります。例えば、モバイル ゲームの物理演算、AI、XR のアップデートを削除する場合などです。
  • 編集モードまたは再生モードで更新ループを視覚化して、デバッグのヘルプに使用できます。

プレイヤーループへのアクセス

プレイヤーループは、一連のネストされたプレイヤーループシステムで構成され、それぞれが
PlayerLoopSystem
構造体です。ルートまたは最も外側のプレイヤー ループ システムは、プレイヤー ループ全体を表し、その下に他のプレイヤー ループ システムがネスト状になっています。
この場合のプレイヤーループシステムとは、プレイヤーループ中に更新が必要なものを抽象化したにすぎません。デフォルトシステムは、オーディオや入力などのネイティブ Unity システムを表しますが、C# コードから独自のカスタムシステムを作成することもできます。
Unity のデフォルト プレイヤー ループにアクセスするには、
PlayerLoop.GetDefaultPlayerLoop
を
使用します。現在エフェクト中のプレイヤー ループにアクセスするには、
PlayerLoop.GetCurrentPlayerLoop
を
使用します。
PlayerLoop.SetPlayerLoop
で新しいカスタムプレイヤーループを適用しない限り、デフォルトと現在のプレイヤーループは同じになります。
プレイヤーループ内の各システムには、それを識別する
PlayerLoopSystem.type
プロパティがあります。プレイヤーループシステム構造体を再帰的に反復し、各
type
プロパティの
Name
を出力して、プレイヤーループ構造体をコンソールにディスプレイできます。以下の例に示すように、
type
プロパティを使用して、追加、削除、または置換するプレイヤーループサブシステムを特定することもできます。デフォルト システムに有効なタイプは、
UnityEngine.PlayerLoop
名前空間で使用できます。
API リファレンス
PlayerLoop.GetDefaultPlayerLoop
には、デフォルトのプレイヤーループを取得し、ネストされたプレイヤーループシステムを反復し、
type
プロパティの
Name
を出力する方法の例が含まれています。コードは以下のコンソール出力を生成します。
ルートノード TimeUpdate WaitForLastPresentationAndUpdateTime 初期化 ProfilerStartFrame UpdateCameraMotionVectors DirectorSampleTime AsyncUploadTimeSlicedUpdate SynchronizeInputs SynchronizeState XREarlyUpdate EarlyUpdate PollPlayerConnection GpuTimestamp AnalyticsCoreStatsUpdate UnityWebRequestUpdate ExecuteMainThreadJobs ProcessMouseInWindow ClearIntermediateRenderers ClearLines PresentBeforeUpdate ResetFrameStatsAfterPresent UpdateAsyncInstantiate UpdateAsyncReadbackManager UpdateStreamingManager UpdateTextureStreamingManager UpdatePreloading UpdateContentLoading RendererNotifyInvisible PlayerCleanupCachedData UpdateMainGameViewRect UpdateCanvasRectTransform XRUpdate UpdateInputManager ProcessRemoteInput ScriptRunDelayedStartupFrame UpdateKinect DeliverIosPlatformEvents ARCoreUpdate DispatchEventQueueEvents Physics2DEarlyUpdate PhysicsResetInterpolatedTransformPosition SpriteAtlasManagerUpdate PerformanceAnalyticsUpdate FixedUpdate ClearLines NewInputFixedUpdate DirectorFixedSampleTime AudioFixedUpdate ScriptRunBehaviourFixedUpdate DirectorFixedUpdate LegacyFixedAnimationUpdate XRFixedUpdate PhysicsFixedUpdate Physics2DFixedUpdate PhysicsClothFixedUpdate DirectorFixedUpdatePostPhysics ScriptRunDelayedFixedFrameRate PreUpdate PhysicsUpdate Physics2DUpdate PhysicsClothUpdate CheckTexFieldInput IMGUISendQueuedEvents NewInputUpdate InputForUIUpdate SendMouseEvents AIUpdate WindUpdate UpdateVideo Update ScriptRunBehaviourUpdate ScriptRunDelayedDynamicFrameRate ScriptRunDelayedTasks DirectorUpdate PreLateUpdate AIUpdatePostScript DirectorUpdateAnimationBegin LegacyAnimationUpdate DirectorUpdateAnimationEnd DirectorDeferredEvaluate AccessibilityUpdate UIElementsUpdatePanels EndGraphicsJobsAfterScriptUpdate ConstraintManagerUpdate ParticleSystemBeginUpdateAll Physics2DLateUpdate PhysicsLateUpdate ScriptRunBehaviourLateUpdate PostLateUpdate PlayerSendFrameStarted DirectorLateUpdate ScriptRunDelayedDynamicFrameRate PhysicsSkinnedClothBeginUpdate UpdateRectTransform PlayerUpdateCanvases UIElementsRepaintPanels UpdateAudio VFXUpdate ParticleSystemEndUpdateAll EndGraphicsJobsAfterScriptLateUpdate UpdateCustomRenderTextures XRPostLateUpdate UpdateAllRenderers UpdateLightProbeProxyVolumes EnlightenRuntimeUpdate UpdateAllSkinnedMeshes ProcessWebSendMessages SortingGroupsUpdate UpdateVideoTextures UpdateVideo DirectorRenderImage PlayerEmitCanvasGeometry UIElementsRenderBatchModeOffscreen PhysicsSkinnedClothFinishUpdate FinishFrameRendering BatchModeUpdate PlayerSendFrameComplete UpdateCaptureScreenshot PresentAfterDraw ClearImmediateRenderers PlayerSendFramePostPresent UpdateResolution InputEndFrame TriggerEndOfFrameCallbacks GUIClearEvents ShaderHandleErrors ResetInputAxis ThreadedLoadingDebug ProfilerSynchronizeStats MemoryFrameMaintenance ExecuteGameCenterCallbacks XRPreEndFrame ProfilerEndFrame GraphicsWarmupPreloadedShaders ObjectDispatcherPostLateUpdate

デフォルトプレイヤーループへのカスタム更新の挿入

プレイヤーループをカスタマイズする推奨かつリスクの低い方法は、カスタムプレイヤーループシステムをデフォルトのプレイヤーループに挿入することです。こうすることで、Unityのビルトイン システムを停止することなく、独自のカスタム更新ロジックを追加できます。
PlayerLoop.SetPlayerLoop
API リファレンスのコード例は、特定の時点でカスタムプレイヤーループシステムをデフォルトプレイヤーループに挿入する方法を示しています。その場合、
CustomUpdate
メソッドの動作は
InsertSystem
クラスで定義されます。ただし、以下の小さな変更によって、
CustomUpdate
を MonoBehaviour スクリプトがサブスクライブできるイベントにしたり、独自の更新ロジックを提供したりできます。
  • イベントを
    InsertSystem
    クラスの public 静的フィールドとして宣言します。
public static event Action AddCustomUpdate;
  • このイベントを呼び出すように
    CustomUpdate
    メソッドの定義を変更します。
private static void CustomUpdate() => AddCustomUpdate?.Invoke();
その後、任意の MonoBehaviour スクリプトから次のようにカスタム更新をサブスクライブできます。
using UnityEngine;public class CustomUpdateFromEvent :MonoBehaviour{ private void OnEnable() { InsertSystem.AddCustomUpdate += MyCustomUpdate; } private void Update() { Debug.Log("Update"); } private void LateUpdate() { Debug.Log("Late Update"); } private void OnDisable() { InsertSystem.AddCustomUpdate -= MyCustomUpdate; } private void MyCustomUpdate() { Debug.Log("Custom update from an event that your MonoBehaviour scripts can subscribe to."); }}
注
PlayerLoop.SetPlayerLoop
にパラメーターとして渡すプレイヤーループシステムは、現在のプレイヤーループを上書きします。したがって、このメソッドに渡すループには、置き換えないシステムを含め、保持するすべてのシステムが含まれていることを確認してください。新しいプレイヤーループシステムをゼロから作成する場合は、保持するすべてのシステムを新しいループに明示的に追加する必要があります。

プレイヤーループ内のデフォルトシステムを置き換える

以下の例では、プレイヤーループ内のデフォルトシステムをカスタム更新に置き換えます。この例では
PreUpdate.AIUpdate
システムを置き換えますが、PlayerLoopSystem.type 識別子を指定することでプレイヤーループ内のすべてのシステムを置き換えることができます。デフォルト システムの有効タイプは、前のセクションで示したデフォルト プレイヤー ループを使用するか、
UnityEngine.PlayerLoop
名前空間から取得できます。
重要
プレイヤーループでシステムを交換すると、そのシステムのデフォルト機能が削除されるため、意図しない結果になる可能性があります。このメソッドは、デフォルトの機能を独自のカスタムロジックに置き換えることが確実な場合にのみ、慎重に使用してください。
using UnityEngine.LowLevel;using UnityEngine.PlayerLoop;using UnityEngine;// Replace an existing system in the Unity Player Loop with a custom update.public class MyReplacementCustomUpdate { } // Empty class to use as a type identifier for the custom updatepublic class SystemReplacement{ //Run this method on runtime initialization [RuntimeInitializeOnLoadMethod] private static void AppStart() { // Retrieve the default Player loop system.デフォルトがすでに以前変更されている場合は、代わりに現在のループを取得します。 var defaultSystems = PlayerLoop.GetDefaultPlayerLoop(); // Create a custom update system to replace an existing one var customUpdate = new PlayerLoopSystem() { updateDelegate = CustomUpdate, type = typeof(MyReplacementCustomUpdate) }; // Specify the system to replace as the type parameter, in this case PreUpdate.AIUpdate ReplaceSystem<PreUpdate.AIUpdate>(ref defaultSystems, customUpdate); PlayerLoop.SetPlayerLoop(defaultSystems); } // Custom update method that will be called in the Player Loop private 静的 void CustomUpdate() { Debug.Log("Custom update from a replacement system."); } //Recursively replace a system of type T with a replacement system in the Player Loop private static bool ReplaceSystem<T>(ref PlayerLoopSystem system, PlayerLoopSystem replacement) { if (system.type == typeof(T)) { system = replacement; return true; } if (system.subSystemList != null) { for (var i = 0; i < system.subSystemList.Length; i++) { if (ReplaceSystem<T>(ref system.subSystemList[i], replacement)) { return true; } } } return false; }}

その他のリソースと例