PlayerLoopInjector
Static utility for injecting custom PlayerLoopSystem entries into Unity's Player Loop. Supports targeting any of the 8 top-level loop phases with prepend or append ordering. Automatically cleans up all injected loops on application quit.
Definition
Namespace: Paragon.Core.LowLevel Assembly: Paragon.dll
public static class PlayerLoopInjectorRemarks
Unity's Player Loop is the master update loop that drives every per-frame system — input polling, physics, animation, rendering, and more. It is organized into top-level phases (e.g., Update, FixedUpdate, PreLateUpdate), each containing an ordered list of subsystems.
PlayerLoopInjector provides a clean API to insert custom PlayerLoopSystem entries into any phase, enabling frame-precise update timing without MonoBehaviour overhead. This is essential for systems that need to run at specific points in the frame (e.g., before physics, after late update) or that need to avoid the overhead of MonoBehaviour.Update().
Lifecycle
Injection — Call
Inject()to add a custom loop to a target phaseTracking — The injected loop is stored in a static list
Cleanup — On
Application.quitting, all injected loops are automatically removed viaRemove()
Duplicate Prevention
Inject() checks if a PlayerLoopSystem with the same type already exists in the target phase. If found, it throws InvalidOperationException rather than silently duplicating.
Quick Lookup
Inject before a phase's systems
PlayerLoopInjector.Inject(loop, phase, InjectionOrder.PREPEND)
Inject after a phase's systems
PlayerLoopInjector.Inject(loop, phase, InjectionOrder.APPEND)
Target the Update phase
UnityPlayerLoopType.UPDATE
Target FixedUpdate
UnityPlayerLoopType.FIXED_UPDATE
Target PostLateUpdate
UnityPlayerLoopType.POST_LATE_UPDATE
Fields
logEnabled
bool
private static
Controls debug logging for injection operations
injectedLoops
List<PlayerLoopSystem>
private static readonly
Tracks all injected loops for cleanup on quit
Methods
Inject
Injects a custom PlayerLoopSystem into a target Player Loop phase.
injectedLoop
PlayerLoopSystem
The custom loop system to inject (must have a unique type)
targetLoopType
UnityPlayerLoopType
Which Player Loop phase to target
injectionOrder
InjectionOrder
Whether to prepend or append to the phase's subsystem list
Behavior:
Maps
UnityPlayerLoopTypeto the corresponding UnityPlayerLooptypeGets the current
PlayerLoopand finds the target phase by typeChecks for duplicates — throws
InvalidOperationExceptionif the loop type already existsPrepends or appends the loop to the target phase's
subSystemListSets the updated
PlayerLoopTracks the injected loop for cleanup
Throws:
InvalidOperationException— if a loop with the same type is already injected in the target phaseArgumentOutOfRangeException— ifinjectionOrderis not a valid enum value
Remove (private)
Removes a previously injected PlayerLoopSystem from the Player Loop.
injectedLoop
PlayerLoopSystem
The loop to remove
Behavior:
Searches all top-level phases for the injected loop by type
Filters it out of the phase's
subSystemListLogs an error if the loop is not found
OnApplicationQuit (private)
Removes all tracked injected loops and clears the tracking list. Subscribed to Application.quitting in the static constructor.
GetUnityLoopType (private)
Maps a UnityPlayerLoopType enum value to the corresponding Unity Type (e.g., typeof(UnityEngine.PlayerLoop.Update)).
Enums
UnityPlayerLoopType
Friendly enum mapping to Unity's 8 top-level Player Loop phases.
InjectionOrder
Controls placement within the target phase's subsystem list.
Common Pitfalls
Duplicate injection throws an exception Calling Inject() twice with the same PlayerLoopSystem.type targeting the same phase throws InvalidOperationException. Ensure each custom loop type is unique and injected only once.
PlayerLoopSystem must have a type set The PlayerLoopSystem.type field is used for duplicate detection and removal. If you create a PlayerLoopSystem without setting its type, injection may behave unpredictably. Always assign a unique type:
Automatic cleanup on quit only Injected loops are only removed when the application quits. If you need to remove a loop mid-session, there is no public Remove() API — the removal method is private. Design your injected systems to be persistent for the application lifetime.
Order within a phase matters PREPEND places your system before all existing subsystems in the phase, while APPEND places it after. Multiple PREPEND calls to the same phase will result in reverse insertion order (last prepended runs first).
Examples
Injecting a Custom Update System
Injecting Before Physics
Post-Late Update Processing
See Also
LowLevel Overview — system overview
IsExternalInit — compiler polyfill in the same module
RuntimeInitializeOnLoad — attribute for static constructor initialization at startup
Last updated