Created
April 12, 2022 07:47
-
-
Save HassakuTb/d5e379dee84557957148024560abae12 to your computer and use it in GitHub Desktop.
assist to construct my own PlayerLoop
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using UnityEngine.LowLevel; | |
namespace HassakuLab.Utils.PlayerLoops | |
{ | |
/// <summary> | |
/// utility to insert PlayerLoop subsystem | |
/// </summary> | |
public static class PlayerLoopInserter | |
{ | |
public enum InsertMode | |
{ | |
BeforeTarget, | |
AfterTarget, | |
First, | |
Last, | |
} | |
/// <summary> | |
/// search for PlayerLoopSystem matching the specified | |
/// </summary> | |
/// <param name="found">output: matched subsystem</param> | |
/// <param name="playerLoop">subjects to search</param> | |
/// <param name="target">target type of searching PlayerLoopSystem</param> | |
/// <returns>true: found, false: not found</returns> | |
private static bool SearchForSubsystem(out PlayerLoopSystem found, PlayerLoopSystem playerLoop, Type target) | |
{ | |
if (playerLoop.type == target) | |
{ | |
found = playerLoop; | |
return true; | |
} | |
if (playerLoop.subSystemList != null) | |
{ | |
foreach (var system in playerLoop.subSystemList) | |
{ | |
if (SearchForSubsystem(out var tmp, system, target)) | |
{ | |
found = tmp; | |
return true; | |
} | |
} | |
} | |
found = new PlayerLoopSystem(); | |
return false; | |
} | |
/// <summary> | |
/// construct new PlayerLoopSystem using new delegate | |
/// </summary> | |
/// <param name="subsystemToInsert">type of subsystem to insert</param> | |
/// <param name="parent">which subsystem insert into</param> | |
/// <param name="insertTarget">where to insert (only work with InsertType.BeforeTarget or InsertType.AfterTarget)</param> | |
/// <param name="updateDelegate">function invoke every frame</param> | |
/// <param name="insertMode">type of insert</param> | |
/// <returns></returns> | |
/// <exception cref="ArgumentException"></exception> | |
/// <exception cref="ArgumentOutOfRangeException"></exception> | |
private static PlayerLoopSystem ConstructInserted( | |
Type subsystemToInsert, | |
PlayerLoopSystem parent, | |
Type insertTarget, | |
PlayerLoopSystem.UpdateFunction updateDelegate, | |
InsertMode insertMode | |
) | |
{ | |
PlayerLoopSystem insertItem = new PlayerLoopSystem | |
{ | |
type = subsystemToInsert, | |
subSystemList = Array.Empty<PlayerLoopSystem>(), | |
updateDelegate = updateDelegate, | |
}; | |
List<PlayerLoopSystem> brothers = parent.subSystemList != null | |
? parent.subSystemList.ToList() | |
: new List<PlayerLoopSystem>(); | |
switch (insertMode) | |
{ | |
case InsertMode.AfterTarget: | |
case InsertMode.BeforeTarget: | |
int targetIndex = brothers.FindIndex(target => target.type == insertTarget); | |
if (targetIndex == -1) throw new ArgumentException($"there is no type ({insertTarget.Name}) in ({parent.type.Name})"); | |
int insertIndex = insertMode switch | |
{ | |
InsertMode.AfterTarget => targetIndex + 1, | |
InsertMode.BeforeTarget => targetIndex, | |
// ReSharper disable once UnreachableSwitchArmDueToIntegerAnalysis | |
_ => 0, | |
}; | |
brothers.Insert(insertIndex, insertItem); | |
break; | |
case InsertMode.First: | |
brothers.Insert(0, insertItem); | |
break; | |
case InsertMode.Last: | |
brothers.Add(insertItem); | |
break; | |
default: | |
throw new ArgumentOutOfRangeException(nameof(insertMode), insertMode, null); | |
} | |
PlayerLoopSystem newSystem = parent; | |
newSystem.subSystemList = brothers.ToArray(); | |
return newSystem; | |
} | |
/// <summary> | |
/// replace subsystem with existing | |
/// </summary> | |
/// <param name="playerLoop">reference to target PlayerLoopSystem</param> | |
/// <param name="replaceTo">item replace to</param> | |
/// <returns>true: finish with replace, false: finish without replace</returns> | |
private static bool ReplaceSubsystem(ref PlayerLoopSystem playerLoop, PlayerLoopSystem replaceTo) | |
{ | |
if (playerLoop.type == replaceTo.type) | |
{ | |
playerLoop = replaceTo; | |
return true; | |
} | |
if (playerLoop.subSystemList != null) | |
{ | |
for (int i = 0; i < playerLoop.subSystemList.Length; ++i) | |
{ | |
if (ReplaceSubsystem(ref playerLoop.subSystemList[i], replaceTo)) | |
{ | |
return true; | |
} | |
} | |
} | |
return false; | |
} | |
/// <summary> | |
/// insert subsystem to PlayerLoop | |
/// </summary> | |
/// <param name="subsystemToInsert">type of subsystem to insert</param> | |
/// <param name="parent">which subsystem insert into</param> | |
/// <param name="insertTarget">where to insert (only work with InsertType.BeforeTarget or InsertType.AfterTarget)</param> | |
/// <param name="updateDelegate">delegate that will invoke every frame</param> | |
/// <param name="insertMode">type of insert</param> | |
/// <exception cref="ArgumentException"></exception> | |
public static void InsertSubsystem( | |
Type subsystemToInsert, | |
Type parent, | |
Type insertTarget, | |
PlayerLoopSystem.UpdateFunction updateDelegate, | |
InsertMode insertMode | |
) | |
{ | |
PlayerLoopSystem playerLoop = PlayerLoop.GetCurrentPlayerLoop(); | |
// search for parent subsystem | |
if (!SearchForSubsystem(out var parentSubsystem, playerLoop, parent)) | |
{ | |
throw new ArgumentException($"there is no type ({parent.Name}) in PlayerLoop"); | |
} | |
// construct subsystem to replace | |
PlayerLoopSystem newSystem = ConstructInserted(subsystemToInsert, parentSubsystem, insertTarget, updateDelegate, insertMode); | |
// replace subsystem | |
if (!ReplaceSubsystem(ref playerLoop, newSystem)) | |
{ | |
throw new ArgumentException($"failed to replace {parent.Name} to {newSystem.type.Name}"); | |
} | |
PlayerLoop.SetPlayerLoop(playerLoop); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using UnityEngine.PlayerLoop; | |
namespace HassaluLab.Utils.PlayerLoops.Samples{ | |
public class SampleUsage : MonoBehaviour | |
{ | |
private struct MyUpdate {} | |
private static void MyUpdateFunction() | |
{ | |
// do something | |
} | |
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] | |
private static void RegisterSubsystem() | |
{ | |
PlayerLoopInserter.InsertSubsystem( | |
subsystemToInsert: typeof(MyUpdate), | |
parent: typeof(Update), | |
insertTarget: typeof(Update.ScriptRunBehaviourUpdate), | |
updateDelegate: MyUpdateFunction, | |
insertMode: PlayerLoopInserter.InsertMode.BeforeTarget | |
); | |
} | |
#endregion | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment