├── .github └── FUNDING.yml ├── .gitignore ├── LICENSE ├── LICENSE.meta ├── README.md ├── README.md.meta ├── Runtime.meta ├── Runtime ├── Extensions.meta ├── Extensions │ ├── ListExtensions.cs │ ├── ListExtensions.cs.meta │ ├── MathExtensions.cs │ ├── MathExtensions.cs.meta │ ├── ObjectExtensions.cs │ └── ObjectExtensions.cs.meta ├── Misc.meta ├── Misc │ ├── ObjectPool.cs │ ├── ObjectPool.cs.meta │ ├── Rand.cs │ ├── Rand.cs.meta │ ├── Ref.cs │ ├── Ref.cs.meta │ ├── SemVer.cs │ └── SemVer.cs.meta ├── Singleton.meta ├── Singleton │ ├── AutoMonoSingleton.cs │ ├── AutoMonoSingleton.cs.meta │ ├── MonoSingleton.cs │ ├── MonoSingleton.cs.meta │ ├── Singleton.Create.cs │ ├── Singleton.Create.cs.meta │ ├── Singleton.New.cs │ └── Singleton.New.cs.meta ├── UnityCommons.Runtime.asmdef ├── UnityCommons.Runtime.asmdef.meta ├── UpdateManager.meta ├── UpdateManager │ ├── AsyncUpdateEvent.cs │ ├── AsyncUpdateEvent.cs.meta │ ├── ParallelAsyncUpdateManager.cs │ ├── ParallelAsyncUpdateManager.cs.meta │ ├── SequentialAsyncUpdateManager.cs │ ├── SequentialAsyncUpdateManager.cs.meta │ ├── UpdateManager.cs │ └── UpdateManager.cs.meta ├── Utilities.meta └── Utilities │ ├── GridXY.cs │ ├── GridXY.cs.meta │ ├── GridXZ.cs │ ├── GridXZ.cs.meta │ ├── ListUtilities.cs │ ├── ListUtilities.cs.meta │ ├── RangeUtils.cs │ ├── RangeUtils.cs.meta │ ├── Run.cs │ ├── Run.cs.meta │ ├── UnityUtils.cs │ └── UnityUtils.cs.meta ├── package.json └── package.json.meta /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: teodorvecerdi -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/unity,rider 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=unity,rider 4 | 5 | ### Rider ### 6 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 7 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 8 | 9 | # User-specific stuff 10 | .idea/**/workspace.xml 11 | .idea/**/tasks.xml 12 | .idea/**/usage.statistics.xml 13 | .idea/**/dictionaries 14 | .idea/**/shelf 15 | 16 | # Generated files 17 | .idea/**/contentModel.xml 18 | 19 | # Sensitive or high-churn files 20 | .idea/**/dataSources/ 21 | .idea/**/dataSources.ids 22 | .idea/**/dataSources.local.xml 23 | .idea/**/sqlDataSources.xml 24 | .idea/**/dynamic.xml 25 | .idea/**/uiDesigner.xml 26 | .idea/**/dbnavigator.xml 27 | 28 | # Gradle 29 | .idea/**/gradle.xml 30 | .idea/**/libraries 31 | 32 | # Gradle and Maven with auto-import 33 | # When using Gradle or Maven with auto-import, you should exclude module files, 34 | # since they will be recreated, and may cause churn. Uncomment if using 35 | # auto-import. 36 | # .idea/artifacts 37 | # .idea/compiler.xml 38 | # .idea/jarRepositories.xml 39 | # .idea/modules.xml 40 | # .idea/*.iml 41 | # .idea/modules 42 | # *.iml 43 | # *.ipr 44 | 45 | # CMake 46 | cmake-build-*/ 47 | 48 | # Mongo Explorer plugin 49 | .idea/**/mongoSettings.xml 50 | 51 | # File-based project format 52 | *.iws 53 | 54 | # IntelliJ 55 | out/ 56 | 57 | # mpeltonen/sbt-idea plugin 58 | .idea_modules/ 59 | 60 | # JIRA plugin 61 | atlassian-ide-plugin.xml 62 | 63 | # Cursive Clojure plugin 64 | .idea/replstate.xml 65 | 66 | # Crashlytics plugin (for Android Studio and IntelliJ) 67 | com_crashlytics_export_strings.xml 68 | crashlytics.properties 69 | crashlytics-build.properties 70 | fabric.properties 71 | 72 | # Editor-based Rest Client 73 | .idea/httpRequests 74 | 75 | # Android studio 3.1+ serialized cache file 76 | .idea/caches/build_file_checksums.ser 77 | 78 | ### Unity ### 79 | # This .gitignore file should be placed at the root of your Unity project directory 80 | # 81 | # Get latest from https://github.com/github/gitignore/blob/master/Unity.gitignore 82 | /[Ll]ibrary/ 83 | /[Tt]emp/ 84 | /[Oo]bj/ 85 | /[Bb]uild/ 86 | /[Bb]uilds/ 87 | /[Ll]ogs/ 88 | /[Uu]ser[Ss]ettings/ 89 | 90 | # MemoryCaptures can get excessive in size. 91 | # They also could contain extremely sensitive data 92 | /[Mm]emoryCaptures/ 93 | 94 | # Asset meta data should only be ignored when the corresponding asset is also ignored 95 | !/[Aa]ssets/**/*.meta 96 | 97 | # Uncomment this line if you wish to ignore the asset store tools plugin 98 | # /[Aa]ssets/AssetStoreTools* 99 | 100 | # Autogenerated Jetbrains Rider plugin 101 | /[Aa]ssets/Plugins/Editor/JetBrains* 102 | 103 | # Visual Studio cache directory 104 | .vs/ 105 | 106 | # Gradle cache directory 107 | .gradle/ 108 | 109 | # Autogenerated VS/MD/Consulo solution and project files 110 | ExportedObj/ 111 | .consulo/ 112 | *.csproj 113 | *.unityproj 114 | *.sln 115 | *.suo 116 | *.tmp 117 | *.user 118 | *.userprefs 119 | *.pidb 120 | *.booproj 121 | *.svd 122 | *.pdb 123 | *.mdb 124 | *.opendb 125 | *.VC.db 126 | 127 | # Unity3D generated meta files 128 | *.pidb.meta 129 | *.pdb.meta 130 | *.mdb.meta 131 | 132 | # Unity3D generated file on crash reports 133 | sysinfo.txt 134 | 135 | # Builds 136 | *.apk 137 | *.unitypackage 138 | 139 | # Crashlytics generated file 140 | 141 | # Autogenerated files 142 | InitTestScene*.unity.meta 143 | InitTestScene*.unity 144 | 145 | 146 | # End of https://www.toptal.com/developers/gitignore/api/unity,rider 147 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Copyright 2022 Teodor Vecerdi 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /LICENSE.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3a99fe99824854142870c2067838487d 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unity Commons 2 | 3 | [![GitHub Repo stars](https://img.shields.io/github/stars/TeodorVecerdi/UnityCommons?label=Star%20on%20GitHub&style=social)](https://github.com/TeodorVecerdi/UnityCommons/stargazers) [![GitHub tag (latest SemVer pre-release)](https://img.shields.io/github/v/tag/TeodorVecerdi/UnityCommons?include_prereleases&label=Latest)](https://github.com/TeodorVecerdi/UnityCommons/releases) 4 | 5 | Library of reusable Unity-related scripts. 6 | 7 | # Installing Unity Commons 8 | 9 | ## 1. as a git submodule (recommended) 10 | 11 | Run the following on the command line in your unity project's folder: 12 | 13 | ```sh 14 | git submodule add https://github.com/TeodorVecerdi/UnityCommons.git Assets/Plugins/UnityCommons 15 | ``` 16 | 17 | ## 2. using OpenUPM 18 | 19 | **Requires [OpenUPM-CLI][openupm-cli]** 20 | Run the following on the command line in your unity project's folder: 21 | 22 | ```sh 23 | openupm add dev.vecerdi.unitycommons 24 | ``` 25 | 26 | [openupm-cli]: https://openupm.com/docs/getting-started.html#installing-openupm-cli 27 | -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f4b7c6cbd793b6a4dbcf696b107b79a7 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 597f7c05e3a182c4c9a33d8cca9293fb 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Extensions.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0285a2fbf46d4c07834d67a98d602965 3 | timeCreated: 1608653944 -------------------------------------------------------------------------------- /Runtime/Extensions/ListExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using UnityEngine; 5 | 6 | namespace UnityCommons { 7 | public static partial class Extensions { 8 | #region Misc 9 | 10 | /// 11 | /// Returns the number of items in , or 0 if it is null 12 | /// 13 | public static int CountAllowNull(this IList list) { 14 | if (list != null) 15 | return list.Count; 16 | return 0; 17 | } 18 | 19 | /// 20 | /// Returns true if is empty or null, and false otherwise 21 | /// 22 | public static bool NullOrEmpty(this IList list) { 23 | if (list != null) 24 | return list.Count == 0; 25 | return true; 26 | } 27 | 28 | /// 29 | /// Shuffles 30 | /// 31 | public static void Shuffle(this IList list) { 32 | int count = list.Count; 33 | while (count > 1) { 34 | --count; 35 | int index = Rand.RangeInclusive(0, count); 36 | T obj = list[index]; 37 | list[index] = list[count]; 38 | list[count] = obj; 39 | } 40 | } 41 | 42 | /// 43 | /// Runs on each element of 44 | /// 45 | public static void ForEach(this IEnumerable enumerable, Action action) { 46 | if (action == null) throw new ArgumentNullException(nameof(action)); 47 | if (enumerable == null) throw new ArgumentNullException(nameof(enumerable)); 48 | 49 | using (IEnumerator enumerator = enumerable.GetEnumerator()) { 50 | while (enumerator.MoveNext()) { 51 | T current = enumerator.Current; 52 | if (current == null) return; 53 | 54 | action(current); 55 | } 56 | } 57 | } 58 | 59 | /// 60 | /// Joins to and pipes it to an IEnumerable 61 | /// 62 | public static IEnumerable AppendMany(this IEnumerable source, IEnumerable enumerable) { 63 | if (source == null) throw new ArgumentNullException(nameof(source)); 64 | if (enumerable == null) throw new ArgumentNullException(nameof(enumerable)); 65 | 66 | foreach (T element in source) yield return element; 67 | foreach (T element in enumerable) yield return element; 68 | } 69 | 70 | #endregion 71 | 72 | #region Copy 73 | 74 | /// 75 | /// Returns a deep copy of 76 | /// 77 | public static IList DeepCopyStruct(this IEnumerable source) where T : struct { 78 | List list = new List(); 79 | foreach (T item in source) { 80 | T copy = item; 81 | list.Add(copy); 82 | } 83 | 84 | return list; 85 | } 86 | 87 | /// 88 | /// Returns a deep copy of 89 | /// 90 | public static IList DeepCopyStructOrNull(this IEnumerable source) where T : struct { 91 | return source?.DeepCopyStruct(); 92 | } 93 | 94 | /// 95 | /// Returns a deep copy of 96 | /// 97 | public static IList DeepCopyCloneable(this IEnumerable source) where T : ICloneable { 98 | return source.Select(item => (T) item.Clone()).ToList(); 99 | } 100 | 101 | /// 102 | /// Returns a deep copy of 103 | /// 104 | public static IList DeepCopyCloneableOrNull(this IEnumerable source) where T : ICloneable { 105 | return source?.DeepCopyCloneable(); 106 | } 107 | 108 | /// 109 | /// Returns a deep copy of 110 | /// 111 | public static IList ShallowCopy(this IEnumerable source) { 112 | return new List(source); 113 | } 114 | 115 | /// 116 | /// Returns a deep copy of 117 | /// 118 | public static IList ShallowCopyOrNull(this IEnumerable source) { 119 | return source?.ShallowCopy(); 120 | } 121 | 122 | #endregion 123 | 124 | #region Duplicates 125 | 126 | /// 127 | /// Removes duplicates from 128 | /// 129 | public static void RemoveDuplicates(this List list) where T : IComparable { 130 | RemoveDuplicates(list, (comparable, comparable1) => comparable.CompareTo(comparable1)); 131 | } 132 | 133 | /// 134 | /// Removes duplicates from 135 | /// 136 | public static void RemoveDuplicates(this List list, Comparison comparison) { 137 | if (list.Count <= 1) 138 | return; 139 | for (int index1 = list.Count - 1; index1 >= 0; --index1) 140 | for (int index2 = 0; index2 < index1; ++index2) 141 | if (comparison(list[index1], list[index2]) == 0) { 142 | list.RemoveAt(index1); 143 | break; 144 | } 145 | } 146 | 147 | /// 148 | /// Removes duplicates from 149 | /// 150 | public static void RemoveDuplicates(this IList list) where T : IEquatable { 151 | if (list.Count <= 1) return; 152 | list = list.Distinct().ToList(); 153 | } 154 | 155 | #endregion 156 | 157 | #region Printing 158 | 159 | /// 160 | /// Prints each element of with the format of "[a, b, c, d, ...]" 161 | /// 162 | public static void Print(this IEnumerable list, string separator = ", ", string start = "[", string end = "]") { 163 | Debug.Log(list.ToListString()); 164 | } 165 | 166 | /// 167 | /// Joins each element of to a string with the format of "[a, b, c, d, ...]" 168 | /// 169 | public static string ToListString(this IEnumerable list, string separator = ", ", string start = "[", string end = "]") { 170 | return start + string.Join(separator, list.Select(item => item.ToString())) + end; 171 | } 172 | 173 | #endregion 174 | 175 | #region Sorting 176 | 177 | /// 178 | /// Returns true if is sorted, false otherwise 179 | /// 180 | public static bool IsSorted(this IList list, Comparison comparison) { 181 | for (int i = 0; i < list.Count - 1; i++) { 182 | if (comparison(list[i], list[i + 1]) > 0) return false; 183 | } 184 | 185 | return true; 186 | } 187 | 188 | /// 189 | /// Returns true if is sorted, false otherwise 190 | /// 191 | public static bool IsSorted(this IList list) where T : IComparable { 192 | return IsSorted(list, (comparable, comparable1) => comparable.CompareTo(comparable1)); 193 | } 194 | 195 | /// 196 | /// Returns sorted using a quicksort algorithm 197 | /// 198 | public static List QuickSorted(this List list, Comparison comparison) { 199 | if (list == null) throw new ArgumentNullException(nameof(list)); 200 | if (comparison == null) throw new ArgumentNullException(nameof(comparison)); 201 | 202 | QuickSort_Impl(list, 0, list.Count - 1, comparison); 203 | return list; 204 | } 205 | 206 | /// 207 | /// Returns sorted using a quicksort algorithm 208 | /// 209 | public static List QuickSorted(this List list) where T : IComparable { 210 | return QuickSorted(list, (comparable, comparable1) => comparable.CompareTo(comparable1)); 211 | } 212 | 213 | /// 214 | /// Returns sorted using an insertion sorting algorithm 215 | /// 216 | public static List InsertionSorted(this List list, Comparison comparison) { 217 | if (list == null) throw new ArgumentNullException(nameof(list)); 218 | if (comparison == null) throw new ArgumentNullException(nameof(comparison)); 219 | 220 | InsertionSort(list, comparison); 221 | return list; 222 | } 223 | 224 | /// 225 | /// Returns sorted using an insertion sorting algorithm 226 | /// 227 | public static List InsertionSorted(this List list) where T : IComparable { 228 | return InsertionSorted(list, (comparable, comparable1) => comparable.CompareTo(comparable1)); 229 | } 230 | 231 | /// 232 | /// Sorts using a quicksort algorithm 233 | /// 234 | public static void QuickSort(this IList list, Comparison comparison) { 235 | QuickSort_Impl(list, 0, list.Count - 1, comparison); 236 | } 237 | 238 | /// 239 | /// Sorts using a quicksort algorithm 240 | /// 241 | public static void QuickSort(this IList list) where T : IComparable { 242 | QuickSort_Impl(list, 0, list.Count - 1, (comparable, comparable1) => comparable.CompareTo(comparable1)); 243 | } 244 | 245 | /// 246 | /// Sorts using an insertion sorting algorithm 247 | /// 248 | public static void InsertionSort(this IList list, Comparison comparison) { 249 | int count = list.Count; 250 | for (int index1 = 1; index1 < count; ++index1) { 251 | T y = list[index1]; 252 | int index2; 253 | for (index2 = index1 - 1; index2 >= 0 && comparison(list[index2], y) > 0; --index2) 254 | list[index2 + 1] = list[index2]; 255 | list[index2 + 1] = y; 256 | } 257 | } 258 | 259 | /// 260 | /// Sorts using an insertion sorting algorithm 261 | /// 262 | public static void InsertionSort(this IList list) where T : IComparable { 263 | InsertionSort(list, (comparable, comparable1) => comparable.CompareTo(comparable1)); 264 | } 265 | 266 | #endregion 267 | 268 | #region private Utilities 269 | 270 | private static void QuickSort_Impl(this IList list, int startIndex, int endIndex, Comparison comparison) { 271 | while (true) { 272 | if (startIndex >= endIndex) return; 273 | 274 | int partitionIndex = QuickSort_Partition(list, startIndex, endIndex, comparison); 275 | 276 | QuickSort_Impl(list, startIndex, partitionIndex - 1, comparison); 277 | startIndex = partitionIndex + 1; 278 | } 279 | } 280 | 281 | private static int QuickSort_Partition(IList list, int low, int high, Comparison comparison) { 282 | T pivot = list[high]; 283 | int lowIndex = low - 1; 284 | 285 | for (int j = low; j < high; j++) 286 | if (comparison(list[j], pivot) <= 0) { 287 | lowIndex++; 288 | T temp = list[lowIndex]; 289 | list[lowIndex] = list[j]; 290 | list[j] = temp; 291 | } 292 | 293 | T temp1 = list[lowIndex + 1]; 294 | list[lowIndex + 1] = list[high]; 295 | list[high] = temp1; 296 | 297 | return lowIndex + 1; 298 | } 299 | 300 | #endregion 301 | } 302 | } -------------------------------------------------------------------------------- /Runtime/Extensions/ListExtensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 65b6208db80048d4996d71f69d283345 3 | timeCreated: 1608653952 -------------------------------------------------------------------------------- /Runtime/Extensions/MathExtensions.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace UnityCommons { 4 | public static partial class Extensions { 5 | #region General Mapping 6 | 7 | /// 8 | /// Maps from the range [, ] to the range [, ]. 9 | /// 10 | /// Value to map 11 | /// Minimum source value 12 | /// Maximum source value 13 | /// Minimum target value 14 | /// Maximum target value 15 | /// mapped from range [, ] to range [, ] 16 | public static float Map(this float value, float fromSource, float toSource, float fromTarget, float toTarget) { 17 | return (value - fromSource) / (toSource - fromSource) * (toTarget - fromTarget) + fromTarget; 18 | } 19 | 20 | /// 21 | /// Maps from the range [, ] to the range [, ]. 22 | /// 23 | /// Value to map 24 | /// Minimum source value 25 | /// Maximum source value 26 | /// Minimum target value 27 | /// Maximum target value 28 | /// mapped from range [, ] to range [, ] 29 | public static int Map(this int value, int fromSource, int toSource, int fromTarget, int toTarget) { 30 | return (int) (((float)value - fromSource) / (toSource - fromSource) * (toTarget - fromTarget) + fromTarget); 31 | } 32 | 33 | /// 34 | /// Maps from the range [, ] to the range [, ]. 35 | /// 36 | /// Value to map 37 | /// Minimum source value 38 | /// Maximum source value 39 | /// Minimum target value 40 | /// Maximum target value 41 | /// mapped from range [, ] to range [, ] 42 | public static UnityEngine.Vector2 Map(this UnityEngine.Vector2 value, float fromSource, float toSource, float fromTarget, float toTarget) { 43 | return new UnityEngine.Vector2(value.x.Map(fromSource, toSource, fromTarget, toTarget), 44 | value.y.Map(fromSource, toSource, fromTarget, toTarget)); 45 | } 46 | 47 | /// 48 | /// Maps from the range [, ] to the range [, ]. 49 | /// 50 | /// Value to map 51 | /// Minimum source value 52 | /// Maximum source value 53 | /// Minimum target value 54 | /// Maximum target value 55 | /// mapped from range [, ] to range [, ] 56 | public static UnityEngine.Vector3 Map(this UnityEngine.Vector3 value, float fromSource, float toSource, float fromTarget, float toTarget) { 57 | return new UnityEngine.Vector3(value.x.Map(fromSource, toSource, fromTarget, toTarget), 58 | value.y.Map(fromSource, toSource, fromTarget, toTarget), 59 | value.z.Map(fromSource, toSource, fromTarget, toTarget)); 60 | } 61 | 62 | #endregion 63 | 64 | #region Extensions 65 | 66 | /// 67 | /// Returns clamped to be in range [0, 1] 68 | /// 69 | /// The value to clamp 70 | /// clamped to the range [0, 1] 71 | public static float Clamped01(this float value) => Clamped(value, 0.0f, 1.0f); 72 | 73 | /// 74 | /// Returns clamped to be in range [0, 1] 75 | /// 76 | /// The value to clamp 77 | /// clamped to the range [0, 1] 78 | public static double Clamped01(this double value) => Clamped(value, 0.0d, 1.0d); 79 | 80 | /// 81 | /// Returns clamped to be in range [0, 1] 82 | /// 83 | /// The value to clamp 84 | /// clamped to the range [0, 1] 85 | public static decimal Clamped01(this decimal value) => Clamped(value, 0.0m, 1.0m); 86 | 87 | /// 88 | /// Returns clamped to be in range [0, 1] 89 | /// 90 | /// The value to clamp 91 | /// clamped to the range [0, 1] 92 | public static int Clamped01(this int value) => value <= 0 ? 0 : 1; 93 | 94 | /// 95 | /// Returns clamped to be in range [(0,0,0), (1,1,1)] 96 | /// 97 | /// The value to clamp 98 | /// clamped to the range [(0,0,0), (1,1,1)] 99 | public static Vector3 Clamped01(this Vector3 value) => value.Clamped(Vector3.zero, Vector3.one); 100 | 101 | /// 102 | /// Returns clamped to be in range [, ] 103 | /// 104 | /// The value to clamp 105 | /// The minimum value 106 | /// The maximum value 107 | /// clamped to the range [, ] 108 | public static Vector3 Clamped(this Vector3 value, Vector3 min, Vector3 max) { 109 | return new Vector3(value.x.Clamped(min.x, max.x), 110 | value.y.Clamped(min.y, max.y), 111 | value.z.Clamped(min.z, max.z)); 112 | } 113 | 114 | /// 115 | /// Returns wrapped between the range and . 116 | /// 117 | public static float Wrapped(this float value, float min, float max) { 118 | if (value < min) 119 | return max - (min - value) % (max - min); 120 | return min + (value - min) % (max - min); 121 | } 122 | 123 | /// 124 | /// Returns wrapped between the range and . 125 | /// 126 | public static double Wrapped(this double value, double min, double max) { 127 | if (value < min) 128 | return max - (min - value) % (max - min); 129 | return min + (value - min) % (max - min); 130 | } 131 | 132 | /// 133 | /// Returns wrapped between the range and . 134 | /// 135 | public static decimal Wrapped(this decimal value, decimal min, decimal max) { 136 | if (value < min) 137 | return max - (min - value) % (max - min); 138 | return min + (value - min) % (max - min); 139 | } 140 | 141 | /// 142 | /// Returns wrapped between the range and . 143 | /// 144 | public static int Wrapped(this int value, int min, int max) { 145 | if (value < min) 146 | return max - (min - value) % (max - min); 147 | return min + (value - min) % (max - min); 148 | } 149 | 150 | /// 151 | /// Returns rounded to the nearest multiple of 152 | /// 153 | /// The value to round 154 | /// The number to round to nearest multiple of 155 | /// rounded to the nearest multiple of 156 | public static int RoundedTo(this int value, int n) { 157 | int remainder = value % n; 158 | if (System.Math.Abs(remainder) < n / 2.0) return value - remainder; 159 | if (value > 0) return value + (n - System.Math.Abs(remainder)); 160 | return value - (n - System.Math.Abs(remainder)); 161 | } 162 | 163 | /// 164 | /// Returns rounded to the nearest multiple of 165 | /// 166 | /// The value to round 167 | /// The number to round to nearest multiple of 168 | /// rounded to the nearest multiple of 169 | public static float RoundedTo(this float value, float n) => UnityEngine.Mathf.Round(value / n) * n; 170 | 171 | /// 172 | /// Returns rounded to the nearest multiple of 173 | /// 174 | /// The value to round 175 | /// The number to round to nearest multiple of 176 | /// rounded to the nearest multiple of 177 | public static double RoundedTo(this double value, double n) => System.Math.Round(value / n, System.MidpointRounding.AwayFromZero) * n; 178 | 179 | /// 180 | /// Returns rounded to the nearest multiple of 181 | /// 182 | /// The value to round 183 | /// The number to round to nearest multiple of 184 | /// rounded to the nearest multiple of 185 | public static decimal RoundedTo(this decimal value, decimal n) => System.Math.Round(value / n, System.MidpointRounding.AwayFromZero) * n; 186 | 187 | #endregion 188 | 189 | #region Generic Extensions 190 | 191 | /// 192 | /// Returns clamped to be in range [, ] 193 | /// 194 | /// The value to clamp 195 | /// The minimum value 196 | /// The maximum value 197 | /// Type that implements IComparable 198 | /// clamped to the range [, ] 199 | public static T Clamped(this T value, T min, T max) where T : System.IComparable { 200 | // swap is min is greater than max 201 | if (min.CompareTo(max) > 0) { 202 | (min, max) = (max, min); 203 | } 204 | return value.CompareTo(min) < 0 ? min : value.CompareTo(max) > 0 ? max : value; 205 | } 206 | 207 | /// 208 | /// Constrains to be at least 209 | /// 210 | /// The value to constrain 211 | /// The minimum value 212 | /// Type that implements IComparable 213 | /// if is less than , and otherwise 214 | public static T MinClamped(this T value, T min) where T : System.IComparable { 215 | return value.CompareTo(min) < 0 ? min : value; 216 | } 217 | 218 | /// 219 | /// Constrains to be at most 220 | /// 221 | /// The value to constrain 222 | /// The maximum value 223 | /// Type that implements IComparable 224 | /// if is greater than , and otherwise 225 | public static T MaxClamped(this T value, T max) where T : System.IComparable { 226 | return value.CompareTo(max) > 0 ? max : value; 227 | } 228 | 229 | /// 230 | /// Returns true if is between (inclusive) and (inclusive), otherwise false. 231 | /// 232 | /// The value to compare 233 | /// The minimum value to compare against (inclusive) 234 | /// The maximum value to compare against (inclusive) 235 | /// Type that implements IComparable 236 | /// true if is between (inclusive) and (inclusive), otherwise false 237 | public static bool Between(this T value, T a, T b) where T : System.IComparable => value.CompareTo(a) >= 0 && value.CompareTo(b) <= 0; 238 | 239 | #endregion 240 | } 241 | } -------------------------------------------------------------------------------- /Runtime/Extensions/MathExtensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 84376514daf94f97a5dec986ff44b908 3 | timeCreated: 1609418329 -------------------------------------------------------------------------------- /Runtime/Extensions/ObjectExtensions.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace UnityCommons { 4 | public static partial class Extensions { 5 | public static void Log(this object value) { 6 | Debug.Log(value); 7 | } 8 | 9 | public static void LogWarning(this object value) { 10 | Debug.LogWarning(value); 11 | } 12 | 13 | public static void LogError(this object value) { 14 | Debug.LogError(value); 15 | } 16 | 17 | public static void Log(this object value, Object context) { 18 | Debug.Log(value, context); 19 | } 20 | 21 | public static void LogWarning(this object value, Object context) { 22 | Debug.LogWarning(value, context); 23 | } 24 | 25 | public static void LogError(this object value, Object context) { 26 | Debug.LogError(value, context); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /Runtime/Extensions/ObjectExtensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ff02e37d71ac42cabd311b68ac26e2f1 3 | timeCreated: 1613925449 -------------------------------------------------------------------------------- /Runtime/Misc.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 159d01ccfd4be4f4388fde7c8b28fc85 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Misc/ObjectPool.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEngine; 3 | 4 | namespace UnityCommons { 5 | public abstract class ObjectPool : MonoBehaviour { 6 | [Header("Pool Settings")] 7 | [SerializeField, Tooltip("The number of items initially allocated to the pool.")] private int initialPoolSize = 16; 8 | [SerializeField, Tooltip("Represents the factor by which the pool grows in size. For example, a growth factor of 2.0 means that the pool doubles in size once it runs out of elements")] private float growthFactor = 1.5f; 9 | 10 | private Queue pool; 11 | private int size; 12 | private bool initialized; 13 | 14 | public int PooledCount => pool.Count; 15 | public int ReleasedCount => size - pool.Count; 16 | 17 | private void Awake() { 18 | pool = new Queue(); 19 | size = 0; 20 | initialized = false; 21 | } 22 | 23 | /// 24 | /// Returns a new instance of type . 25 | /// 26 | protected abstract T CreatePooled(); 27 | 28 | /// 29 | /// Called when an item is requested from the pool. 30 | /// 31 | protected virtual void OnGet(T pooledItem) { 32 | } 33 | 34 | /// 35 | /// Called when an item is returned back to the pool. 36 | /// 37 | protected virtual void OnReturn(T pooledItem) { 38 | } 39 | 40 | /// 41 | /// Gets an item from the pool. 42 | /// 43 | public T Get() { 44 | if (!initialized) { 45 | Allocate(initialPoolSize); 46 | initialized = true; 47 | } 48 | 49 | if (pool.Count == 0) { 50 | Grow(); 51 | } 52 | 53 | T pooledItem = pool.Dequeue(); 54 | OnGet(pooledItem); 55 | return pooledItem; 56 | } 57 | 58 | /// 59 | /// Returns to the pool. 60 | /// 61 | public void Return(T pooledItem) { 62 | OnReturn(pooledItem); 63 | pool.Enqueue(pooledItem); 64 | } 65 | 66 | private void Allocate(int amount) { 67 | size += amount; 68 | for (int i = 0; i < amount; i++) { 69 | pool.Enqueue(CreatePooled()); 70 | } 71 | } 72 | 73 | private void Grow() { 74 | if (growthFactor <= 1.0f) growthFactor = 1.5f; 75 | int newItems = Mathf.CeilToInt(size * growthFactor) - size; 76 | Allocate(newItems); 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /Runtime/Misc/ObjectPool.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d31c6050692f4e27a60d438c901c0b46 3 | timeCreated: 1641815488 -------------------------------------------------------------------------------- /Runtime/Misc/Rand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using UnityEngine; 5 | 6 | namespace UnityCommons { 7 | public static class Rand { 8 | private static readonly RNGProvider provider = new RNGProvider(); 9 | private static readonly Stack stateStack = new Stack(); 10 | private static uint iterations; 11 | private const float pi = 3.1415926535897932f; 12 | private const float twoPi = 6.2831853071795864f; 13 | 14 | public static int Seed { 15 | set { 16 | if (stateStack.Count == 0) 17 | Debug.LogError("Modifying the initial rand seed. Call PushState() first. The initial rand seed should always be based on the startup time and set only once."); 18 | provider.Seed = (uint) value; 19 | iterations = 0U; 20 | } 21 | } 22 | 23 | /// 24 | /// Returns a random float from 0 to 1 (both inclusive) 25 | /// 26 | public static float Float => provider.GetFloat(iterations++); 27 | 28 | /// 29 | /// Returns a random integer 30 | /// 31 | public static int Int => provider.GetInt(iterations++); 32 | 33 | /// 34 | /// Returns a random long 35 | /// 36 | public static long Long => BitConverter.ToInt64(Bytes(8), 0); 37 | 38 | /// 39 | /// Returns a random bool 40 | /// 41 | public static bool Bool => Float < 0.5; 42 | 43 | /// 44 | /// Returns a random sign. (Either 1 or -1) 45 | /// 46 | public static int Sign => Bool ? 1 : -1; 47 | 48 | private static ulong StateCompressed { 49 | get => provider.Seed | ((ulong) iterations << 32); 50 | set { 51 | provider.Seed = (uint) (value & uint.MaxValue); 52 | iterations = (uint) ((value >> 32) & uint.MaxValue); 53 | } 54 | } 55 | 56 | static Rand() { 57 | provider.Seed = (uint) DateTime.Now.GetHashCode(); 58 | } 59 | 60 | #region Lists 61 | 62 | /// 63 | /// Returns a random element from 64 | /// 65 | public static T ListItem(IList list) { 66 | return list[Range(0, list.Count)]; 67 | } 68 | 69 | /// 70 | /// Returns a random element from 71 | /// 72 | public static T ReadOnlyListItem(IReadOnlyList readOnlyList) { 73 | return readOnlyList[Range(0, readOnlyList.Count)]; 74 | } 75 | 76 | #endregion 77 | 78 | #region General 79 | 80 | /// 81 | /// Returns a random integer from (inclusive) to (exclusive) 82 | /// 83 | /// Minimum range (inclusive) 84 | /// Maximum range (exclusive) 85 | /// A random integer from (inclusive) to (exclusive) 86 | public static int Range(int min, int max) { 87 | if (max <= min) 88 | return min; 89 | return min + Mathf.Abs(Int % (max - min)); 90 | } 91 | /// 92 | /// Returns a random integer from 0 (inclusive) to (exclusive) 93 | /// 94 | /// Maximum range (exclusive) 95 | /// A random integer from 0 (inclusive) to (exclusive) 96 | public static int Range(int max) { 97 | return Range(0, max); 98 | } 99 | 100 | /// 101 | /// Returns a random integer from (inclusive) to (inclusive) 102 | /// 103 | /// Minimum range (inclusive) 104 | /// Maximum range (inclusive) 105 | /// A random integer from (inclusive) to (inclusive) 106 | public static int RangeInclusive(int min, int max) { 107 | if (max <= min) 108 | return min; 109 | return Range(min, max + 1); 110 | } 111 | 112 | /// 113 | /// Returns a random float from (inclusive) to (inclusive) 114 | /// 115 | /// Minimum range (inclusive) 116 | /// Maximum range (inclusive) 117 | /// A random float from (inclusive) to (inclusive) 118 | public static float Range(float min, float max) { 119 | if (max <= (double) min) 120 | return min; 121 | return Float * (max - min) + min; 122 | } 123 | 124 | /// 125 | /// Returns a random float from 0 (inclusive) to (inclusive) 126 | /// 127 | /// Maximum range (inclusive) 128 | /// A random float from 0 (inclusive) to (inclusive) 129 | public static float Range(float max) { 130 | return Range(0f, max); 131 | } 132 | 133 | /// 134 | /// Returns true if a random chance occurs, false otherwise 135 | /// 136 | /// The chance (between 0 and 1) 137 | /// true if a random chance occurs, false otherwise 138 | public static bool Chance(float chance) { 139 | if (chance <= 0.0) 140 | return false; 141 | if (chance >= 1.0) 142 | return true; 143 | return Float < (double) chance; 144 | } 145 | 146 | /// 147 | /// Returns an array of random bytes 148 | /// 149 | /// The number of bytes to generate 150 | /// An array of random bytes 151 | public static byte[] Bytes(int count) { 152 | byte[] buffer = new byte[count]; 153 | for (int i = 0; i < buffer.Length; i++) { 154 | buffer[i] = (byte) (Int % 256); 155 | } 156 | return buffer; 157 | } 158 | 159 | #endregion 160 | 161 | #region Geometric 162 | 163 | /// 164 | /// Returns a random unit vector 165 | /// 166 | public static Vector3 UnitVector3 => new Vector3(Gaussian(), Gaussian(), Gaussian()).normalized; 167 | 168 | /// 169 | /// Returns a random unit vector 170 | /// 171 | public static Vector2 UnitVector2 => new Vector2(Gaussian(), Gaussian()).normalized; 172 | 173 | /// 174 | /// Returns a random point inside the unit circle 175 | /// 176 | public static Vector2 InsideUnitCircle { 177 | get { 178 | Vector2 vector; 179 | do { 180 | vector = new Vector2(Float - 0.5f, Float - 0.5f) * 2f; 181 | } while (vector.sqrMagnitude > 1.0f); 182 | 183 | return vector; 184 | } 185 | } 186 | 187 | /// 188 | /// Returns a random point inside the unit circle 189 | /// 190 | public static Vector3 InsideUnitCircleVec3 { 191 | get { 192 | Vector2 insideUnitCircle = InsideUnitCircle; 193 | return new Vector3(insideUnitCircle.x, 0.0f, insideUnitCircle.y); 194 | } 195 | } 196 | 197 | /// 198 | /// Returns a random point inside the unit sphere 199 | /// 200 | public static Vector3 InsideUnitSphere { 201 | get { 202 | Vector3 vector; 203 | do { 204 | vector = new Vector3(Float - 0.5f, Float - 0.5f, Float - 0.5f) * 2f; 205 | } while (vector.sqrMagnitude > 1.0f); 206 | 207 | return vector; 208 | } 209 | } 210 | 211 | #endregion 212 | 213 | #region Seeded 214 | 215 | /// 216 | /// Returns a random integer in from (inclusive) to (exclusive) using as a seed 217 | /// 218 | /// Minimum range (inclusive) 219 | /// Maximum range (exclusive) 220 | /// Custom seed 221 | /// A random integer in from (inclusive) to (exclusive) using as a seed 222 | public static int RangeSeeded(int min, int max, int seed) { 223 | PushState(seed); 224 | int num = Range(min, max); 225 | PopState(); 226 | return num; 227 | } 228 | 229 | /// 230 | /// Returns a random integer in from (inclusive) to (inclusive) using as a seed 231 | /// 232 | /// Minimum range (inclusive) 233 | /// Maximum range (inclusive) 234 | /// Custom seed 235 | /// A random integer in from (inclusive) to (inclusive) using as a seed 236 | public static int RangeInclusiveSeeded(int min, int max, int seed) { 237 | PushState(seed); 238 | int num = RangeInclusive(min, max); 239 | PopState(); 240 | return num; 241 | } 242 | 243 | /// 244 | /// Returns a random float in from (inclusive) to (inclusive) using as a seed 245 | /// 246 | /// Minimum range (inclusive) 247 | /// Maximum range (inclusive) 248 | /// Custom seed 249 | /// A random float in from (inclusive) to (inclusive) using as a seed 250 | public static float RangeSeeded(float min, float max, int seed) { 251 | PushState(seed); 252 | float num = Range(min, max); 253 | PopState(); 254 | return num; 255 | } 256 | 257 | /// 258 | /// Returns a random float from 0 to 1 (both inclusive) using as a seed 259 | /// 260 | /// Custom seed 261 | /// A random float from 0 to 1 (both inclusive) using as a seed 262 | public static float FloatSeeded(int seed) { 263 | PushState(seed); 264 | float num = Float; 265 | PopState(); 266 | return num; 267 | } 268 | 269 | /// 270 | /// Returns true if a random chance occurs using as a seed, false otherwise 271 | /// 272 | /// The chance (between 0 and 1) 273 | /// Custom seed 274 | /// true if a random chance occurs using as a seed, false otherwise 275 | public static bool ChanceSeeded(float chance, int seed) { 276 | PushState(seed); 277 | bool flag = Chance(chance); 278 | PopState(); 279 | return flag; 280 | } 281 | 282 | /// 283 | /// Returns an array of random bytes using as a seed 284 | /// 285 | /// The number of bytes to generate 286 | /// Custom seed 287 | /// An array of random bytes 288 | public static byte[] BytesSeeded(int count, int seed) { 289 | PushState(seed); 290 | byte[] bytes = Bytes(count); 291 | PopState(); 292 | return bytes; 293 | } 294 | 295 | #endregion 296 | 297 | #region Element 298 | 299 | /// 300 | /// Returns either or randomly. 301 | /// 302 | public static T Element(T a, T b) { 303 | if (Bool) 304 | return a; 305 | return b; 306 | } 307 | 308 | /// 309 | /// Returns either , , or randomly. 310 | /// 311 | public static T Element(T a, T b, T c) { 312 | float num = Float; 313 | if (num < 0.333330005407333) 314 | return a; 315 | if (num < 0.666660010814667) 316 | return b; 317 | return c; 318 | } 319 | 320 | /// 321 | /// Returns either , , , or randomly. 322 | /// 323 | public static T Element(T a, T b, T c, T d) { 324 | float num = Float; 325 | if (num < 0.25) 326 | return a; 327 | if (num < 0.5) 328 | return b; 329 | if (num < 0.75) 330 | return c; 331 | return d; 332 | } 333 | 334 | /// 335 | /// Returns either , , , , or randomly. 336 | /// 337 | public static T Element(T a, T b, T c, T d, T e) { 338 | float num = Float; 339 | if (num < 0.2f) 340 | return a; 341 | if (num < 0.4f) 342 | return b; 343 | if (num < 0.6f) 344 | return c; 345 | if (num < 0.8f) 346 | return d; 347 | return e; 348 | } 349 | 350 | /// 351 | /// Returns either , , , , , or randomly. 352 | /// 353 | public static T Element(T a, T b, T c, T d, T e, T f) { 354 | float num = Float; 355 | if (num < 0.166659995913506) 356 | return a; 357 | if (num < 0.333330005407333) 358 | return b; 359 | if (num < 0.5) 360 | return c; 361 | if (num < 0.666660010814667) 362 | return d; 363 | if (num < 0.833329975605011) 364 | return e; 365 | return f; 366 | } 367 | 368 | /// 369 | /// Returns a random element from 370 | /// 371 | public static T Element(params T[] items) { 372 | return ListItem(items); 373 | } 374 | 375 | #endregion 376 | 377 | #region Vector Ranges 378 | 379 | /// 380 | /// Returns a random float from .x to .y 381 | /// 382 | public static float Range(Vector2 range) { 383 | return Range(range.x, range.y); 384 | } 385 | 386 | /// 387 | /// Returns a random integer from .x (inclusive) to .y (exclusive) 388 | /// 389 | public static int Range(Vector2Int range) { 390 | return Range(range.x, range.y); 391 | } 392 | 393 | /// 394 | /// Returns a random integer from .x (inclusive) to .y (inclusive) 395 | /// 396 | public static int RangeInclusive(Vector2Int range) { 397 | return RangeInclusive(range.x, range.y); 398 | } 399 | 400 | #endregion 401 | 402 | #region Utilities 403 | 404 | public static float Gaussian(float centerX = 0.0f, float widthFactor = 1f) { 405 | return Mathf.Sqrt(-2f * Mathf.Log(Float)) * Mathf.Sin(twoPi * Float) * widthFactor + centerX; 406 | } 407 | 408 | public static float GaussianAsymmetric(float centerX = 0.0f, float lowerWidthFactor = 1f, float upperWidthFactor = 1f) { 409 | float num = Mathf.Sqrt(-2f * Mathf.Log(Float)) * Mathf.Sin(twoPi * Float); 410 | if (num <= 0.0) 411 | return num * lowerWidthFactor + centerX; 412 | return num * upperWidthFactor + centerX; 413 | } 414 | 415 | public static void PushState() { 416 | stateStack.Push(StateCompressed); 417 | } 418 | 419 | /// 420 | /// Replaces the current seed with . Use to undo this operation and retrieve the original state 421 | /// 422 | public static void PushState(int replacementSeed) { 423 | PushState(); 424 | Seed = replacementSeed; 425 | } 426 | 427 | public static void PopState() { 428 | StateCompressed = stateStack.Pop(); 429 | } 430 | 431 | public static void EnsureStateStackEmpty() { 432 | if (stateStack.Count <= 0) 433 | return; 434 | Debug.LogWarning("Random state stack is not empty. There were more calls to PushState than PopState. Fixing."); 435 | while (stateStack.Any()) 436 | PopState(); 437 | } 438 | 439 | #endregion 440 | 441 | #region Miscellaneous 442 | 443 | /// 444 | /// Generates a random color using an HSV range and alpha range. 445 | /// 446 | /// Minimum hue [range 0..1] 447 | /// Maximum hue [range 0..1] 448 | /// Minimum saturation [range 0..1] 449 | /// Maximum saturation [range 0..1] 450 | /// Minimum value [range 0..1] 451 | /// Maximum value [range 0..1] 452 | /// Minimum alpha [range 0..1] 453 | /// Maximum alpha [range 0..1] 454 | /// A random color with HSV and alpha values in input range 455 | ///
This is the same implementation as UnityEngine.Random.ColorHSV()
456 | public static Color ColorHSV( 457 | float hueMin = 0.0f, float hueMax = 1.0f, float saturationMin = 0.0f, float saturationMax = 1.0f, 458 | float valueMin = 0.0f, float valueMax = 1.0f, float alphaMin = 1.0f, float alphaMax = 1.0f 459 | ) { 460 | Color color = Color.HSVToRGB( 461 | Range(hueMin.Clamped01(), hueMax.Clamped01()), 462 | Range(saturationMin.Clamped01(), saturationMax.Clamped01()), 463 | Range(valueMin.Clamped01(), valueMax.Clamped01()) 464 | ); 465 | color.a = Range(alphaMin.Clamped01(), alphaMax.Clamped01()); 466 | return color; 467 | } 468 | 469 | #endregion 470 | 471 | private class RNGProvider { 472 | public uint Seed = (uint) DateTime.Now.GetHashCode(); 473 | 474 | public int GetInt(uint iterations) { 475 | return (int) GetHash((int) iterations); 476 | } 477 | 478 | public float GetFloat(uint iterations) { 479 | return (float) ((GetInt(iterations) - (double) int.MinValue) / uint.MaxValue); 480 | } 481 | 482 | private uint GetHash(int buffer) { 483 | uint num1 = Rotate(Seed + 374761393U + 4U + (uint) (buffer * -1028477379), 17) * 668265263U; 484 | uint num2 = (num1 ^ (num1 >> 15)) * 2246822519U; 485 | uint num3 = (num2 ^ (num2 >> 13)) * 3266489917U; 486 | return num3 ^ (num3 >> 16); 487 | } 488 | 489 | private static uint Rotate(uint value, int count) { 490 | return (value << count) | (value >> (32 - count)); 491 | } 492 | } 493 | 494 | } 495 | } -------------------------------------------------------------------------------- /Runtime/Misc/Rand.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 431bfa14b3472ec4a8cf9d8e4007e154 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Misc/Ref.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UnityCommons { 4 | /// 5 | /// Creates a reference object to a struct type 6 | /// 7 | /// Type of object 8 | public class Ref : IEquatable, IEquatable> where T : struct { 9 | private T value; 10 | private Func getValue = null; 11 | private Action setValue = null; 12 | 13 | private Ref(T value) { 14 | this.value = value; 15 | } 16 | 17 | private Ref() { 18 | value = new T(); 19 | } 20 | 21 | public ref T GetReference() { 22 | if (getValue != null) { 23 | T val = getValue(); 24 | if (!val.Equals(value)) value = val; 25 | } 26 | return ref value; 27 | } 28 | 29 | public T GetValue() { 30 | if (getValue != null) { 31 | T val = getValue(); 32 | if (!val.Equals(value)) value = val; 33 | } 34 | return value; 35 | } 36 | 37 | public void Set(T newValue) { 38 | value = newValue; 39 | setValue?.Invoke(); 40 | } 41 | 42 | public void SetValueUnbound(T newValue) { 43 | value = newValue; 44 | } 45 | 46 | public T GetValueUnbound() { 47 | return value; 48 | } 49 | 50 | public void Bind(Func getValue, Action setValue) { 51 | this.getValue = getValue; 52 | this.setValue = setValue; 53 | } 54 | 55 | public void Unbind() { 56 | getValue = null; 57 | setValue = null; 58 | } 59 | 60 | #region Equality Members 61 | public bool Equals(T other) { 62 | return value.Equals(other); 63 | } 64 | 65 | public bool Equals(Ref other) { 66 | if (ReferenceEquals(null, other)) 67 | return false; 68 | if (ReferenceEquals(this, other)) 69 | return true; 70 | return Equals(other.value); 71 | } 72 | 73 | public override bool Equals(object obj) { 74 | if (ReferenceEquals(null, obj)) 75 | return false; 76 | if (ReferenceEquals(this, obj)) 77 | return true; 78 | if (obj.GetType() != this.GetType()) 79 | return false; 80 | return Equals((Ref) obj); 81 | } 82 | 83 | public override int GetHashCode() { 84 | // ReSharper disable once NonReadonlyMemberInGetHashCode 85 | return value.GetHashCode(); 86 | } 87 | 88 | public static bool operator ==(Ref left, Ref right) { 89 | return Equals(left, right); 90 | } 91 | 92 | public static bool operator !=(Ref left, Ref right) { 93 | return !Equals(left, right); 94 | } 95 | 96 | public static bool operator ==(Ref left, T right) { 97 | if (ReferenceEquals(null, left)) 98 | return false; 99 | return Equals(left.value, right); 100 | } 101 | 102 | public static bool operator !=(Ref left, T right) { 103 | if (ReferenceEquals(null, left)) 104 | return true; 105 | return !Equals(left.value, right); 106 | } 107 | #endregion 108 | 109 | public override string ToString() { 110 | return $"Ref<{typeof(T).Name}>[{value}]"; 111 | } 112 | 113 | public static Ref MakeRef(T initialValue, Func getValue, Action setValue) { 114 | Ref reference = new Ref(initialValue); 115 | reference.Bind(getValue, setValue); 116 | return reference; 117 | } 118 | 119 | public static Ref MakeRef(T initialValue) { 120 | return new Ref(initialValue); 121 | } 122 | 123 | public static Ref MakeRef() { 124 | return new Ref(); 125 | } 126 | 127 | #region Operators 128 | public static explicit operator Ref(T value) { 129 | return new Ref(value); 130 | } 131 | #endregion 132 | } 133 | } -------------------------------------------------------------------------------- /Runtime/Misc/Ref.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 32118dfc803ec2d449701d71eebcc739 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Misc/SemVer.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace UnityCommons { 4 | /// 5 | /// Represents a Semantic Versioning (or SemVer) data type. Contains , , and members. 6 | /// 7 | [System.Serializable] 8 | public struct SemVer : System.IEquatable, System.IComparable { 9 | public static readonly SemVer Invalid = new SemVer {MAJOR = -1, MINOR = -1, PATCH = -1}; 10 | 11 | // ReSharper disable once InconsistentNaming 12 | public int MAJOR; 13 | 14 | // ReSharper disable once InconsistentNaming 15 | public int MINOR; 16 | 17 | // ReSharper disable once InconsistentNaming 18 | public int PATCH; 19 | 20 | 21 | public SemVer(string versionString) { 22 | if (!IsValid(versionString, out int major, out int minor, out int patch)) { 23 | Debug.LogError($"Could not parse SemVer string {versionString} into format MAJOR.MINOR.PATCH."); 24 | this = Invalid; 25 | return; 26 | } 27 | 28 | MAJOR = major; 29 | MINOR = minor; 30 | PATCH = patch; 31 | } 32 | 33 | public void BumpMajor() { 34 | MAJOR++; 35 | MINOR = 0; 36 | PATCH = 0; 37 | } 38 | 39 | public void BumpMinor() { 40 | MINOR++; 41 | PATCH = 0; 42 | } 43 | 44 | public void BumpPatch() => PATCH++; 45 | 46 | public override string ToString() { 47 | return $"{MAJOR}.{MINOR}.{PATCH}"; 48 | } 49 | 50 | public static implicit operator string(SemVer semVer) { 51 | return semVer.ToString(); 52 | } 53 | 54 | public static explicit operator SemVer(string versionString) { 55 | return FromVersionString(versionString); 56 | } 57 | 58 | public static SemVer FromVersionString(string versionString) { 59 | return new SemVer(versionString); 60 | } 61 | 62 | public static bool IsValid(string versionString, out int major, out int minor, out int patch) { 63 | string[] split = versionString.Split('.'); 64 | if (split.Length != 3) { 65 | major = -1; 66 | minor = -1; 67 | patch = -1; 68 | return false; 69 | } 70 | 71 | bool majorWorks = int.TryParse(split[0], out int majorParsed) && majorParsed >= 0; 72 | bool minorWorks = int.TryParse(split[1], out int minorParsed) && minorParsed >= 0; 73 | bool patchWorks = int.TryParse(split[2], out int patchParsed) && patchParsed >= 0; 74 | major = majorParsed; 75 | minor = minorParsed; 76 | patch = patchParsed; 77 | return majorWorks && minorWorks && patchWorks; 78 | } 79 | 80 | public static bool IsValid(string versionString) => IsValid(versionString, out _, out _, out _); 81 | 82 | public bool Equals(SemVer other) { 83 | return MAJOR == other.MAJOR && MINOR == other.MINOR && PATCH == other.PATCH; 84 | } 85 | 86 | public override bool Equals(object obj) { 87 | return obj is SemVer other && Equals(other); 88 | } 89 | 90 | public override int GetHashCode() { 91 | unchecked { 92 | int hashCode = MAJOR; 93 | hashCode = (hashCode * 397) ^ MINOR; 94 | hashCode = (hashCode * 397) ^ PATCH; 95 | return hashCode; 96 | } 97 | } 98 | 99 | public static bool operator ==(SemVer left, SemVer right) { 100 | return left.Equals(right); 101 | } 102 | 103 | public static bool operator !=(SemVer left, SemVer right) { 104 | return !left.Equals(right); 105 | } 106 | 107 | public int CompareTo(SemVer other) { 108 | int majorComparison = MAJOR.CompareTo(other.MAJOR); 109 | if (majorComparison != 0) 110 | return majorComparison; 111 | int minorComparison = MINOR.CompareTo(other.MINOR); 112 | if (minorComparison != 0) 113 | return minorComparison; 114 | return PATCH.CompareTo(other.PATCH); 115 | } 116 | } 117 | } -------------------------------------------------------------------------------- /Runtime/Misc/SemVer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a2e11db8a2d66fb4aa5707c3368a2d9c 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Singleton.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5d0dda5df1bb412e9c1a6b194d326400 3 | timeCreated: 1640799715 -------------------------------------------------------------------------------- /Runtime/Singleton/AutoMonoSingleton.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace UnityCommons { 4 | /// 5 | /// Creates a MonoBehaviour singleton of type . Ensures that only a single instance exists. 6 | /// 7 | /// Component type 8 | public abstract class AutoMonoSingleton : MonoBehaviour where T : AutoMonoSingleton { 9 | private static readonly System.Type type = typeof(T); 10 | private static T instance; 11 | public static bool IsInitialized => instance != null; 12 | 13 | public static T Instance { 14 | get { 15 | // Find first object of type T. Other instances are destroyed when Awake is called on them. 16 | #if UNITY_2020_1_OR_NEWER 17 | if (instance == null) instance = FindObjectOfType(true); 18 | #else 19 | if (instance == null) { 20 | T[] objects = Resources.FindObjectsOfTypeAll(); 21 | if (objects.Length > 0) instance = objects[0]; 22 | } 23 | #endif 24 | if (instance != null) return instance; 25 | 26 | instance = new GameObject($"MonoSingleton<{type.Name}>").AddComponent(); 27 | return instance; 28 | } 29 | } 30 | 31 | public static void EnsureInitialized() { 32 | _ = Instance; 33 | } 34 | 35 | private void Awake() { 36 | if (Instance != null && Instance != this) { 37 | Debug.LogError($"Cannot have multiple instances of {type.Name}. Destroying excess instances."); 38 | Destroy(this); 39 | return; 40 | } 41 | 42 | OnAwake(); 43 | } 44 | 45 | protected virtual void OnAwake() { 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /Runtime/Singleton/AutoMonoSingleton.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: cf4de925caaa47a88ebc07924a1e91f8 3 | timeCreated: 1644336523 -------------------------------------------------------------------------------- /Runtime/Singleton/MonoSingleton.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace UnityCommons { 4 | /// 5 | /// Creates a MonoBehaviour singleton of type . Ensures that only a single instance exists. 6 | /// 7 | /// Component type 8 | public abstract class MonoSingleton : MonoBehaviour where T : MonoSingleton { 9 | private static readonly System.Type type = typeof(T); 10 | private static T instance; 11 | public static bool IsInitialized => instance != null; 12 | 13 | public static T Instance { 14 | get { 15 | // Find first object of type T. Other instances are destroyed when Awake is called on them. 16 | #if UNITY_2020_1_OR_NEWER 17 | if (instance == null) instance = FindObjectOfType(true); 18 | #else 19 | if (instance == null) { 20 | T[] objects = Resources.FindObjectsOfTypeAll(); 21 | if (objects.Length > 0) instance = objects[0]; 22 | } 23 | #endif 24 | if (instance != null) return instance; 25 | 26 | Debug.LogWarning($"MonoSingleton<{type.Name}> could not be found!"); 27 | return instance = null; 28 | } 29 | } 30 | 31 | private void Awake() { 32 | if (!IsInitialized) { 33 | instance = (T)this; 34 | OnAwake(); 35 | return; 36 | } 37 | 38 | if (instance != (T)this) { 39 | Debug.LogError($"Cannot have multiple instances of {type.Name}. Destroying excess instances."); 40 | Destroy(this); 41 | return; 42 | } 43 | 44 | OnAwake(); 45 | } 46 | 47 | protected virtual void OnAwake() { 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /Runtime/Singleton/MonoSingleton.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bdab651a02674fe78ed1439c2704898f 3 | timeCreated: 1612529978 -------------------------------------------------------------------------------- /Runtime/Singleton/Singleton.Create.cs: -------------------------------------------------------------------------------- 1 | namespace UnityCommons { 2 | public static partial class Singleton { 3 | public abstract class Create where T : Create { 4 | private static T instance; 5 | public static T Instance => instance ?? (instance = MakeInstance()); 6 | public static bool IsInitialized => instance != null; 7 | 8 | protected abstract T CreateInstance(); 9 | 10 | private static T MakeInstance() { 11 | return ((T)System.Runtime.Serialization.FormatterServices.GetUninitializedObject(typeof(T))).CreateInstance(); 12 | } 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /Runtime/Singleton/Singleton.Create.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e84c369c5efd444e82b6d48455173dff 3 | timeCreated: 1640799724 -------------------------------------------------------------------------------- /Runtime/Singleton/Singleton.New.cs: -------------------------------------------------------------------------------- 1 | namespace UnityCommons { 2 | public static partial class Singleton { 3 | public abstract class New where T : New, new() { 4 | private static T instance; 5 | public static T Instance => instance ?? (instance = new T()); 6 | public static bool IsInitialized => instance != null; 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /Runtime/Singleton/Singleton.New.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7d4c326ca744455a99bca54e0572604f 3 | timeCreated: 1640799724 -------------------------------------------------------------------------------- /Runtime/UnityCommons.Runtime.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "UnityCommons.Runtime", 3 | "references": [ 4 | "GUID:6055be8ebefd69e48b49212b09b47b2f" 5 | ], 6 | "includePlatforms": [], 7 | "excludePlatforms": [], 8 | "allowUnsafeCode": false, 9 | "overrideReferences": false, 10 | "precompiledReferences": [], 11 | "autoReferenced": true, 12 | "defineConstraints": [], 13 | "versionDefines": [], 14 | "noEngineReferences": false 15 | } -------------------------------------------------------------------------------- /Runtime/UnityCommons.Runtime.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d0ebc447e86057349b22b7994d613127 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime/UpdateManager.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3e5a6ec3c10a450a89cd5929c3dd25f5 3 | timeCreated: 1640799791 -------------------------------------------------------------------------------- /Runtime/UpdateManager/AsyncUpdateEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace UnityCommons { 7 | public delegate Task AsyncUpdateDelegate(); 8 | 9 | public sealed class AsyncUpdateEvent { 10 | private readonly List delegates = new List(); 11 | 12 | private AsyncUpdateEvent Add(AsyncUpdateDelegate @delegate) { 13 | if (@delegate == null) { 14 | throw new ArgumentNullException(nameof(@delegate)); 15 | } 16 | 17 | delegates.Add(@delegate); 18 | return this; 19 | } 20 | 21 | private AsyncUpdateEvent Remove(AsyncUpdateDelegate @delegate) { 22 | if (@delegate == null) { 23 | throw new ArgumentNullException(nameof(@delegate)); 24 | } 25 | 26 | delegates.Remove(@delegate); 27 | return this; 28 | } 29 | 30 | internal async Task InvokeSequential() { 31 | foreach (AsyncUpdateDelegate @delegate in delegates) 32 | await @delegate().ConfigureAwait(false); 33 | } 34 | 35 | internal async Task InvokeParallel() { 36 | await Task.WhenAll(delegates.Select(@delegate => @delegate())).ConfigureAwait(false); 37 | } 38 | 39 | public static AsyncUpdateEvent operator +(AsyncUpdateEvent @event, AsyncUpdateDelegate @delegate) { 40 | return @event.Add(@delegate); 41 | } 42 | 43 | public static AsyncUpdateEvent operator -(AsyncUpdateEvent @event, AsyncUpdateDelegate @delegate) { 44 | return @event.Remove(@delegate); 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /Runtime/UpdateManager/AsyncUpdateEvent.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 78b2d001c93a483a96dcc36e1f57f4cc 3 | timeCreated: 1640799806 -------------------------------------------------------------------------------- /Runtime/UpdateManager/ParallelAsyncUpdateManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UnityCommons { 4 | public sealed class ParallelAsyncUpdateManager : MonoSingleton { 5 | private readonly AsyncUpdateEvent onUpdate = new AsyncUpdateEvent(); 6 | private readonly AsyncUpdateEvent onLateUpdate = new AsyncUpdateEvent(); 7 | private readonly AsyncUpdateEvent onFixedUpdate = new AsyncUpdateEvent(); 8 | 9 | public AsyncUpdateEvent OnUpdate { 10 | get => onUpdate; 11 | set { 12 | if (value != onUpdate) throw new InvalidOperationException("Cannot change OnUpdate event"); 13 | } 14 | } 15 | 16 | public AsyncUpdateEvent OnLateUpdate { 17 | get => onLateUpdate; 18 | set { 19 | if (value != onLateUpdate) throw new InvalidOperationException("Cannot change OnLateUpdate event"); 20 | } 21 | } 22 | 23 | public AsyncUpdateEvent OnFixedUpdate { 24 | get => onFixedUpdate; 25 | set { 26 | if (value != onFixedUpdate) throw new InvalidOperationException("Cannot change OnFixedUpdate event"); 27 | } 28 | } 29 | 30 | private async void Update() { 31 | await OnUpdate.InvokeParallel(); 32 | } 33 | 34 | private async void LateUpdate() { 35 | await OnLateUpdate.InvokeParallel(); 36 | } 37 | 38 | private async void FixedUpdate() { 39 | await OnFixedUpdate.InvokeParallel(); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /Runtime/UpdateManager/ParallelAsyncUpdateManager.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c0e183f43f9d4de59afa0cf0656d4d18 3 | timeCreated: 1640799806 -------------------------------------------------------------------------------- /Runtime/UpdateManager/SequentialAsyncUpdateManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UnityCommons { 4 | public sealed class SequentialAsyncUpdateManager : MonoSingleton { 5 | private readonly AsyncUpdateEvent onUpdate = new AsyncUpdateEvent(); 6 | private readonly AsyncUpdateEvent onLateUpdate = new AsyncUpdateEvent(); 7 | private readonly AsyncUpdateEvent onFixedUpdate = new AsyncUpdateEvent(); 8 | 9 | public AsyncUpdateEvent OnUpdate { 10 | get => onUpdate; 11 | set { 12 | if (value != onUpdate) throw new InvalidOperationException("Cannot change OnUpdate event"); 13 | } 14 | } 15 | 16 | public AsyncUpdateEvent OnLateUpdate { 17 | get => onLateUpdate; 18 | set { 19 | if (value != onLateUpdate) throw new InvalidOperationException("Cannot change OnLateUpdate event"); 20 | } 21 | } 22 | 23 | public AsyncUpdateEvent OnFixedUpdate { 24 | get => onFixedUpdate; 25 | set { 26 | if (value != onFixedUpdate) throw new InvalidOperationException("Cannot change OnFixedUpdate event"); 27 | } 28 | } 29 | 30 | private async void Update() { 31 | await OnUpdate.InvokeSequential(); 32 | } 33 | 34 | private async void LateUpdate() { 35 | await OnLateUpdate.InvokeSequential(); 36 | } 37 | 38 | private async void FixedUpdate() { 39 | await OnFixedUpdate.InvokeSequential(); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /Runtime/UpdateManager/SequentialAsyncUpdateManager.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 494e0b73f1be4c35bf46214a9bd6e318 3 | timeCreated: 1640799806 -------------------------------------------------------------------------------- /Runtime/UpdateManager/UpdateManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UnityCommons { 4 | public class UpdateManager : MonoSingleton { 5 | private static readonly Action nop = () => { }; 6 | 7 | public event Action OnUpdate = nop; 8 | public event Action OnLateUpdate = nop; 9 | public event Action OnFixedUpdate = nop; 10 | 11 | private void Update() { 12 | OnUpdate(); 13 | } 14 | 15 | private void LateUpdate() { 16 | OnLateUpdate(); 17 | } 18 | 19 | private void FixedUpdate() { 20 | OnFixedUpdate(); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /Runtime/UpdateManager/UpdateManager.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 40fc7141468a4504ba2ca9b574d516a9 3 | timeCreated: 1640799806 -------------------------------------------------------------------------------- /Runtime/Utilities.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 19d929ea43804675bdc7077fddcbe125 3 | timeCreated: 1609418542 -------------------------------------------------------------------------------- /Runtime/Utilities/GridXY.cs: -------------------------------------------------------------------------------- 1 | using TMPro; 2 | using UnityEngine; 3 | 4 | namespace UnityCommons { 5 | public class GridXY { 6 | public event GridValueChangedEvent OnGridValueChanged; 7 | 8 | private readonly int width; 9 | private readonly int height; 10 | private readonly float cellSize; 11 | private readonly Vector3 gridOrigin; 12 | private readonly T[,] grid; 13 | private readonly TextMeshPro[,] debugText; 14 | 15 | public GridXY(int width, int height, float cellSize, Vector3 gridOrigin = default, T startingValue = default, bool debug = false, DebugOptions? debugOptions = null) { 16 | this.width = width; 17 | this.height = height; 18 | this.cellSize = cellSize; 19 | this.gridOrigin = gridOrigin; 20 | 21 | grid = new T[width, height]; 22 | for (int x = 0; x < width; x++) { 23 | for (int y = 0; y < height; y++) { 24 | grid[x, y] = startingValue; 25 | } 26 | } 27 | 28 | if (!debug) return; 29 | 30 | if (debugOptions == null) debugOptions = new DebugOptions(); 31 | Quaternion rotation = Quaternion.Euler(90, 0, 0); 32 | if(debugOptions.Value.ShowText) 33 | debugText = new TextMeshPro[width, height]; 34 | for (int x = 0; x < width; x++) { 35 | for (int y = 0; y < height; y++) { 36 | Debug.DrawLine(GetWorldCoordinates(x, y), GetWorldCoordinates(x, y + 1), Color.white, debugOptions.Value.LineDuration, false); 37 | Debug.DrawLine(GetWorldCoordinates(x, y), GetWorldCoordinates(x + 1, y), Color.white, debugOptions.Value.LineDuration, false); 38 | 39 | if(debugOptions.Value.ShowText) 40 | debugText[x, y] = Utils.CreateWorldText(grid[x, y].ToString(), position: GetWorldCoordinates(x, y) + new Vector3(cellSize, cellSize) * 0.5f, 41 | fontSize: debugOptions.Value.FontSize, rotation: rotation, horizontalAlignment: HorizontalAlignmentOptions.Center, 42 | verticalAlignment: VerticalAlignmentOptions.Middle); 43 | } 44 | } 45 | 46 | Debug.DrawLine(GetWorldCoordinates(0, height), GetWorldCoordinates(width, height), Color.white, debugOptions.Value.LineDuration, false); 47 | Debug.DrawLine(GetWorldCoordinates(width, 0), GetWorldCoordinates(width, height), Color.white, debugOptions.Value.LineDuration, false); 48 | OnGridValueChanged += args => { debugText[args.X, args.Y].text = args.NewValue.ToString(); }; 49 | } 50 | 51 | public Vector3 GetWorldCoordinates(int x, int y) { 52 | return new Vector3(x, y) * cellSize + gridOrigin; 53 | } 54 | 55 | public (int x, int y) GetGridCoordinates(Vector3 worldCoordinates) { 56 | worldCoordinates -= gridOrigin; 57 | return (Mathf.FloorToInt(worldCoordinates.x / cellSize), Mathf.FloorToInt(worldCoordinates.y / cellSize)); 58 | } 59 | 60 | public T this[int x, int y] { 61 | get { 62 | if (Utils.RangeCheck(x, width) && Utils.RangeCheck(y, height)) { 63 | return grid[x, y]; 64 | } 65 | 66 | return default; 67 | } 68 | set { 69 | if (Utils.RangeCheck(x, width) && Utils.RangeCheck(y, height)) { 70 | grid[x, y] = value; 71 | OnGridValueChanged?.Invoke((x, y, value)); 72 | } 73 | } 74 | } 75 | 76 | public T this[Vector2Int gridPosition] { 77 | get => this[gridPosition.x, gridPosition.y]; 78 | set => this[gridPosition.x, gridPosition.y] = value; 79 | } 80 | 81 | public T this[Vector3 worldPosition] { 82 | get { 83 | (int x, int y) = GetGridCoordinates(worldPosition); 84 | return this[x, y]; 85 | } 86 | set { 87 | (int x, int y) = GetGridCoordinates(worldPosition); 88 | this[x, y] = value; 89 | } 90 | } 91 | 92 | public delegate void GridValueChangedEvent(GridValueChangedEventArgs eventArgs); 93 | 94 | public readonly struct GridValueChangedEventArgs { 95 | public readonly int X; 96 | public readonly int Y; 97 | public readonly T NewValue; 98 | 99 | public GridValueChangedEventArgs(int x, int y, T newValue) { 100 | X = x; 101 | Y = y; 102 | NewValue = newValue; 103 | } 104 | 105 | public static implicit operator GridValueChangedEventArgs((int x, int y, T value) tuple) { 106 | (int x, int y, T value) = tuple; 107 | return new GridValueChangedEventArgs(x, y, value); 108 | } 109 | } 110 | } 111 | 112 | // Used in GridXZ as well 113 | public readonly struct DebugOptions { 114 | public readonly int FontSize; 115 | public readonly float LineDuration; 116 | public readonly bool ShowText; 117 | 118 | public DebugOptions(int fontSize = 2, float lineDuration = float.MaxValue, bool showText = false) { 119 | FontSize = fontSize; 120 | LineDuration = lineDuration; 121 | ShowText = showText; 122 | } 123 | } 124 | 125 | } -------------------------------------------------------------------------------- /Runtime/Utilities/GridXY.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 159f0df197f0424486da96b5a1582d62 3 | timeCreated: 1613209096 -------------------------------------------------------------------------------- /Runtime/Utilities/GridXZ.cs: -------------------------------------------------------------------------------- 1 | using TMPro; 2 | using UnityEngine; 3 | 4 | namespace UnityCommons { 5 | public class GridXZ { 6 | public event GridValueChangedEvent OnGridValueChanged; 7 | 8 | private readonly int width; 9 | private readonly int height; 10 | private readonly float cellSize; 11 | private readonly Vector3 gridOrigin; 12 | private readonly T[,] grid; 13 | private readonly TextMeshPro[,] debugText; 14 | 15 | public GridXZ(int width, int height, float cellSize, Vector3 gridOrigin = default, T startingValue = default, bool debug = false, DebugOptions? debugOptions = null) { 16 | this.width = width; 17 | this.height = height; 18 | this.cellSize = cellSize; 19 | this.gridOrigin = gridOrigin; 20 | 21 | grid = new T[width, height]; 22 | for (int x = 0; x < width; x++) { 23 | for (int y = 0; y < height; y++) { 24 | grid[x, y] = startingValue; 25 | } 26 | } 27 | 28 | if (!debug) return; 29 | 30 | if (debugOptions == null) debugOptions = new DebugOptions(); 31 | Quaternion rotation = Quaternion.Euler(90, 0, 0); 32 | if(debugOptions.Value.ShowText) 33 | debugText = new TextMeshPro[width, height]; 34 | for (int x = 0; x < width; x++) { 35 | for (int y = 0; y < height; y++) { 36 | Debug.DrawLine(GetWorldCoordinates(x, y), GetWorldCoordinates(x, y + 1), Color.white, debugOptions.Value.LineDuration, false); 37 | Debug.DrawLine(GetWorldCoordinates(x, y), GetWorldCoordinates(x + 1, y), Color.white, debugOptions.Value.LineDuration, false); 38 | 39 | if(debugOptions.Value.ShowText) 40 | debugText[x, y] = Utils.CreateWorldText(grid[x, y].ToString(), position: GetWorldCoordinates(x, y) + new Vector3(cellSize, cellSize) * 0.5f, 41 | fontSize: debugOptions.Value.FontSize, rotation: rotation, horizontalAlignment: HorizontalAlignmentOptions.Center, 42 | verticalAlignment: VerticalAlignmentOptions.Middle); 43 | } 44 | } 45 | 46 | Debug.DrawLine(GetWorldCoordinates(0, height), GetWorldCoordinates(width, height), Color.white, debugOptions.Value.LineDuration, false); 47 | Debug.DrawLine(GetWorldCoordinates(width, 0), GetWorldCoordinates(width, height), Color.white, debugOptions.Value.LineDuration, false); 48 | OnGridValueChanged += args => { debugText[args.X, args.Y].text = args.NewValue.ToString(); }; 49 | } 50 | 51 | public Vector3 GetWorldCoordinates(int x, int y) { 52 | return new Vector3(x, 0, y) * cellSize + gridOrigin; 53 | } 54 | 55 | public (int x, int y) GetGridCoordinates(Vector3 worldCoordinates) { 56 | worldCoordinates -= gridOrigin; 57 | return (Mathf.FloorToInt(worldCoordinates.x / cellSize), Mathf.FloorToInt(worldCoordinates.z / cellSize)); 58 | } 59 | 60 | public T this[int x, int y] { 61 | get { 62 | if (Utils.RangeCheck(x, width) && Utils.RangeCheck(y, height)) { 63 | return grid[x, y]; 64 | } 65 | 66 | return default; 67 | } 68 | set { 69 | if (Utils.RangeCheck(x, width) && Utils.RangeCheck(y, height)) { 70 | grid[x, y] = value; 71 | OnGridValueChanged?.Invoke((x, y, value)); 72 | } 73 | } 74 | } 75 | 76 | public T this[Vector2Int gridPosition] { 77 | get => this[gridPosition.x, gridPosition.y]; 78 | set => this[gridPosition.x, gridPosition.y] = value; 79 | } 80 | 81 | public T this[Vector3 worldPosition] { 82 | get { 83 | (int x, int y) = GetGridCoordinates(worldPosition); 84 | return this[x, y]; 85 | } 86 | set { 87 | (int x, int y) = GetGridCoordinates(worldPosition); 88 | this[x, y] = value; 89 | } 90 | } 91 | 92 | public delegate void GridValueChangedEvent(GridValueChangedEventArgs eventArgs); 93 | 94 | public readonly struct GridValueChangedEventArgs { 95 | public readonly int X; 96 | public readonly int Y; 97 | public readonly T NewValue; 98 | 99 | public GridValueChangedEventArgs(int x, int y, T newValue) { 100 | X = x; 101 | Y = y; 102 | NewValue = newValue; 103 | } 104 | 105 | public static implicit operator GridValueChangedEventArgs((int x, int y, T value) tuple) { 106 | (int x, int y, T value) = tuple; 107 | return new GridValueChangedEventArgs(x, y, value); 108 | } 109 | } 110 | } 111 | } -------------------------------------------------------------------------------- /Runtime/Utilities/GridXZ.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: aae67a5c99824c979c58deea1267211c 3 | timeCreated: 1613210482 -------------------------------------------------------------------------------- /Runtime/Utilities/ListUtilities.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace UnityCommons { 5 | public static class ListUtilities { 6 | /// 7 | /// Combines into a single list 8 | /// 9 | public static List Combine(params List[] lists) { 10 | List combined = new List(); 11 | foreach (List list in lists) { 12 | combined.AddRange(list); 13 | } 14 | 15 | return combined; 16 | } 17 | 18 | /// 19 | /// Combines into a single list 20 | /// 21 | public static List Combine(IEnumerable> lists) { 22 | return lists.SelectMany(list => list).ToList(); 23 | } 24 | 25 | /// 26 | /// Combines into a single list 27 | /// 28 | public static IEnumerable Combine(IEnumerable lists) where TList : IEnumerable { 29 | return lists.SelectMany(list => list); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /Runtime/Utilities/ListUtilities.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f56ef9c8b2854962b914ecc8c59687f4 3 | timeCreated: 1609418549 -------------------------------------------------------------------------------- /Runtime/Utilities/RangeUtils.cs: -------------------------------------------------------------------------------- 1 | namespace UnityCommons { 2 | public static partial class Utils { 3 | /// 4 | /// Returns true 5 | /// if is between (inclusive) and (exclusive), 6 | /// and false otherwise. 7 | /// 8 | public static bool RangeCheck(int value, int minValue, int maxValue) { 9 | return value >= minValue && value < maxValue; 10 | } 11 | 12 | /// 13 | /// Returns true 14 | /// if is between (inclusive) and (inclusive), 15 | /// and false otherwise. 16 | /// 17 | public static bool RangeCheckInclusive(int value, int minValue, int maxValue) { 18 | return value >= minValue && value <= maxValue; 19 | } 20 | 21 | /// 22 | /// Returns true 23 | /// if is between 0 (inclusive) and (exclusive), 24 | /// and false otherwise. 25 | /// 26 | public static bool RangeCheck(int value, int maxValue) { 27 | return RangeCheck(value, 0, maxValue); 28 | } 29 | 30 | /// 31 | /// Returns true 32 | /// if is between 0 (inclusive) and (inclusive), 33 | /// and false otherwise. 34 | /// 35 | public static bool RangeCheckInclusive(int value, int maxValue) { 36 | return RangeCheckInclusive(value, 0, maxValue); 37 | } 38 | 39 | /// 40 | /// Returns true 41 | /// if {x,y} is between {x,y} (inclusive) and {x,y} (exclusive), 42 | /// and false otherwise. 43 | /// 44 | public static bool RangeCheck((int a, int b) value, (int a, int b) minValue, (int a, int b) maxValue) { 45 | return RangeCheck(value.a, minValue.a, maxValue.a) && RangeCheck(value.b, minValue.b, maxValue.b); 46 | } 47 | 48 | /// 49 | /// Returns true 50 | /// if {x,y} is between {x,y} (inclusive) and {x,y} (inclusive), 51 | /// and false otherwise. 52 | /// 53 | public static bool RangeCheckInclusive((int a, int b) value, (int a, int b) minValue, (int a, int b) maxValue) { 54 | return RangeCheckInclusive(value.a, minValue.a, maxValue.a) && RangeCheckInclusive(value.b, minValue.b, maxValue.b); 55 | } 56 | 57 | /// 58 | /// Returns true 59 | /// if {x,y} is between 0{x,y} (inclusive) and {x,y} (exclusive), 60 | /// and false otherwise. 61 | /// 62 | public static bool RangeCheck((int a, int b) value, (int a, int b) maxValue) { 63 | return RangeCheck(value.a, 0, maxValue.a) && RangeCheck(value.b, 0, maxValue.b); 64 | } 65 | 66 | /// 67 | /// Returns true 68 | /// if {x,y} is between 0{x,y} (inclusive) and {x,y} (inclusive), 69 | /// and false otherwise. 70 | /// 71 | public static bool RangeCheckInclusive((int a, int b) value, (int a, int b) maxValue) { 72 | return RangeCheckInclusive(value.a, 0, maxValue.a) && RangeCheckInclusive(value.b, 0, maxValue.b); 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /Runtime/Utilities/RangeUtils.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 99a9f2e3a2cb4a87aa565554c6f3e049 3 | timeCreated: 1613209200 -------------------------------------------------------------------------------- /Runtime/Utilities/Run.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Concurrent; 4 | using System.Collections.Generic; 5 | using JetBrains.Annotations; 6 | using UnityEngine; 7 | 8 | namespace UnityCommons { 9 | public static class Run { 10 | /// 11 | /// Runs every updates/ticks. determines whether 12 | /// is ran in the Update, LateUpdate, or FixedUpdate loop 13 | /// 14 | /// An IDisposable which can be used to remove from updating by calling .Dispose() on it 15 | [MustUseReturnValue] public static IDisposable EveryTicks(int ticks, UpdateType updateType, Action action) { 16 | return RunUtilityUpdater.Instance.EveryTicks(ticks, action, updateType); 17 | } 18 | 19 | /// 20 | /// Runs every updates/ticks. 21 | /// 22 | /// An IDisposable which can be used to remove from updating by calling .Dispose() on it 23 | [MustUseReturnValue] public static IDisposable EveryTicks(int ticks, Action action) { 24 | return RunUtilityUpdater.Instance.EveryTicks(ticks, action, UpdateType.Normal); 25 | } 26 | 27 | /// 28 | /// Runs every frame. determines whether 29 | /// is ran in the Update, LateUpdate, or FixedUpdate loop 30 | /// 31 | /// An IDisposable which can be used to remove from updating by calling .Dispose() on it 32 | [MustUseReturnValue] public static IDisposable EveryFrame(UpdateType updateType, Action action) { 33 | return RunUtilityUpdater.Instance.EveryFrame(action, updateType); 34 | } 35 | 36 | /// 37 | /// Runs every Update loop (every frame). 38 | /// 39 | /// An IDisposable which can be used to remove from updating by calling .Dispose() on it 40 | [MustUseReturnValue] public static IDisposable EveryFrame(Action action) { 41 | return RunUtilityUpdater.Instance.EveryFrame(action, UpdateType.Normal); 42 | } 43 | 44 | /// 45 | /// Runs every seconds, with an initial delay of 0 seconds. 46 | /// 47 | /// An IDisposable which can be used to remove from being run by calling .Dispose() on it 48 | [MustUseReturnValue] public static IDisposable Every(float rate, Action action) { 49 | return RunUtilityUpdater.Instance.Every(action, rate, 0); 50 | } 51 | 52 | /// 53 | /// Runs every seconds, with an initial delay of seconds. 54 | /// 55 | /// An IDisposable which can be used to remove from being run by calling .Dispose() on it 56 | [MustUseReturnValue] public static IDisposable Every(float rate, float initialDelay, Action action) { 57 | return RunUtilityUpdater.Instance.Every(action, rate, initialDelay); 58 | } 59 | 60 | /// 61 | /// Runs in the next frame. determines whether 62 | /// is ran in the Update, LateUpdate, or FixedUpdate loop 63 | /// 64 | public static void NextFrame(Action action, UpdateType updateType = UpdateType.Normal) { 65 | RunUtilityUpdater.Instance.NextFrame(action, updateType); 66 | } 67 | 68 | /// 69 | /// Runs after seconds. 70 | /// 71 | /// An IDisposable which can be used to cancel the call of by calling .Dispose() on it 72 | public static IDisposable After(float delay, Action action) { 73 | return RunUtilityUpdater.Instance.After(action, delay); 74 | } 75 | 76 | public static void EnsureInitialized() { 77 | RunUtilityUpdater.EnsureInitialized(); 78 | } 79 | 80 | private class RunUtilityUpdater : AutoMonoSingleton { 81 | private readonly List functions = new List(); 82 | private readonly ConcurrentDictionary nextUpdateFunctions = new ConcurrentDictionary(); 83 | private readonly Queue removeUpdate = new Queue(); 84 | private readonly Queue removeLate = new Queue(); 85 | private readonly Queue removeFixed = new Queue(); 86 | private readonly ConcurrentQueue<(int, Function)> removeNextUpdate = new ConcurrentQueue<(int, Function)>(); 87 | 88 | protected override void OnAwake() { 89 | gameObject.hideFlags = HideFlags.HideAndDontSave; 90 | } 91 | 92 | private void Update() { 93 | ClearQueue(removeUpdate); 94 | foreach (Function function in functions) { 95 | if (function.updateType != UpdateType.Normal) continue; 96 | if (function is TickFunction tickFunction) { 97 | if (Time.frameCount % tickFunction.ticks != 0) continue; 98 | } 99 | 100 | function.action?.Invoke(); 101 | } 102 | ClearQueue(removeUpdate); 103 | 104 | foreach (KeyValuePair kvp in nextUpdateFunctions) { 105 | (int key, Function value) = (kvp.Key, kvp.Value); 106 | if (value.updateType != UpdateType.Normal) continue; 107 | value.action?.Invoke(); 108 | removeNextUpdate.Enqueue((key, value)); 109 | } 110 | ClearQueue(removeNextUpdate); 111 | } 112 | 113 | private void LateUpdate() { 114 | ClearQueue(removeLate); 115 | foreach (Function function in functions) { 116 | if (function.updateType != UpdateType.Late) continue; 117 | if (function is TickFunction tickFunction) { 118 | if (Time.frameCount % tickFunction.ticks != 0) continue; 119 | } 120 | 121 | function.action?.Invoke(); 122 | } 123 | ClearQueue(removeLate); 124 | 125 | foreach (KeyValuePair kvp in nextUpdateFunctions) { 126 | (int key, Function value) = (kvp.Key, kvp.Value); 127 | if (value.updateType != UpdateType.Late) continue; 128 | value.action?.Invoke(); 129 | removeNextUpdate.Enqueue((key, value)); 130 | } 131 | ClearQueue(removeNextUpdate); 132 | } 133 | 134 | private void FixedUpdate() { 135 | ClearQueue(removeFixed); 136 | foreach (Function function in functions) { 137 | if (function.updateType != UpdateType.Fixed) continue; 138 | if (function is TickFunction tickFunction) { 139 | int fixedFrameCount = Mathf.RoundToInt(Time.fixedTime / Time.fixedDeltaTime); 140 | if (fixedFrameCount % tickFunction.ticks != 0) continue; 141 | } 142 | 143 | function.action?.Invoke(); 144 | } 145 | ClearQueue(removeFixed); 146 | 147 | foreach (KeyValuePair kvp in nextUpdateFunctions) { 148 | (int key, Function value) = (kvp.Key, kvp.Value); 149 | if (value.updateType != UpdateType.Fixed) continue; 150 | value.action?.Invoke(); 151 | removeNextUpdate.Enqueue((key, value)); 152 | } 153 | ClearQueue(removeNextUpdate); 154 | } 155 | 156 | internal IDisposable EveryTicks(int ticks, Action action, UpdateType updateType) { 157 | TickFunction function = new TickFunction(ticks, action, updateType); 158 | functions.Add(function); 159 | return new FunctionDisposable(this, function); 160 | } 161 | 162 | internal IDisposable EveryFrame(Action action, UpdateType updateType) { 163 | Function function = new Function(action, updateType); 164 | functions.Add(function); 165 | return new FunctionDisposable(this, function); 166 | } 167 | 168 | internal IDisposable Every(Action action, float rate, float initialDelay) { 169 | return new CoroutineDisposable(this, StartCoroutine(Runner(action, rate, initialDelay))); 170 | } 171 | 172 | internal IDisposable After(Action action, float delay) { 173 | return new CoroutineDisposable(this, StartCoroutine(Delayer(action, delay))); 174 | } 175 | 176 | internal void NextFrame(Action action, UpdateType updateType) { 177 | if (!nextUpdateFunctions.TryAdd(nextUpdateFunctions.Count, new Function(action, updateType))) { 178 | Debug.LogWarning("Failed to add function to next update queue"); 179 | } 180 | } 181 | 182 | private IEnumerator Runner(Action action, float rate, float initialDelay) { 183 | yield return new WaitForSeconds(initialDelay); 184 | 185 | while (true) { 186 | yield return null; 187 | action?.Invoke(); 188 | yield return new WaitForSeconds(rate); 189 | } 190 | } 191 | 192 | private IEnumerator Delayer(Action action, float delay) { 193 | yield return new WaitForSeconds(delay); 194 | yield return null; 195 | action?.Invoke(); 196 | } 197 | 198 | private void ClearQueue(Queue queue) { 199 | while (queue.Count > 0) { 200 | Function func = queue.Dequeue(); 201 | functions.Remove(func); 202 | } 203 | } 204 | 205 | internal void ClearQueue(ConcurrentQueue<(int, Function)> queue) { 206 | while (queue.Count > 0) { 207 | if (queue.TryDequeue(out (int, Function) pair)) { 208 | if (!nextUpdateFunctions.TryRemove(pair.Item1, out _)) { 209 | Debug.LogWarning("Failed to remove function from next update queue"); 210 | } 211 | } 212 | } 213 | } 214 | 215 | internal void QueueFree(Function function) { 216 | switch (function.updateType) { 217 | case UpdateType.Normal: 218 | removeUpdate.Enqueue(function); 219 | break; 220 | case UpdateType.Late: 221 | removeLate.Enqueue(function); 222 | break; 223 | case UpdateType.Fixed: 224 | removeFixed.Enqueue(function); 225 | break; 226 | default: 227 | throw new ArgumentOutOfRangeException(); 228 | } 229 | } 230 | } 231 | 232 | private class CoroutineDisposable : IDisposable { 233 | private readonly RunUtilityUpdater owner; 234 | private readonly Coroutine coroutine; 235 | private bool disposed; 236 | 237 | public CoroutineDisposable(RunUtilityUpdater owner, Coroutine coroutine) { 238 | this.owner = owner; 239 | this.coroutine = coroutine; 240 | } 241 | 242 | public void Dispose() { 243 | if (disposed) return; 244 | 245 | owner.StopCoroutine(coroutine); 246 | disposed = true; 247 | } 248 | } 249 | 250 | private class FunctionDisposable : IDisposable { 251 | private readonly RunUtilityUpdater owner; 252 | private readonly Function function; 253 | private bool disposed; 254 | 255 | public FunctionDisposable(RunUtilityUpdater owner, Function function) { 256 | this.owner = owner; 257 | this.function = function; 258 | } 259 | 260 | public void Dispose() { 261 | if (disposed) return; 262 | 263 | owner.QueueFree(function); 264 | disposed = true; 265 | } 266 | } 267 | 268 | private class Function { 269 | // ReSharper disable once InconsistentNaming 270 | internal readonly Action action; 271 | // ReSharper disable once InconsistentNaming 272 | internal readonly UpdateType updateType; 273 | 274 | public Function(Action action, UpdateType updateType) { 275 | this.action = action; 276 | this.updateType = updateType; 277 | } 278 | } 279 | 280 | private class TickFunction : Function { 281 | // ReSharper disable once InconsistentNaming 282 | internal readonly int ticks; 283 | 284 | public TickFunction(int ticks, Action action, UpdateType updateType) : base(action, updateType) { 285 | this.ticks = ticks; 286 | } 287 | } 288 | 289 | public enum UpdateType { 290 | Normal, 291 | Late, 292 | Fixed 293 | } 294 | } 295 | } -------------------------------------------------------------------------------- /Runtime/Utilities/Run.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5d07fd64d43c4870b2cc5e2475b9dc00 3 | timeCreated: 1613925928 -------------------------------------------------------------------------------- /Runtime/Utilities/UnityUtils.cs: -------------------------------------------------------------------------------- 1 | using TMPro; 2 | using UnityEngine; 3 | 4 | namespace UnityCommons { 5 | public static partial class Utils { 6 | /// 7 | /// Returns the world position of the mouse in screen position 8 | /// using Camera.main as the active camera, with z = 0. 9 | /// 10 | public static Vector3 MouseWorld_NoZ() { 11 | Vector3 world = MouseWorld(); 12 | world.z = 0; 13 | return world; 14 | } 15 | 16 | /// 17 | /// Returns the world position of the mouse in screen position 18 | /// using Camera.main as the active camera, with y = 0. 19 | /// 20 | public static Vector3 MouseWorld_NoY() { 21 | Vector3 world = MouseWorld(); 22 | world.y = 0; 23 | return world; 24 | } 25 | 26 | /// 27 | /// Returns the world position of the mouse in screen position 28 | /// using Camera.main as the active camera. 29 | /// 30 | public static Vector3 MouseWorld() { 31 | return ScreenToWorld(Input.mousePosition, Camera.main); 32 | } 33 | 34 | /// 35 | /// Returns the world position of the mouse in screen position 36 | /// using as the active camera. 37 | /// 38 | public static Vector3 MouseWorld(Camera camera) { 39 | return ScreenToWorld(Input.mousePosition, camera); 40 | } 41 | 42 | /// 43 | /// Returns the world position of the mouse in screen position using as the 44 | /// active camera, and as the layer mask. 45 | /// 46 | public static Vector3 MouseWorld(Camera camera, int layerMask) { 47 | return ScreenToWorld(Input.mousePosition, camera, layerMask); 48 | } 49 | 50 | /// 51 | /// Returns the world position of 52 | /// using Camera.main as the active camera. 53 | /// 54 | public static Vector3 ScreenToWorld(Vector3 screenPoint) { 55 | return ScreenToWorld(screenPoint, Camera.main); 56 | } 57 | 58 | /// 59 | /// Returns the world position of 60 | /// using as the active camera. 61 | /// 62 | public static Vector3 ScreenToWorld(Vector3 screenPoint, Camera camera) { 63 | Ray ray = camera.ScreenPointToRay(screenPoint); 64 | return Physics.Raycast(ray, out RaycastHit info, 10000f) ? info.point : Vector3.zero; 65 | } 66 | 67 | /// 68 | /// Returns the world position of using 69 | /// as the active camera, and as a layer mask. 70 | /// 71 | public static Vector3 ScreenToWorld(Vector3 screenPoint, Camera camera, int layerMask) { 72 | Ray ray = camera.ScreenPointToRay(screenPoint); 73 | return Physics.Raycast(ray, out RaycastHit info, 10000f, layerMask) ? info.point : Vector3.zero; 74 | } 75 | 76 | /// 77 | /// Returns the world position in 2D of Input.mousePosition with y = 0, 78 | /// using Camera.main as the active camera. 79 | /// 80 | public static Vector3 MouseWorld2D_NoY() { 81 | Vector3 position = MouseWorld2D(); 82 | position.y = 0; 83 | return position; 84 | } 85 | 86 | /// 87 | /// Returns the world position in 2D of Input.mousePosition with z = 0, 88 | /// using Camera.main as the active camera. 89 | /// 90 | public static Vector3 MouseWorld2D_NoZ() { 91 | Vector3 position = MouseWorld2D(); 92 | position.z = 0; 93 | return position; 94 | } 95 | 96 | /// 97 | /// Returns the world position in 2D of Input.mousePosition 98 | /// using Camera.main as the active camera. 99 | /// 100 | public static Vector3 MouseWorld2D() { 101 | return ScreenToWorld2D(Input.mousePosition, Camera.main); 102 | } 103 | 104 | /// 105 | /// Returns the world position in 2D of Input.mousePosition 106 | /// using as the active camera. 107 | /// 108 | public static Vector3 MouseWorld2D(Camera camera) { 109 | return ScreenToWorld2D(Input.mousePosition, camera); 110 | } 111 | 112 | /// 113 | /// Returns the world position in 2D of 114 | /// using Camera.main as the active camera. 115 | /// 116 | public static Vector3 ScreenToWorld2D(Vector3 screenPoint) { 117 | return ScreenToWorld2D(screenPoint, Camera.main); 118 | } 119 | 120 | /// 121 | /// Returns the world position in 2D of 122 | /// using as the active camera. 123 | /// 124 | public static Vector3 ScreenToWorld2D(Vector3 screenPoint, Camera camera) { 125 | return camera.ScreenToWorldPoint(screenPoint); 126 | } 127 | 128 | /// 129 | /// Creates a 3D text (TextMeshPro) object using the specified parameters. 130 | /// 131 | /// The 3D text instance 132 | public static TextMeshPro CreateWorldText(string text, Transform parent = null, Vector3 position = default, Quaternion? rotation = null, int fontSize = 32, 133 | Color? color = null, 134 | HorizontalAlignmentOptions horizontalAlignment = HorizontalAlignmentOptions.Left, 135 | VerticalAlignmentOptions verticalAlignment = VerticalAlignmentOptions.Top, int sortingOrder = 1000) { 136 | return CreateWorldText(text, parent, position, rotation ?? Quaternion.identity, fontSize, color ?? Color.white, horizontalAlignment, verticalAlignment, sortingOrder); 137 | } 138 | 139 | /// 140 | /// Creates a 3D text (TextMeshPro) object using the specified parameters. 141 | /// 142 | /// The 3D text instance 143 | public static TextMeshPro CreateWorldText(string text, Transform parent, Vector3 position, Quaternion rotation, int fontSize, Color color, 144 | HorizontalAlignmentOptions horizontalAlignment, VerticalAlignmentOptions verticalAlignment, int sortingOrder) { 145 | TextMeshPro textMesh = new GameObject("WorldText", typeof(TextMeshPro)).GetComponent(); 146 | textMesh.text = text; 147 | textMesh.fontSize = fontSize; 148 | textMesh.color = color; 149 | textMesh.horizontalAlignment = horizontalAlignment; 150 | textMesh.verticalAlignment = verticalAlignment; 151 | textMesh.sortingOrder = sortingOrder; 152 | 153 | textMesh.transform.SetParent(parent, false); 154 | textMesh.transform.localPosition = position; 155 | textMesh.transform.localRotation = rotation; 156 | 157 | return textMesh; 158 | } 159 | } 160 | } -------------------------------------------------------------------------------- /Runtime/Utilities/UnityUtils.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: af2cab2c3865456da228398b04f7771c 3 | timeCreated: 1613210591 -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dev.vecerdi.unitycommons", 3 | "version": "2.1.5", 4 | "displayName": "Unity Commons", 5 | "description": "Library of project-agnostic & reusable scripts.", 6 | "license": "MIT", 7 | "unity": "2019.4", 8 | "type": "tool", 9 | "keywords": [ 10 | "scripts", 11 | "library", 12 | "common", 13 | "utility" 14 | ], 15 | "author": { 16 | "name": "Teodor Vecerdi", 17 | "email": "teodor.vecerdi@gmail.com", 18 | "url": "https://github.com/TeodorVecerdi" 19 | }, 20 | "dependencies": { 21 | }, 22 | "repository": { 23 | "url": "https://github.com/TeodorVecerdi/UnityCommons.git", 24 | "type": "git" 25 | } 26 | } -------------------------------------------------------------------------------- /package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f2b4fbe4c0eb0e74697e83c73da9871b 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | --------------------------------------------------------------------------------