From 87dbf593bc441c6a7a671f8c34acdf6c15f2b519 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 18 Oct 2025 23:04:49 +0000 Subject: [PATCH 1/4] Initial plan From 4eaa6b08cf15c175a239e503ab2831e0f9bc3558 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 18 Oct 2025 23:13:11 +0000 Subject: [PATCH 2/4] Optimize RandomUtility: remove LINQ allocations and improve algorithms Co-authored-by: D3TONAT0R <20219966+D3TONAT0R@users.noreply.github.com> --- Runtime/RandomUtility.cs | 106 ++++++++++++++++++++++++++++++++------- 1 file changed, 87 insertions(+), 19 deletions(-) diff --git a/Runtime/RandomUtility.cs b/Runtime/RandomUtility.cs index 18216d2..6b2e439 100644 --- a/Runtime/RandomUtility.cs +++ b/Runtime/RandomUtility.cs @@ -72,15 +72,37 @@ public static Vector4 RandomPoint4D(float min, float max) public static int RangeExcluding(int min, int max, params int[] exclude) { if(max - min <= 0) return -1; - var array = Enumerable.Range(min, max - min).Except(exclude).ToArray(); - if(array.Length > 0) + + // Build valid range without LINQ to avoid allocations + int rangeSize = max - min; + int validCount = rangeSize; + + // Count how many values are excluded + for(int i = 0; i < exclude.Length; i++) { - return Random.Range(0, array.Length); + if(exclude[i] >= min && exclude[i] < max) + { + validCount--; + } } - else + + if(validCount <= 0) return -1; + + // Pick a random index in the valid range + int pick = Random.Range(0, validCount); + int current = min; + + // Skip excluded values + for(int offset = 0; offset <= pick; offset++) { - return -1; + while(System.Array.IndexOf(exclude, current) >= 0) + { + current++; + } + if(offset < pick) current++; } + + return current; } /// @@ -89,6 +111,10 @@ public static int RangeExcluding(int min, int max, params int[] exclude) /// A random item from the array. public static T PickRandom(params T[] array) { + if(array == null || array.Length == 0) + { + throw new System.ArgumentException("Array cannot be null or empty"); + } return array[Random.Range(0, array.Length)]; } @@ -98,6 +124,10 @@ public static T PickRandom(params T[] array) /// A random item from the list. public static T PickRandom(List list) { + if(list == null || list.Count == 0) + { + throw new System.ArgumentException("List cannot be null or empty"); + } return list[Random.Range(0, list.Count)]; } @@ -129,15 +159,34 @@ public static T PickRandomExcluding(List list, params int[] excludeIndices /// A random item from the array. public static int PickRandomIndexExcluding(int length, params int[] excludeIndices) { - var indices = Enumerable.Range(0, length).Except(excludeIndices).ToArray(); - if(indices.Length > 0) + // Calculate valid count without LINQ to avoid allocations + int validCount = length; + + for(int i = 0; i < excludeIndices.Length; i++) { - return indices[Random.Range(0, indices.Length)]; + if(excludeIndices[i] >= 0 && excludeIndices[i] < length) + { + validCount--; + } } - else + + if(validCount <= 0) return -1; + + // Pick a random index in the valid range + int pick = Random.Range(0, validCount); + int current = 0; + + // Skip excluded indices + for(int offset = 0; offset <= pick; offset++) { - return -1; + while(System.Array.IndexOf(excludeIndices, current) >= 0) + { + current++; + } + if(offset < pick) current++; } + + return current; } /// @@ -145,6 +194,10 @@ public static int PickRandomIndexExcluding(int length, params int[] excludeIndic /// public static T TakeRandomItem(List list) { + if(list == null || list.Count == 0) + { + throw new System.ArgumentException("List cannot be null or empty"); + } int i = Random.Range(0, list.Count); var item = list[i]; list.RemoveAt(i); @@ -158,8 +211,16 @@ public static T TakeRandomItem(List list) /// The picked item's index. public static int PickRandomWeighted(float[] weights) { + if(weights == null || weights.Length == 0) + { + throw new System.ArgumentException("Weights array cannot be null or empty"); + } float total = 0; foreach(var w in weights) total += w; + if(total <= 0) + { + throw new System.ArgumentException("Total weight must be greater than zero"); + } float pick = Random.value * total; for(int i = 0; i < weights.Length; i++) { @@ -173,29 +234,36 @@ public static int PickRandomWeighted(float[] weights) } /// - /// Returns a shuffled version of the given array. + /// Returns a shuffled version of the given array using the Fisher-Yates algorithm. /// public static T[] Shuffle(T[] array) { T[] shuffled = new T[array.Length]; - var list = array.ToList(); - for(int i = 0; i < array.Length; i++) + System.Array.Copy(array, shuffled, array.Length); + + // Fisher-Yates shuffle algorithm + for(int i = shuffled.Length - 1; i > 0; i--) { - shuffled[i] = TakeRandomItem(list); + int j = Random.Range(0, i + 1); + T temp = shuffled[i]; + shuffled[i] = shuffled[j]; + shuffled[j] = temp; } return shuffled; } /// - /// Randomly shuffles the given list. + /// Randomly shuffles the given list using the Fisher-Yates algorithm. /// public static void Shuffle(List list) { - var elements = new List(list); - list.Clear(); - for(int i = 0; i < elements.Count; i++) + // Fisher-Yates shuffle algorithm - in-place + for(int i = list.Count - 1; i > 0; i--) { - list.Add(TakeRandomItem(elements)); + int j = Random.Range(0, i + 1); + T temp = list[i]; + list[i] = list[j]; + list[j] = temp; } } } From 14e07e2eb9bd3a99ea3751199d1723ebf4a74ddf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 18 Oct 2025 23:15:53 +0000 Subject: [PATCH 3/4] Add readonly modifiers for better performance and immutability Co-authored-by: D3TONAT0R <20219966+D3TONAT0R@users.noreply.github.com> --- Runtime/ExtraGizmos.cs | 2 +- Runtime/InstancePool.cs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Runtime/ExtraGizmos.cs b/Runtime/ExtraGizmos.cs index 8baae9e..c78d1b4 100644 --- a/Runtime/ExtraGizmos.cs +++ b/Runtime/ExtraGizmos.cs @@ -34,7 +34,7 @@ public class ExtraGizmos internal static GUIStyle labelStyle; internal static GUIStyle boxStyle; - private static List circlePointCache = new List(); + private static readonly List circlePointCache = new List(128); static ExtraGizmos() { diff --git a/Runtime/InstancePool.cs b/Runtime/InstancePool.cs index 2d66d33..31b9b8b 100644 --- a/Runtime/InstancePool.cs +++ b/Runtime/InstancePool.cs @@ -57,10 +57,10 @@ public class InstancePool where T : Component /// public int TotalInstanceCount => InactiveInstanceCount + ActiveInstanceCount; - private List inactivePool; - private List activePool; - private Dictionary lastActivationTimes; - private T[] iterationCache; + private readonly List inactivePool; + private readonly List activePool; + private readonly Dictionary lastActivationTimes; + private readonly T[] iterationCache; private float lastUpdateTime; /// From defa7afe0101aab30141f22846ec4348184cdd46 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 18 Oct 2025 23:18:22 +0000 Subject: [PATCH 4/4] Add readonly modifiers to UpdateLoop InvocationTarget fields Co-authored-by: D3TONAT0R <20219966+D3TONAT0R@users.noreply.github.com> --- Runtime/UpdateLoop/UpdateLoop.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Runtime/UpdateLoop/UpdateLoop.cs b/Runtime/UpdateLoop/UpdateLoop.cs index c03066f..1d76bff 100644 --- a/Runtime/UpdateLoop/UpdateLoop.cs +++ b/Runtime/UpdateLoop/UpdateLoop.cs @@ -33,8 +33,8 @@ internal class InvocationList { public class InvocationTarget { - public Action action; - public Behaviour targetComponent; + public readonly Action action; + public readonly Behaviour targetComponent; public readonly bool isComponentTarget; @@ -55,7 +55,7 @@ public bool IsActiveAndEnabled() } public readonly string name; - public List subscribers = new List(256); + public readonly List subscribers = new List(256); public InvocationList(string name) {