Skip to content

Instantly share code, notes, and snippets.

@ashuatz
Created November 28, 2023 01:38
Show Gist options
  • Save ashuatz/f2e84fbaf44db9aa5e3b41879cb9f320 to your computer and use it in GitHub Desktop.
Save ashuatz/f2e84fbaf44db9aa5e3b41879cb9f320 to your computer and use it in GitHub Desktop.
custom PlayerLoopManager
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using UnityEngine.LowLevel;
namespace Utils
{
public static class PlayerLoopManager
{
[RuntimeInitializeOnLoadMethod]
private static void RunTimeInitialize()
{
PlayerLoopSystem playerLoop = PlayerLoop.GetCurrentPlayerLoop();
for (int i = 0; i < playerLoop.subSystemList.Length; i++)
{
var subsystem = playerLoop.subSystemList[i];
var subSystemList = new List<PlayerLoopSystem>(subsystem.subSystemList);
var loopSystem = new PlayerLoopSystem
{
type = typeof(PlayerLoopManager),
updateDelegate = GetUpdateMethod(subsystem.type.Name)
};
subSystemList.Insert(0, loopSystem);
subsystem.subSystemList = subSystemList.ToArray();
playerLoop.subSystemList[i] = subsystem;
}
PlayerLoop.SetPlayerLoop(playerLoop);
}
private static PlayerLoopSystem.UpdateFunction GetUpdateMethod(string methodName)
{
MethodInfo methodInfo = typeof(PlayerLoopManager).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Static);
if (methodInfo == null)
{
Debug.LogWarning($"Method {methodName} not found.");
return null;
}
try
{
return (PlayerLoopSystem.UpdateFunction)Delegate.CreateDelegate(typeof(PlayerLoopSystem.UpdateFunction), methodInfo);
}
catch (ArgumentNullException ex)
{
Debug.LogError($"ArgumentNullException: {ex.Message}");
}
catch (ArgumentException ex)
{
Debug.LogError($"ArgumentException: The delegate type or method signature is not valid for {methodName}: {ex.Message}");
}
catch (MethodAccessException ex)
{
Debug.LogError($"MethodAccessException: The caller does not have access to method {methodName}: {ex.Message}");
}
catch (MissingMethodException ex)
{
Debug.LogError($"MissingMethodException: No method found named {methodName}: {ex.Message}");
}
catch (InvalidOperationException ex)
{
Debug.LogError($"InvalidOperationException: The method {methodName} cannot be bound to the delegate type: {ex.Message}");
}
return null;
}
public static event Action OnTimeUpdate;
public static event Action OnInitialization;
public static event Action OnEarlyUpdate;
public static event Action OnPreUpdate;
public static event Action OnPreLateUpdate;
public static event Action OnPostLateUpdate;
private static void TimeUpdate() => OnTimeUpdate?.Invoke();
private static void Initialization() => OnInitialization?.Invoke();
private static void EarlyUpdate() => OnEarlyUpdate?.Invoke();
private static void PreUpdate() => OnPreUpdate?.Invoke();
private static void PreLateUpdate() => OnPreLateUpdate?.Invoke();
private static void PostLateUpdate() => OnPostLateUpdate?.Invoke();
}
}
@ashuatz
Copy link
Author

ashuatz commented Nov 28, 2023

PlayerLoopManager

The PlayerLoopManager is a utility class designed to be used with Unity's PlayerLoopSystem. It allows you to inject custom update functions into Unity's player loop at runtime. This can be particularly useful for controlling the order of execution of certain pieces of your game logic.

Features

  • Inject custom update functions into Unity's player loop.
  • Easily manage update functions with Unity events.
  • Customize the player loop without altering the project settings or the manifest.

Requirements

  • Unity 2019.4 LTS or later.

Usage

To use PlayerLoopManager, you need to define methods that you want to insert into the player loop and subscribe to the corresponding events.

Example of subscribing to an event:

void OnEnable() {
    PlayerLoopManager.OnTimeUpdate += MyCustomUpdate;
}

void OnDisable() {
    PlayerLoopManager.OnTimeUpdate -= MyCustomUpdate;
}

void MyCustomUpdate() {
    // Your update logic here
}

To create a new update function in the player loop, follow these steps:

  1. Declare a method with the same name as the subsystem you want to hook into, for example, TimeUpdate.
  2. Make sure the method is static and has no parameters.
  3. Subscribe to the corresponding event of the PlayerLoopManager.
PlayerLoopManager.OnTimeUpdate += TimeUpdate;

Custom Update Functions

The following events are available to subscribe to:

  • OnTimeUpdate
  • OnInitialization
  • OnEarlyUpdate
  • OnPreUpdate
  • OnPreLateUpdate
  • OnPostLateUpdate

Each event corresponds to a particular phase of the Unity player loop.

License

The PlayerLoopManager is open-sourced software licensed under the MIT license.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment