├── Countdown.meta ├── Countdown ├── Countdown.cs ├── Countdown.cs.meta ├── CountdownProperty.cs ├── CountdownProperty.cs.meta ├── CountdownPropertyLoop.cs ├── CountdownPropertyLoop.cs.meta ├── CountdownPropertyOnce.cs ├── CountdownPropertyOnce.cs.meta ├── Editor.meta ├── Editor │ ├── CountdownLoopPropertyDrawer.cs │ ├── CountdownLoopPropertyDrawer.cs.meta │ ├── CountdownOncePropertyDrawer.cs │ ├── CountdownOncePropertyDrawer.cs.meta │ ├── CountdownPropertyDrawerBase.cs │ └── CountdownPropertyDrawerBase.cs.meta ├── README.md └── README.md.meta ├── Doxyfile.dox ├── Doxyfile.dox.meta ├── EditorHelper.meta ├── EditorHelper ├── Editor.meta ├── Editor │ ├── EditorHelper.cs │ └── EditorHelper.cs.meta ├── README.md └── README.md.meta ├── Examples.meta ├── Examples ├── CountdownExample.cs ├── CountdownExample.cs.meta ├── CountdownPropertyExample.cs ├── CountdownPropertyExample.cs.meta ├── LINQExtensionsExamples.cs ├── LINQExtensionsExamples.cs.meta ├── MathHelperExamples.cs ├── MathHelperExamples.cs.meta ├── MeshCreatorExample.cs ├── MeshCreatorExample.cs.meta ├── NoiseOutputValueExample.cs ├── NoiseOutputValueExample.cs.meta ├── RandomBagExample.cs ├── RandomBagExample.cs.meta ├── RangeExample.cs ├── RangeExample.cs.meta ├── RollingArrayExample.cs ├── RollingArrayExample.cs.meta ├── SingletonEntityManager.cs ├── SingletonEntityManager.cs.meta ├── SingletonMusicManager.cs ├── SingletonMusicManager.cs.meta ├── UnityHelperExamples.cs ├── UnityHelperExamples.cs.meta ├── XmlHelperExample.cs └── XmlHelperExample.cs.meta ├── LICENSE ├── LICENSE.meta ├── LINQExtensions.meta ├── LINQExtensions ├── LINQExtensions.cs ├── LINQExtensions.cs.meta ├── README.md └── README.md.meta ├── MathHelper.meta ├── MathHelper ├── MathHelper.cs ├── MathHelper.cs.meta ├── README.md └── README.md.meta ├── MeshCreator.meta ├── MeshCreator ├── MeshCreator.cs ├── MeshCreator.cs.meta ├── MeshTriangle.cs ├── MeshTriangle.cs.meta ├── MeshVertex.cs ├── MeshVertex.cs.meta ├── README.md └── README.md.meta ├── NoiseOutputValue.meta ├── NoiseOutputValue ├── NoiseOutputValue.cs ├── NoiseOutputValue.cs.meta ├── README.md └── README.md.meta ├── README.md ├── README.md.meta ├── RandomBag.meta ├── RandomBag ├── README.md ├── README.md.meta ├── RandomBag.cs └── RandomBag.cs.meta ├── Range.meta ├── Range ├── Editor.meta ├── Editor │ ├── RangeFloatPropertyDrawer.cs │ ├── RangeFloatPropertyDrawer.cs.meta │ ├── RangeIntPropertyDrawer.cs │ ├── RangeIntPropertyDrawer.cs.meta │ ├── RangePropertyDrawerBase.cs │ └── RangePropertyDrawerBase.cs.meta ├── README.md ├── README.md.meta ├── RangeFloat.cs ├── RangeFloat.cs.meta ├── RangeInt.cs └── RangeInt.cs.meta ├── RollingArray.meta ├── RollingArray ├── README.md ├── README.md.meta ├── RollingArray.cs └── RollingArray.cs.meta ├── Singleton.meta ├── Singleton ├── PersistentSingletonMonoBehaviour.cs ├── PersistentSingletonMonoBehaviour.cs.meta ├── README.md ├── README.md.meta ├── SelfCreatingSingletonMonoBehaviour.cs ├── SelfCreatingSingletonMonoBehaviour.cs.meta ├── SingletonMonoBehaviour.cs └── SingletonMonoBehaviour.cs.meta ├── UnityHelper.meta ├── UnityHelper ├── README.md ├── README.md.meta ├── UnityHelper.cs └── UnityHelper.cs.meta ├── XmlHelper.meta ├── XmlHelper ├── README.md ├── README.md.meta ├── XmlHelper.cs └── XmlHelper.cs.meta ├── _Images.meta └── _Images ├── CountdownPropertyExample.png ├── CountdownPropertyExample.png.meta ├── EasedLerpFactorExample.gif ├── EasedLerpFactorExample.gif.meta ├── NoiseOutputValueExample.gif ├── NoiseOutputValueExample.gif.meta ├── RangeExample.png └── RangeExample.png.meta /Countdown.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1f118e99765cc44489971355083bddb8 3 | folderAsset: yes 4 | timeCreated: 1462732529 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Countdown/Countdown.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | namespace UnityUtilities 5 | { 6 | /// 7 | /// A handy countdown class providing a lot of convenience methods. 8 | /// 9 | public class Countdown 10 | { 11 | /// 12 | /// The time left at the current countdown. 13 | /// 14 | public float TimeLeft { get; set; } 15 | 16 | /// 17 | /// The initial duration set. 18 | /// 19 | public float Duration { get; private set; } 20 | 21 | /// 22 | /// A delegate for automatically providing a new duration 23 | /// when the time left runs out. 24 | /// 25 | public Func CalculateDurationDelegate { get; set; } 26 | 27 | /// 28 | /// Should the countdown automatically loop when it reaches zero? 29 | /// 30 | public bool Loop { get; set; } 31 | 32 | /// 33 | /// Creates a new countdown with no TotalTime duration set yet. 34 | /// Should the countdown loop automatically? 35 | /// 36 | public Countdown(bool loop) : this(loop, 0f) 37 | { 38 | } 39 | 40 | /// 41 | /// Creates a new countdown, starting at . 42 | /// 43 | /// Should the countdown loop automatically? 44 | /// The starting time to count down from. 45 | public Countdown(bool loop, float startDuration) 46 | { 47 | Loop = loop; 48 | TimeLeft = startDuration; 49 | Duration = TimeLeft; 50 | CalculateDurationDelegate = null; 51 | } 52 | 53 | /// 54 | /// Creates a new countdown with a delegate specifying how long it should take. 55 | /// 56 | /// Should the countdown loop automatically? 57 | /// A delegate providing the value to count down from. 58 | /// Optional: A one-time starting duration. Afterwards the is used. 59 | public Countdown(bool loop, Func calculateDurationDelegate, float? startDuration = null) 60 | { 61 | Loop = loop; 62 | 63 | // If there is no starting value, ask the delegate for one. 64 | if (startDuration == null) 65 | startDuration = calculateDurationDelegate(); 66 | 67 | TimeLeft = startDuration.Value; 68 | Duration = startDuration.Value; 69 | CalculateDurationDelegate = calculateDurationDelegate; 70 | } 71 | 72 | /// 73 | /// Progresses the countdown by (or Time.deltaTime). Returns true if the countdown 74 | /// it reached zero this frame. If the countdown is already at zero when this is called and 75 | /// is true, it is restarted automatically. 76 | /// 77 | /// Optional: A deltaTime to use instead of Time.deltaTime. 78 | /// True if the countdown reached zero this frame. False if it is still going or was already zero and isn't looping. 79 | public bool Progress(float? deltaTime = null) 80 | { 81 | // If no deltaTime is supplied, use Time.deltaTime 82 | if (deltaTime == null) 83 | deltaTime = Time.deltaTime; 84 | 85 | // If the countdown is already over, loop 86 | if (ReachedZero) 87 | { 88 | // ...unless we don't want to loop 89 | if (!Loop) 90 | return false; 91 | 92 | // Refill the time. This might change the duration if we use a CalculateDurationDelegate. 93 | Refill(); 94 | 95 | // If the duration is zero or less, this is stopped. (A CalculateDurationDelegate in 96 | // Refill() might have changed that, which is why we check it here and not earlier.) 97 | if (Duration <= 0) 98 | return false; 99 | } 100 | 101 | // Progress the countdown 102 | TimeLeft -= deltaTime.Value; 103 | 104 | // Returns if the countdown reached zero 105 | return ReachedZero; 106 | } 107 | 108 | /// 109 | /// Resets the countdown to TotalTime. If a is supplied, 110 | /// a new TotalTime is picked first. 111 | /// 112 | public void Reset() 113 | { 114 | if (CalculateDurationDelegate != null) 115 | { 116 | Duration = CalculateDurationDelegate(); 117 | } 118 | 119 | TimeLeft = Duration; 120 | } 121 | 122 | /// 123 | /// Sets the and the . 124 | /// 125 | /// The time to set TotalTime and TimeLeft to. 126 | public void Reset(float timeLeft) 127 | { 128 | TimeLeft = timeLeft; 129 | Duration = timeLeft; 130 | } 131 | 132 | /// 133 | /// Loops the countdown by adding TotalTime to TimeLeft. This should only be called when is already zero 134 | /// or less. If a is supplied, a new TotalTime is picked first. 135 | /// 136 | /// The difference between Reset() and Loop() is that Loop() factors in when is already less than zero 137 | /// and subtracts it from the TotalTime. 138 | /// 139 | public void Refill() 140 | { 141 | if (CalculateDurationDelegate != null) 142 | { 143 | Refill(CalculateDurationDelegate()); 144 | } 145 | else 146 | { 147 | Refill(Duration); 148 | } 149 | } 150 | 151 | /// 152 | /// Loops the countdown by settings TotalTime to and then adds it to . 153 | /// 154 | /// The difference between Reset() and Loop() is that Loop() factors in when is already less than zero 155 | /// and subtracts it from the TotalTime. 156 | /// 157 | /// The time to set TotalTime to and to add to TimeLeft. 158 | public void Refill(float newTotalTimeLeft) 159 | { 160 | TimeLeft += newTotalTimeLeft; 161 | Duration = newTotalTimeLeft; 162 | } 163 | 164 | /// 165 | /// Returns true when the countdown reached zero (or less). 166 | /// 167 | public bool ReachedZero 168 | { 169 | get { return TimeLeft <= 0; } 170 | } 171 | 172 | /// 173 | /// Returns true if the countdown is currently over zero. 174 | /// 175 | public bool IsRunning 176 | { 177 | get { return TimeLeft > 0; } 178 | } 179 | 180 | /// 181 | /// Returns the time passed for this countdown: (TotalTime-TimeLeft). 182 | /// 183 | public float TimePassed 184 | { 185 | get { return Duration - TimeLeft; } 186 | } 187 | 188 | /// 189 | /// Returns 0 when the countdown is just starting and 1 when it is elapsed. 190 | /// If TotalTime is 0, this returns 1. 191 | /// 192 | public float PercentElapsed 193 | { 194 | get { return 1 - PercentLeft; } 195 | } 196 | 197 | /// 198 | /// Returns 1 when the countdown is just starting and 0 when it is elapsed. 199 | /// If TotalTime is 0, this returns 0. 200 | /// 201 | public float PercentLeft 202 | { 203 | get 204 | { 205 | if (Duration == 0) 206 | return 0f; 207 | 208 | return Mathf.Clamp01(TimeLeft / Duration); 209 | } 210 | } 211 | } 212 | } -------------------------------------------------------------------------------- /Countdown/Countdown.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2ab57da72e2f287418bdf88873d67111 3 | timeCreated: 1462732529 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Countdown/CountdownProperty.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | namespace UnityUtilities 5 | { 6 | /// 7 | /// A base version for properties based on . 8 | /// 9 | [Serializable] 10 | public class CountdownProperty : Countdown, ISerializationCallbackReceiver 11 | { 12 | /// 13 | /// The total time to be set. Refreshed at each loop to allow for live editing. 14 | /// 15 | [SerializeField] float time; 16 | 17 | bool initialized; 18 | 19 | /// 20 | /// Empty constructor for the editor. 21 | /// 22 | public CountdownProperty(bool loop) : base(loop) 23 | { 24 | CalculateDurationDelegate = GetTimeProperty; 25 | } 26 | 27 | /// 28 | /// The method used to set the total time each loop. 29 | /// 30 | /// The value of the field. 31 | float GetTimeProperty() 32 | { 33 | return time; 34 | } 35 | 36 | /// 37 | /// Called before the serialization (reading the editor value) of the "time" property happens. 38 | /// It's empty and just here because needs it. 39 | /// 40 | public void OnBeforeSerialize() 41 | { 42 | } 43 | 44 | /// 45 | /// Called after the serialization (reading the editor value) of the "time" property happens. 46 | /// If the timer is not initialized yet, it will be here. 47 | /// 48 | public void OnAfterDeserialize() 49 | { 50 | if (initialized) 51 | return; 52 | 53 | initialized = true; 54 | Reset(); 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /Countdown/CountdownProperty.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 52154614c7e358d4abe708a4fe27908a 3 | timeCreated: 1462737279 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Countdown/CountdownPropertyLoop.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UnityUtilities 4 | { 5 | /// 6 | /// A version of that can be set in the Editor. 7 | /// Refreshes the total time on each loop to allow for live editing. Loops. 8 | /// 9 | [Serializable] 10 | public class CountdownPropertyLoop : CountdownProperty 11 | { 12 | /// 13 | /// Empty constructor for the editor. 14 | /// 15 | public CountdownPropertyLoop() : base(true) 16 | { 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Countdown/CountdownPropertyLoop.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6b8459496fb6a2a4b9763696e3f271e9 3 | timeCreated: 1462735192 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Countdown/CountdownPropertyOnce.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UnityUtilities 4 | { 5 | /// 6 | /// A version of that can be set in the Editor. 7 | /// Just called once, but refreshes the total time on each call to . 8 | /// 9 | [Serializable] 10 | public class CountdownPropertyOnce : CountdownProperty 11 | { 12 | /// 13 | /// Empty constructor for the editor. 14 | /// 15 | public CountdownPropertyOnce() : base(false) 16 | { 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Countdown/CountdownPropertyOnce.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 74dbb9fefcad3174199e29b4dabd37d0 3 | timeCreated: 1462735192 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Countdown/Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 52f73aee8c49930469dedb0acdd35685 3 | folderAsset: yes 4 | timeCreated: 1462732529 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Countdown/Editor/CountdownLoopPropertyDrawer.cs: -------------------------------------------------------------------------------- 1 | using UnityEditor; 2 | 3 | namespace UnityUtilities 4 | { 5 | [CustomPropertyDrawer(typeof (CountdownPropertyLoop))] 6 | public class CountdownLoopPropertyDrawer : CountdownPropertyDrawerBase 7 | { 8 | } 9 | } -------------------------------------------------------------------------------- /Countdown/Editor/CountdownLoopPropertyDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: da409ad08412e904f9f429e4810083c3 3 | timeCreated: 1462735312 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Countdown/Editor/CountdownOncePropertyDrawer.cs: -------------------------------------------------------------------------------- 1 | using UnityEditor; 2 | 3 | namespace UnityUtilities 4 | { 5 | [CustomPropertyDrawer(typeof (CountdownPropertyOnce))] 6 | public class CountdownOncePropertyDrawer : CountdownPropertyDrawerBase 7 | { 8 | } 9 | } -------------------------------------------------------------------------------- /Countdown/Editor/CountdownOncePropertyDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c2dc4b255e32c4c4dac9cc7ebe0cc5d8 3 | timeCreated: 1462737346 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Countdown/Editor/CountdownPropertyDrawerBase.cs: -------------------------------------------------------------------------------- 1 | using UnityEditor; 2 | using UnityEngine; 3 | 4 | namespace UnityUtilities 5 | { 6 | public class CountdownPropertyDrawerBase : PropertyDrawer 7 | { 8 | public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) 9 | { 10 | // Using BeginProperty / EndProperty on the parent property means that 11 | // prefab override logic works on the entire property. 12 | EditorGUI.BeginProperty(position, label, property); 13 | 14 | // Set tooltip, if any 15 | label.tooltip = EditorHelper.GetTooltip(fieldInfo); 16 | 17 | // Draw label 18 | position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label); 19 | 20 | // Don't make child field be indented 21 | var indent = EditorGUI.indentLevel; 22 | EditorGUI.indentLevel = 0; 23 | 24 | // Draw the time field - passs GUIContent.none to each so they are drawn without labels 25 | EditorGUI.PropertyField(position, property.FindPropertyRelative("time"), GUIContent.none); 26 | 27 | // Set indent back to what it was 28 | EditorGUI.indentLevel = indent; 29 | 30 | EditorGUI.EndProperty(); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /Countdown/Editor/CountdownPropertyDrawerBase.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1e89fc7efa522a44e85f2029e6f5f9c8 3 | timeCreated: 1462735312 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Countdown/README.md: -------------------------------------------------------------------------------- 1 | # Countdown 2 | 3 | This class is useful for things like cooldowns or spawn delays. It is also helpful for tweening things by using the `PercentElapsed` property. 4 | 5 | (If you want to tween a lot of stuff, you might want to check out [DOTween](http://dotween.demigiant.com). Actually, check it out either way, DOTween is fabulous!) 6 | 7 | ## Examples 8 | 9 | ### CountdownExample 10 | 11 | ```C# 12 | public class CountdownExample : MonoBehaviour 13 | { 14 | Countdown countdownLoop; 15 | Countdown countdownRandom; 16 | Countdown countdownOnce; 17 | Countdown countdownDelay; 18 | 19 | void Awake() 20 | { 21 | // Looping countdown, every 2 seconds 22 | countdownLoop = new Countdown(true, 2f); 23 | 24 | // Looping countdown, delegate is called every loop to determine duration (1 to 5 seconds) 25 | countdownRandom = new Countdown(true, () => UnityEngine.Random.Range(1f, 5f)); 26 | 27 | // Non-looping countdown, 5 seconds duration. Can be reset by calling Reset(). 28 | countdownOnce = new Countdown(false, 5f); 29 | 30 | // Non-looping countdown that isn't started yet. Can be started by calling e.g. Reset(5f). 31 | countdownDelay = new Countdown(false); 32 | } 33 | 34 | void Update() 35 | { 36 | // Looping automatically 37 | if (countdownLoop.Progress()) 38 | Debug.Log("This is logged every 2 seconds."); 39 | 40 | // Looping automatically. Duration is chosen between 1 and 5 every loop. 41 | if (countdownRandom.Progress()) 42 | Debug.Log("This is logged every 1 to 5 seconds."); 43 | 44 | // Not looping 45 | if (countdownOnce.Progress()) 46 | Debug.Log("This is shown once after 5 seconds."); 47 | 48 | // Start countdownDelay (again) - if it's already running, it is reset 49 | if (Input.GetKeyDown(KeyCode.Alpha1)) 50 | countdownDelay.Reset(1f); // 1 second 51 | 52 | if (Input.GetKeyDown(KeyCode.Alpha2)) 53 | countdownDelay.Reset(2f); // 2 seconds 54 | 55 | // Show this once 1/2 seconds after 1/2 was last pressed 56 | if (countdownDelay.Progress()) 57 | Debug.Log("This is shown after " + countdownDelay.Duration + " seconds."); 58 | 59 | // Output data about the countdown when space is pressed 60 | if (Input.GetKeyDown(KeyCode.Space)) 61 | { 62 | if (countdownDelay.IsRunning) 63 | Debug.Log("countdownDelay is " + (countdownDelay.PercentElapsed * 100) + "% complete."); 64 | else 65 | Debug.Log("countdownDelay is stopped."); 66 | } 67 | } 68 | ``` 69 | 70 | ### CountdownPropertyExample 71 | 72 | You can also use `CountdownPropertyOnce` and `CountdownPropertyLoop` to get the values directly from the 73 | editor. Live-editing works - if you change the value in the editor, it will be used in the next loop. 74 | 75 | ![CountdownPropertyExample Editor Screenshot](../_Images/CountdownPropertyExample.png) 76 | 77 | ```C# 78 | public class CountdownPropertyExample : MonoBehaviour 79 | { 80 | [SerializeField] CountdownPropertyOnce countdownOnce; 81 | [SerializeField] CountdownPropertyLoop countdownLoop; 82 | 83 | void Update() 84 | { 85 | // Looping automatically 86 | if (countdownLoop.Progress()) 87 | Debug.Log("This is logged every " + countdownLoop.Duration + " seconds."); 88 | 89 | // Not looping 90 | if (countdownOnce.Progress()) 91 | Debug.Log("This is shown once after " + countdownOnce.Duration + " seconds."); 92 | 93 | // Start countdownOnce again - if it's already running, it is reset 94 | if (Input.GetKeyDown(KeyCode.Space)) 95 | countdownOnce.Reset(); 96 | } 97 | } 98 | ``` 99 | 100 | ## Dependencies 101 | 102 | None. -------------------------------------------------------------------------------- /Countdown/README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a6049e8646652634d99337ce6717016e 3 | timeCreated: 1462733353 4 | licenseType: Free 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Doxyfile.dox.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 09d4a4db002008149aa44ec7c00ac54a 3 | timeCreated: 1462828878 4 | licenseType: Free 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /EditorHelper.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 30705395a9275334a8a69a8b7eae8a19 3 | folderAsset: yes 4 | timeCreated: 1465150348 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /EditorHelper/Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d072766fbad5a6747a8ced474a2a0210 3 | folderAsset: yes 4 | timeCreated: 1465151252 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /EditorHelper/Editor/EditorHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using UnityEngine; 3 | 4 | namespace UnityUtilities 5 | { 6 | /// 7 | /// Provides static helper methods for editor classes. 8 | /// 9 | public static class EditorHelper 10 | { 11 | /// 12 | /// Gets the tooltip of a field. 13 | /// 14 | /// The field to get the tooltip from. 15 | /// The tooltip if set, else an empty string. 16 | public static string GetTooltip(FieldInfo field) 17 | { 18 | var attributes = (TooltipAttribute[]) field.GetCustomAttributes(typeof (TooltipAttribute), true); 19 | return (attributes.Length > 0) 20 | ? attributes[0].tooltip 21 | : ""; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /EditorHelper/Editor/EditorHelper.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c229a6b2549c99445b3022e1232242b8 3 | timeCreated: 1465150348 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /EditorHelper/README.md: -------------------------------------------------------------------------------- 1 | # EditorHelper 2 | 3 | Contains a method for getting the `[Tooltip]` attribute content of fields for editor classes. I might add more helper methods in the future. 4 | 5 | ## Example 6 | 7 | ```C# 8 | public class RangePropertyDrawerBase : PropertyDrawer 9 | { 10 | public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) 11 | { 12 | // Using BeginProperty / EndProperty on the parent property means that 13 | // prefab override logic works on the entire property. 14 | EditorGUI.BeginProperty(position, label, property); 15 | 16 | // Set tooltip, if any 17 | label.tooltip = EditorHelper.GetTooltip(fieldInfo); 18 | 19 | // Draw label 20 | position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label); 21 | 22 | // Draw the GUI 23 | // [...] 24 | 25 | EditorGUI.EndProperty(); 26 | } 27 | } 28 | ``` 29 | 30 | ## Dependencies 31 | 32 | None. -------------------------------------------------------------------------------- /EditorHelper/README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5ffee59e65410ae48a58fb0973a32369 3 | timeCreated: 1465151238 4 | licenseType: Pro 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Examples.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6ba4d0c59e672b8488efffd8c25d7aba 3 | folderAsset: yes 4 | timeCreated: 1462735836 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Examples/CountdownExample.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace UnityUtilities.Examples 4 | { 5 | public class CountdownExample : MonoBehaviour 6 | { 7 | Countdown countdownLoop; 8 | Countdown countdownRandom; 9 | Countdown countdownOnce; 10 | Countdown countdownDelay; 11 | 12 | void Awake() 13 | { 14 | // Looping countdown, every 2 seconds 15 | countdownLoop = new Countdown(true, 2f); 16 | 17 | // Looping countdown, delegate is called every loop to determine duration (1 to 5 seconds) 18 | countdownRandom = new Countdown(true, () => UnityEngine.Random.Range(1f, 5f)); 19 | 20 | // Non-looping countdown, 5 seconds duration. Can be reset by calling Reset(). 21 | countdownOnce = new Countdown(false, 5f); 22 | 23 | // Non-looping countdown that isn't started yet. Can be started by calling e.g. Reset(5f). 24 | countdownDelay = new Countdown(false); 25 | } 26 | 27 | void Update() 28 | { 29 | // Looping automatically 30 | if (countdownLoop.Progress()) 31 | Debug.Log("This is logged every 2 seconds."); 32 | 33 | // Looping automatically. Duration is chosen between 1 and 5 every loop. 34 | if (countdownRandom.Progress()) 35 | Debug.Log("This is logged every 1 to 5 seconds."); 36 | 37 | // Not looping 38 | if (countdownOnce.Progress()) 39 | Debug.Log("This is shown once after 5 seconds."); 40 | 41 | // Start countdownDelay (again) - if it's already running, it is reset 42 | if (Input.GetKeyDown(KeyCode.Alpha1)) 43 | countdownDelay.Reset(1f); // 1 second 44 | 45 | if (Input.GetKeyDown(KeyCode.Alpha2)) 46 | countdownDelay.Reset(2f); // 2 seconds 47 | 48 | // Show this once 1/2 seconds after 1/2 was last pressed 49 | if (countdownDelay.Progress()) 50 | Debug.Log("This is shown after " + countdownDelay.Duration + " seconds."); 51 | 52 | // Output data about the countdown when space is pressed 53 | if (Input.GetKeyDown(KeyCode.Space)) 54 | { 55 | if (countdownDelay.IsRunning) 56 | Debug.Log("countdownDelay is " + Mathf.FloorToInt(countdownDelay.PercentElapsed * 100) + "% complete."); 57 | else 58 | Debug.Log("countdownDelay is stopped."); 59 | } 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /Examples/CountdownExample.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2e65fcbdbbfca7a41aa8f9a6b28333ec 3 | timeCreated: 1462734965 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Examples/CountdownPropertyExample.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace UnityUtilities.Examples 4 | { 5 | public class CountdownPropertyExample : MonoBehaviour 6 | { 7 | [Tooltip("Test")] 8 | [SerializeField] CountdownPropertyOnce countdownOnce; 9 | [SerializeField] CountdownPropertyLoop countdownLoop; 10 | 11 | void Update() 12 | { 13 | // Looping automatically 14 | if (countdownLoop.Progress()) 15 | Debug.Log("This is logged every " + countdownLoop.Duration + " seconds."); 16 | 17 | // Not looping 18 | if (countdownOnce.Progress()) 19 | Debug.Log("This is shown once after " + countdownOnce.Duration + " seconds."); 20 | 21 | // Start countdownOnce again - if it's already running, it is reset 22 | if (Input.GetKeyDown(KeyCode.Space)) 23 | countdownOnce.Reset(); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /Examples/CountdownPropertyExample.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 40c876e76324fd8438c7107873f75e35 3 | timeCreated: 1466344927 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Examples/LINQExtensionsExamples.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using UnityEngine; 5 | 6 | namespace UnityUtilities.Examples 7 | { 8 | public class LINQExtensionsExamples : MonoBehaviour 9 | { 10 | [SerializeField] Transform[] elements; 11 | 12 | void Awake() 13 | { 14 | int[] items = new int[] {1, 2, 3, 4, 5}; 15 | 16 | // Gets a random item 17 | Debug.Log(items.RandomElement()); 18 | 19 | // Shuffles the array in place 20 | items.Shuffle(); 21 | 22 | // Outputs the array on one line. Great for debugging. 23 | // Example output: 24 | // "3", "5", "2", "1", "4" 25 | Debug.Log(items.ToOneLineString()); 26 | } 27 | 28 | void Update() 29 | { 30 | if (Input.GetMouseButtonDown(0)) 31 | { 32 | // Get the x/y position of the click (for an orthographic camera) 33 | var mousePositionWorld = Camera.main.ScreenToWorldPoint(Input.mousePosition); 34 | 35 | // Gets the element closest to the mouse click 36 | var nearestElement = elements.Nearest(mousePositionWorld); 37 | 38 | Debug.Log("Nearest element " + nearestElement.name + " at position " + nearestElement.position); 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Examples/LINQExtensionsExamples.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 808c85cf6542d124b8c9635c0a6e7008 3 | timeCreated: 1463270294 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Examples/MathHelperExamples.cs: -------------------------------------------------------------------------------- 1 | 2 | using UnityEngine; 3 | 4 | #pragma warning disable 219 // We don't care about: warning CS0219: The variable `[...]' is assigned but its value is never used 5 | namespace UnityUtilities.Examples 6 | { 7 | public class MathHelperExamples : MonoBehaviour 8 | { 9 | [SerializeField] private Transform runner; 10 | [SerializeField] private Transform follower; 11 | 12 | void Awake() 13 | { 14 | MappingExamples(); 15 | AnglesExamples(); 16 | 17 | // Checks what calling the same EasedLerp from 0 to 1 and a factor of 75% 18 | // with 1, 10, 100, 1000 or 10000 FPS would return after one second. 19 | CheckEasedLerp(1); // => 0.75 20 | CheckEasedLerp(10); // => 0.7499999 21 | CheckEasedLerp(100); // => 0.7499999 22 | CheckEasedLerp(1000); // => 0.7499995 23 | CheckEasedLerp(10000); // => 0.7500508 24 | } 25 | 26 | void Update() 27 | { 28 | EasedLerpFactorExample(); 29 | } 30 | 31 | private void MappingExamples() 32 | { 33 | // Maps 10 from [-250..250] to [0..10] 34 | Debug.Log(MathHelper.MapClamped(10f, -250f, 250f, 0f, 10f)); // => 5.2 35 | 36 | // Applies a deadzone to a joystick input (positive and negative) to make sure that 37 | // little imperfections in the stick resting position don't make the character move 38 | Debug.Log(MathHelper.ApplyJoystickDeadzone(0.1f, 0.2f)); // => 0 39 | Debug.Log(MathHelper.ApplyJoystickDeadzone(0.2f, 0.2f)); // => 0 40 | Debug.Log(MathHelper.ApplyJoystickDeadzone(0.21f, 0.2f)); // => 0.21 41 | Debug.Log(MathHelper.ApplyJoystickDeadzone(0.3f, 0.2f)); // => 0.3 42 | Debug.Log(MathHelper.ApplyJoystickDeadzone(1f, 0.2f)); // => 1 43 | Debug.Log(MathHelper.ApplyJoystickDeadzone(-0.1f, 0.2f)); // => 0 44 | Debug.Log(MathHelper.ApplyJoystickDeadzone(-0.2f, 0.2f)); // => 0 45 | Debug.Log(MathHelper.ApplyJoystickDeadzone(-0.21f, 0.2f)); // => -0.21 46 | Debug.Log(MathHelper.ApplyJoystickDeadzone(-0.3f, 0.2f)); // => -0.3 47 | Debug.Log(MathHelper.ApplyJoystickDeadzone(-1f, 0.2f)); // => -1 48 | } 49 | 50 | private void AnglesExamples() 51 | { 52 | // Gets the center angle between two angles 53 | Debug.Log(MathHelper.GetCenterAngleDeg(20f, 160f)); // => 90 54 | Debug.Log(MathHelper.GetCenterAngleDeg(20f, 220f)); // => -60 55 | Debug.Log(MathHelper.GetCenterAngleDeg(20f, -140f)); // => -60 56 | 57 | // Normalizes an angle between 0 (inclusive) and 360 (exclusive). 58 | Debug.Log(MathHelper.NormalizeAngleDeg360(-180f)); // => 180 59 | Debug.Log(MathHelper.NormalizeAngleDeg360(180f)); // => 180 60 | Debug.Log(MathHelper.NormalizeAngleDeg360(0f)); // => 0 61 | Debug.Log(MathHelper.NormalizeAngleDeg360(360f)); // => 0 62 | Debug.Log(MathHelper.NormalizeAngleDeg360(340f)); // => 340 63 | 64 | // Normalizes an angle between -180 (inclusive) and 180 (exclusive). 65 | Debug.Log(MathHelper.NormalizeAngleDeg180(-180f)); // => -180 66 | Debug.Log(MathHelper.NormalizeAngleDeg180(180f)); // => -180 67 | Debug.Log(MathHelper.NormalizeAngleDeg180(0f)); // => 0 68 | Debug.Log(MathHelper.NormalizeAngleDeg180(360f)); // => 0 69 | Debug.Log(MathHelper.NormalizeAngleDeg180(340f)); // => -20 70 | } 71 | 72 | private void EasedLerpFactorExample() 73 | { 74 | // Get the world position of the mouse pointer 75 | Vector3 mousePositionWorld = Camera.main.ScreenToWorldPoint(Input.mousePosition); 76 | mousePositionWorld.z = 0f; 77 | 78 | // Set the runner position to the mouse pointer 79 | runner.position = mousePositionWorld; 80 | 81 | // Move the follower 75% of the remaining distance to the runner per second 82 | follower.position = UnityHelper.EasedLerpVector3(follower.position, runner.position, 0.75f); 83 | 84 | // ...which is the same as: 85 | 86 | //float t = MathHelper.EasedLerpFactor(0.75f); 87 | //follower.position = Vector3.Lerp(follower.position, mousePositionWorld, t); 88 | } 89 | 90 | void CheckEasedLerp(int steps) 91 | { 92 | var dt = 1f / steps; 93 | 94 | var currentValue = 0f; 95 | for (var i = 0; i < steps; i++) 96 | currentValue = MathHelper.EasedLerp(currentValue, 1f, 0.75f, dt); 97 | 98 | Debug.LogFormat("CheckEasedLerp({0}): {1}", steps, currentValue); 99 | } 100 | } 101 | } 102 | #pragma warning restore 168 103 | 104 | -------------------------------------------------------------------------------- /Examples/MathHelperExamples.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dadacaa2be8390043a3822f31a737948 3 | timeCreated: 1477153028 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Examples/MeshCreatorExample.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TobiasWehrum/unity-utilities/c78da2928b1f7b73046a697185271e7effeddd1f/Examples/MeshCreatorExample.cs -------------------------------------------------------------------------------- /Examples/MeshCreatorExample.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3e0b2799eda53e34fa4e3e64a348fa02 3 | timeCreated: 1467505582 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Examples/NoiseOutputValueExample.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace UnityUtilities.Examples 4 | { 5 | public class NoiseOutputValueExample : MonoBehaviour 6 | { 7 | [SerializeField] NoiseOutputValue positionNoise; 8 | [SerializeField] Transform sphere; 9 | 10 | void Update() 11 | { 12 | // Updates the value with Time.deltaTime*speed 13 | positionNoise.Progress(); 14 | 15 | // Sets the y position at the current output value 16 | sphere.transform.position = new Vector3(0, positionNoise.OutputValue, 0f); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Examples/NoiseOutputValueExample.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c51a9de8de585c54bb1f08bc4aada97c 3 | timeCreated: 1462744871 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Examples/RandomBagExample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using UnityEngine; 4 | 5 | namespace UnityUtilities.Examples 6 | { 7 | public class RandomBagExample : MonoBehaviour 8 | { 9 | enum TetrisPiece 10 | { 11 | Line, Square, T, J, L, S, Z 12 | } 13 | 14 | RandomBag pieceBag; 15 | 16 | void Awake() 17 | { 18 | // Get an array with each value of TetrisPiece 19 | TetrisPiece[] tetrisPieceArray = (TetrisPiece[]) Enum.GetValues(typeof (TetrisPiece)); 20 | 21 | // Create the bag containing two instances of every value of TetrisPiece 22 | pieceBag = new RandomBag(tetrisPieceArray, 2); 23 | 24 | // Gets 50 items from the bag. The bag will be filled with 14 TetrisPieces and 25 | // automatically refilled with 14 more when needed. No two pieces will ever be 26 | // more than 14 calls apart - and even that will only happen if that piece was 27 | // the first and last item in the current 14 piece bag filling. 28 | StringBuilder str = new StringBuilder(); 29 | for (var i = 0; i < 50; i++) 30 | { 31 | str.Append(pieceBag.PopRandomItem()); 32 | str.Append(", "); 33 | } 34 | 35 | Debug.Log(str); 36 | 37 | // Example output: 38 | // T, Z, J, Square, Z, S, L, L, S, Line, J, Line, Square, T, Square, Z, 39 | // S, T, Line, Square, Z, T, J, Line, S, L, J, L, S, Z, Line, J, Line, 40 | // J, L, L, S, T, T, Z, Square, Square, Z, T, S, Z, J, J, L, Line, 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Examples/RandomBagExample.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 94efbeb7353e5ce4c947c5e49e1656ea 3 | timeCreated: 1463259046 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Examples/RangeExample.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace UnityUtilities.Examples 4 | { 5 | public class RangeExample : MonoBehaviour 6 | { 7 | [SerializeField] RangeInt amountRange; 8 | [SerializeField] RangeFloat numberRange; 9 | 10 | void Awake() 11 | { 12 | // Get a random number in amountRange 13 | int amount = amountRange.RandomInclusive; 14 | 15 | // Output [amount] numbers 16 | for (int i = 0; i < amount; i++) 17 | { 18 | // Transform [i..(amount-1)] to [0..1] 19 | var t = ((float)i / (amount - 1)); 20 | 21 | // Mathf.Lerp(numberRange.From, numberRange.To, t) 22 | Debug.Log(numberRange.Lerp(t)); 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Examples/RangeExample.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 79d9570a75721024e80e0cd578b370e1 3 | timeCreated: 1462746256 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Examples/RollingArrayExample.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace UnityUtilities.Examples 4 | { 5 | public class RollingArrayExample : MonoBehaviour 6 | { 7 | [SerializeField] Transform indicatorObject; 8 | 9 | RollingArray mousePositions; 10 | Camera mainCamera; 11 | 12 | void Awake() 13 | { 14 | // Save the last 50 elements 15 | mousePositions = new RollingArray(50); 16 | 17 | // Cache a reference to the main camera 18 | mainCamera = Camera.main; 19 | } 20 | 21 | void FixedUpdate() 22 | { 23 | // Get the mouse position in a fixed interval 24 | // If we get to 50 positions, the oldest position will be replaced 25 | mousePositions.Append(mainCamera.ScreenToWorldPoint(Input.mousePosition)); 26 | } 27 | 28 | void Update() 29 | { 30 | // Only continue if we have at least one mouse position 31 | if (mousePositions.IsEmpty) 32 | return; 33 | 34 | // Go through all the saved mouse positions from oldest to newest to get the average 35 | Vector2 averagePosition = new Vector2(); 36 | for (var i = 0; i < mousePositions.Count; i++) 37 | { 38 | averagePosition += mousePositions[i]; 39 | } 40 | averagePosition /= mousePositions.Count; 41 | 42 | // Set the indicator object to the average position 43 | indicatorObject.position = averagePosition; 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /Examples/RollingArrayExample.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fed215a0a85333744a0f6903548366cf 3 | timeCreated: 1465133468 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Examples/SingletonEntityManager.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEngine; 3 | 4 | namespace UnityUtilities.Examples 5 | { 6 | /* If the following component is added on a game object in the scene, it could be accessed from anywhere 7 | * via SingletonEntityManager.Instance, e.g.: SingletonEntityManager.Instance.AddEntity(newEntity); 8 | * 9 | * This is available even before SingletonEntityManager.Awake() is called. 10 | * 11 | * If you want to use OnDestroy(), you have to override it like shown in the example below. All other MonoBehaviour 12 | * callbacks can be used as usual. 13 | */ 14 | 15 | public class SingletonEntityManager : SingletonMonoBehaviour 16 | { 17 | List entities; 18 | 19 | public IEnumerable Entities 20 | { 21 | get { return entities; } 22 | } 23 | 24 | void Awake() 25 | { 26 | entities = new List(); 27 | } 28 | 29 | // If you want to use OnDestroy(), you have to override it like this 30 | protected override void OnDestroy() 31 | { 32 | base.OnDestroy(); 33 | Debug.Log("Destroyed"); 34 | } 35 | 36 | public void AddEntity(GameObject entity) 37 | { 38 | entities.Add(entity); 39 | } 40 | 41 | public void RemoveEntity(GameObject entity) 42 | { 43 | entities.Remove(entity); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Examples/SingletonEntityManager.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e9eef450bee08ed46979cccbf8a00b24 3 | timeCreated: 1462747651 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Examples/SingletonMusicManager.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace UnityUtilities.Examples 4 | { 5 | /* The SingletonMusicManager in the following example can be accessed in the same way, but it is not destroyed when the scenes switches. 6 | 7 | You could make this SingletonMusicManager a prefab and drop it in multiple scenes that you work on. If at any time there are two `SingletonMusicManager`, 8 | the one from the previous scene survives and the new one is destroyed. (For that reason, you should never create an Awake() method in a 9 | PersistentSingletonMonoBehaviour. Instead, use `OnPersistentSingletonAwake()` because it is only called on "surviving" instances. Similarily, you shouldn't 10 | have an OnDestroy() method which would be called if this is ever destroyed via Destroy(); instead, use OnPersistentSingletonDestroyed().) 11 | 12 | Note that SingletonMusicManager.Instance is only available after SingletonMusicManager.Awake() was called, so if you need it in another Awake() 13 | call, you should put the SingletonMusicManager higher in the Script Execution Order: http://docs.unity3d.com/Manual/class-ScriptExecution.html. 14 | */ 15 | 16 | public class SingletonMusicManager : PersistentSingletonMonoBehaviour 17 | { 18 | protected override void OnPersistentSingletonAwake() 19 | { 20 | base.OnPersistentSingletonAwake(); 21 | 22 | // Start playing the music the first time Awake() is called 23 | PlayMusic(); 24 | } 25 | 26 | protected override void OnSceneSwitched() 27 | { 28 | base.OnSceneSwitched(); 29 | 30 | // Fade to random song once a new scene is loaded 31 | FadeToRandomSong(); 32 | } 33 | 34 | protected override void OnPersistentSingletonDestroyed() 35 | { 36 | base.OnPersistentSingletonDestroyed(); 37 | 38 | // Stop the music when Destroy() was called on the active instance. 39 | StopMusic(); 40 | } 41 | 42 | public void PlayMusic() 43 | { 44 | Debug.Log("Play music"); 45 | } 46 | 47 | public void StopMusic() 48 | { 49 | Debug.Log("Stop music"); 50 | } 51 | 52 | public void FadeToRandomSong() 53 | { 54 | Debug.Log("Fade to a random song"); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Examples/SingletonMusicManager.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 19d5da28c8805a24bb4571bd23ad2003 3 | timeCreated: 1462747648 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Examples/UnityHelperExamples.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | #pragma warning disable 219 // We don't care about: warning CS0219: The variable `[...]' is assigned but its value is never used 4 | namespace UnityUtilities.Examples 5 | { 6 | public class UnityHelperExamples : MonoBehaviour 7 | { 8 | [SerializeField] Transform someEnemyTransform; 9 | [SerializeField] Transform enemyIndicator; 10 | [SerializeField] GameObject prefab; 11 | [SerializeField] LayerMask someLayerMask; 12 | [SerializeField] CharacterController playerCharacterController; 13 | 14 | void Awake() 15 | { 16 | TransformVectorColorExamples(); 17 | CentroidExample(); 18 | Vector2RotationExamples(); 19 | GameObjectExamples(); 20 | RectExamples(); 21 | PlayerPrefsExample(); 22 | CapsuleCastExample(); 23 | RandomExamples(); 24 | OtherExamples(); 25 | 26 | // You can find an example for eased lerping in MathHelperExamples.EasedLerpFactorExample() 27 | } 28 | 29 | void TransformVectorColorExamples() 30 | { 31 | /* Set the transform.position.x to 5 and z to 3. Keeps y. 32 | * Equivalent to: 33 | * var position = transform.position; 34 | * position.x = 5; 35 | * position.z = 3; 36 | * transform.position = position; 37 | */ 38 | transform.SetPosition(x: 5, z: 3); 39 | 40 | // Same as above; only sets transform.localEulerAngles.y. 41 | // There are extension methods for all position/rotation/scales. 42 | transform.SetLocalEulerAngles(y: 180); 43 | 44 | // Similar methods are available for Vector2/3/4 and Color: 45 | // Gets the transform.position, but with y set to 0. 46 | Vector3 floorPosition = transform.position.Change3(y: 0); 47 | 48 | // Gets the material color, but sets the color.a value to 0.5. 49 | Color halfTransparentColor = GetComponent().sharedMaterial.color.ChangeAlpha(0.5f); 50 | 51 | // Sets the position/rotation of enemyIndicator to someEnemyTransform.position/rotation 52 | enemyIndicator.CopyPositionAndRotatationFrom(someEnemyTransform); 53 | } 54 | 55 | private void CentroidExample() 56 | { 57 | Vector3[] list = { 58 | new Vector3(-5, 10, 12), 59 | new Vector3(55, 32, 10), 60 | new Vector3(85, -40, 80) 61 | }; 62 | 63 | // Calculates the geometric center (the average) of the input list 64 | Debug.Log("Centroid: " + list.CalculateCentroid()); // => Centroid: (45.0, 0.7, 34.0) 65 | } 66 | 67 | void Vector2RotationExamples() 68 | { 69 | // Create a length 1 Vector2 pointing 40 degrees away from (1.0, 0.0) 70 | var vector = UnityHelper.CreateVector2AngleDeg(20f); 71 | Debug.Log(vector); // => (0.9, 0.3) 72 | 73 | // Rotate the vector 70 degrees 74 | vector = vector.RotateDeg(70f); 75 | Debug.Log(vector); // => (0.0, 1.0) 76 | 77 | // Output the current vector rotation 78 | Debug.Log(vector.GetAngleDeg()); // => 90 79 | } 80 | 81 | void GameObjectExamples() 82 | { 83 | // Assigns layer 4 to this GameObject and all its children recursively 84 | gameObject.AssignLayerToHierarchy(4); 85 | 86 | // Create an instance of a prefab. When the prefab is named "Original", the instance will 87 | // be named "Original(Copy)" 88 | GameObject copiedGameObject = Instantiate(prefab); 89 | 90 | // Return the name without "(Copy)" 91 | Debug.Log(copiedGameObject.GetNameWithoutClone()); // => Original 92 | 93 | // Change the name back to "Original" 94 | copiedGameObject.StripCloneFromName(); 95 | } 96 | 97 | void RectExamples() 98 | { 99 | // Make a rect from (10|20) to (60|120) 100 | Rect rect = new Rect(10, 20, 50, 100); 101 | 102 | // Gets a random position for an enemy in the rect, leaving a 5 unit border 103 | Vector2 enemySpawnPosition = rect.RandomPosition(-5); 104 | 105 | // Gets a random sub rect of size 10|10 in which we could spawn multiple enemies 106 | Rect enemySpawnSubrect = rect.RandomSubRect(10, 10); 107 | 108 | Vector2 enemyPosition = new Vector2(0, 500); 109 | 110 | // Clamp an enemy position to the rect 111 | enemyPosition = rect.Clamp2(enemyPosition); 112 | Debug.Log(enemyPosition); // Output: (10.0, 120.0) 113 | 114 | // Create a rect that is 10 units bigger to each side 115 | Rect biggerRect = rect.Extend(10); 116 | 117 | // Get the corner points 118 | Vector2[] cornerPoints = rect.GetCornerPoints(); 119 | } 120 | 121 | void PlayerPrefsExample() 122 | { 123 | // Gets a PlayerPrefs key "FirstStart" or return true if not set 124 | bool isFirstStart = UnityHelper.PlayerPrefsGetBool("FirstStart", true); 125 | 126 | // Set the key FirstStart to false 127 | UnityHelper.PlayerPrefsSetBool("FirstStart", false); 128 | } 129 | 130 | void CapsuleCastExample() 131 | { 132 | Vector3 point1; 133 | Vector3 point2; 134 | float radius; 135 | Vector3 origin = playerCharacterController.transform.position; 136 | 137 | // Get the data for the capsule cast from the current player position 138 | UnityHelper.GetCapsuleCastData(playerCharacterController, origin, out point1, out point2, out radius); 139 | 140 | // Cast 2 units forwards 141 | bool hitSomething = Physics.CapsuleCast(point1, point2, radius, Vector3.forward, 2f); 142 | } 143 | 144 | void RandomExamples() 145 | { 146 | // Points in a random 2D direction 147 | var randomDirection2D = UnityHelper.RandomOnUnitCircle; 148 | 149 | // Either goes left or right 150 | var deltaX = 20 * UnityHelper.RandomSign; 151 | 152 | // Gets set to either choice 153 | var choice = UnityHelper.RandomBool ? "Choice A" : "Choice B"; 154 | } 155 | 156 | void OtherExamples() 157 | { 158 | // Does the layer mask contain layer 4? 159 | bool containsLayer4 = someLayerMask.ContainsLayer(4); 160 | 161 | // Get the bounds of all colliders in the level to clamp the camera later on 162 | Collider[] allColliders = FindObjectsOfType(); 163 | Bounds levelBounds = UnityHelper.CombineColliderBounds(allColliders); 164 | 165 | // Find out how much the perspective camera can see at 10 unit away 166 | Vector2 viewportSizeAtDistance = Camera.main.CalculateViewportWorldSizeAtDistance(10); 167 | } 168 | } 169 | } 170 | #pragma warning restore 168 -------------------------------------------------------------------------------- /Examples/UnityHelperExamples.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 262d17ba29d2c21409dd7bac960113f0 3 | timeCreated: 1465301267 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Examples/XmlHelperExample.cs: -------------------------------------------------------------------------------- 1 | using System.Xml; 2 | using System.Xml.Serialization; 3 | using UnityEngine; 4 | 5 | #pragma warning disable 414, 219 6 | namespace UnityUtilities.Examples 7 | { 8 | public class XmlHelperExample : MonoBehaviour 9 | { 10 | public class TestData 11 | { 12 | // A public field - will be serialized 13 | public int field; 14 | 15 | // A private field with public property - will be serialized 16 | float property; 17 | public float Property 18 | { 19 | get { return property; } 20 | set { property = value;} 21 | } 22 | 23 | // An auto property - will be serialized 24 | public bool AutoProperty { get; set; } 25 | 26 | // A private field - will *not* be serialized 27 | string privateField = "Test"; 28 | 29 | // A public field marked "XmlIgnore" - will *not* be serialized 30 | [XmlIgnore] 31 | public double publicNonSerialized = 5.5; 32 | 33 | // The public default constructor is needed for the XmlSerializer. 34 | public TestData() 35 | { 36 | } 37 | 38 | public TestData(int field, float property, bool autoProperty) 39 | { 40 | this.field = field; 41 | this.property = property; 42 | AutoProperty = autoProperty; 43 | } 44 | } 45 | 46 | void Awake() 47 | { 48 | SerializationExamples(); 49 | XmlExamples(); 50 | } 51 | 52 | void SerializationExamples() 53 | { 54 | // Create a new TestData object 55 | TestData data = new TestData(1, 2.3f, true); 56 | 57 | // Serialize the TestData object into a string 58 | string xmlString = data.SerializeToXmlString(); 59 | 60 | /* Output: 61 | 62 | 63 | 64 | 1 65 | 2.3 66 | true 67 | 68 | */ 69 | Debug.Log(xmlString); 70 | 71 | // Get the data back from the string 72 | TestData deserializedData = xmlString.DeserializeFromXmlString(); 73 | } 74 | 75 | void XmlExamples() 76 | { 77 | // Create an XmlDocument with test data 78 | XmlDocument xmlDocument = new XmlDocument(); 79 | xmlDocument.LoadXml("" + 80 | " " + 81 | " Grunt" + 82 | " " + 83 | " " + 84 | " " + 85 | " Tank" + 86 | " " + 87 | " true" + 88 | " " + 89 | ""); 90 | 91 | // Read each enemyData element in the enemyList 92 | foreach (XmlNode enemyData in xmlDocument["enemyList"].ChildNodes) 93 | { 94 | // Get the name element content, if it exists, else set "???" 95 | string name = enemyData.GetElementString("name", "???"); 96 | 97 | // Get the position element and then its attributes 98 | XmlNode position = enemyData["position"]; 99 | int x = position.GetAttributeInt("x"); 100 | int y = position.GetAttributeInt("y"); 101 | 102 | // Get the ranged element content, if it exists, else set "false" 103 | bool ranged = enemyData.GetElementBool("ranged", false); 104 | 105 | // Output the result 106 | Debug.Log(string.Format("{0} at {1}|{2} is {3}", 107 | name, 108 | x, 109 | y, 110 | ranged ? "ranged" : "not ranged")); 111 | } 112 | 113 | /* Grunt at 5|3 is not ranged 114 | Tank at 7|1 is ranged 115 | */ 116 | } 117 | } 118 | } 119 | #pragma warning restore 414, 219 -------------------------------------------------------------------------------- /Examples/XmlHelperExample.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 30bf2162031183f46970f7b4a1c358af 3 | timeCreated: 1466344872 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Tobias Wehrum 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /LICENSE.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d07847ece7dacc24591ca0b7266b4180 3 | timeCreated: 1462727730 4 | licenseType: Free 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /LINQExtensions.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 68eeefa7b7ec5cd40a04e24c328fbf43 3 | folderAsset: yes 4 | timeCreated: 1463231729 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /LINQExtensions/LINQExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using UnityEngine; 5 | 6 | namespace UnityUtilities 7 | { 8 | /// 9 | /// A collection of extension methods for , and arrays. 10 | /// 11 | public static class LINQExtensions 12 | { 13 | /// 14 | /// Takes a collection, generates values from the items and and returns the item with the lowest generated value. 15 | /// Useful for example to find the closest item. Reverse the generated values to find the item with the highest generated value. 16 | /// 17 | /// This returns the same as list.Where(element => predicateValue(valueConverter(element))).OrderBy(valueConverter).First(), but it 18 | /// a) doesn't order the whole list and 19 | /// b) doesn't call valueConverted more than once per element. 20 | /// 21 | /// The collection element type. 22 | /// The generated value type. 23 | /// The list of elements. 24 | /// The method to convert an element to a generated value used for ordering. 25 | /// A predicate testing whether the generated value is permitted. If true, the element is used; if false, the element is skipped. 26 | /// The first element by the generated value in order that succeeded the predicateValue test. 27 | public static TElement FirstByGeneratedValue(this IEnumerable list, Func valueConverter, Predicate predicateValue = null) 28 | where TValue : IComparable 29 | { 30 | var isFirstElement = true; 31 | TElement bestElement = default(TElement); 32 | TValue bestValue = default(TValue); 33 | 34 | // For each element... 35 | foreach (var element in list) 36 | { 37 | // Generate its value 38 | var value = valueConverter(element); 39 | 40 | // Check whether its value is permitted 41 | if ((predicateValue != null) && (!predicateValue(value))) 42 | continue; 43 | 44 | // If it's the first permitted element or better than the previous best element... 45 | if (isFirstElement || (value.CompareTo(bestValue) < 0)) 46 | { 47 | // ...set it as the best element 48 | isFirstElement = false; 49 | bestElement = element; 50 | bestValue = value; 51 | } 52 | } 53 | 54 | return bestElement; 55 | } 56 | 57 | /// 58 | /// Returns the component nearest to the referencePoint and in minimum/maximum range. 59 | /// 60 | /// The collection element type. Needs to be a subclass of . 61 | /// The list of components. 62 | /// A reference point to get the distance from. 63 | /// Optional: The minimum distance. 64 | /// Optional: The maximum distance. 65 | /// The element nearest to the referencePoint and in minimum/maximum range. 66 | public static TElement Nearest(this IEnumerable list, Vector3 referencePoint, float minDistance = 0, float maxDistance = float.PositiveInfinity) 67 | where TElement : Component 68 | { 69 | // Create the predicate value from the min/max distance. 70 | Predicate predicateValue = null; 71 | if ((minDistance != 0) || !float.IsPositiveInfinity(maxDistance)) 72 | { 73 | var minDistanceSq = minDistance * minDistance; 74 | var maxDistanceSq = maxDistance * maxDistance; 75 | predicateValue = (distanceSq => (distanceSq >= minDistanceSq) && (distanceSq <= maxDistanceSq)); 76 | } 77 | 78 | // Return the nearest element to the reference point in the given range. 79 | return list.FirstByGeneratedValue(component => (component.transform.position - referencePoint).sqrMagnitude, predicateValue); 80 | } 81 | 82 | /// 83 | /// Shuffles an array in place. 84 | /// 85 | /// The array element type. 86 | /// The array to shuffle. 87 | public static void Shuffle(this T[] list) 88 | { 89 | var count = list.Length; 90 | for (int i1 = 0; i1 < count; i1++) 91 | { 92 | var i2 = UnityEngine.Random.Range(0, count); 93 | var element = list[i1]; 94 | list[i1] = list[i2]; 95 | list[i2] = element; 96 | } 97 | } 98 | 99 | /// 100 | /// Shuffles a list in place. 101 | /// 102 | /// The list element type. 103 | /// The list to shuffle. 104 | public static void Shuffle(this List list) 105 | { 106 | var count = list.Count; 107 | for (int i1 = 0; i1 < count; i1++) 108 | { 109 | var i2 = UnityEngine.Random.Range(0, count); 110 | var element = list[i1]; 111 | list[i1] = list[i2]; 112 | list[i2] = element; 113 | } 114 | } 115 | 116 | /// 117 | /// Returns a random element from the array. 118 | /// 119 | /// The array element type. 120 | /// The array to return an element from. 121 | /// A random element from the array. 122 | public static T RandomElement(this T[] array) 123 | { 124 | var index = UnityEngine.Random.Range(0, array.Length); 125 | return array[index]; 126 | } 127 | 128 | /// 129 | /// Returns a random element from the list. 130 | /// 131 | /// The list element type. 132 | /// The list to return an element from. 133 | /// A random element from the list. 134 | public static T RandomElement(this List list) 135 | { 136 | var index = UnityEngine.Random.Range(0, list.Count); 137 | return list[index]; 138 | } 139 | 140 | /// 141 | /// Calls ToString() on every element of the list, puts [encapsulate] directly before and after the result 142 | /// and then concatenates the results with [seperator] between them. 143 | /// 144 | /// The collection element type. 145 | /// The collection to concatenate. 146 | /// The seperator between entries. 147 | /// The string to put directly before and after every item. 148 | /// A string containing the ToString() results of all items. 149 | public static string ToOneLineString(this IEnumerable list, string separator = ", ", string encapsulate = "\"") 150 | { 151 | var useEncapsulate = encapsulate.Length > 0; 152 | 153 | var result = new StringBuilder(); 154 | foreach (var element in list) 155 | { 156 | if (result.Length > 0) 157 | result.Append(separator); 158 | 159 | if (useEncapsulate) 160 | result.Append(encapsulate); 161 | 162 | result.Append(element); 163 | 164 | if (useEncapsulate) 165 | result.Append(encapsulate); 166 | } 167 | 168 | return result.ToString(); 169 | } 170 | } 171 | } -------------------------------------------------------------------------------- /LINQExtensions/LINQExtensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3c3902b08d7a97840a4f53d71841fd89 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /LINQExtensions/README.md: -------------------------------------------------------------------------------- 1 | # LINQExtensions 2 | 3 | A collection of extension methods for `IEnumerable`, `List` and arrays. 4 | 5 | ## Examples 6 | 7 | # RandomElement/Shuffle/ToOneLineString 8 | 9 | ```C# 10 | int[] items = new int[] {1, 2, 3, 4, 5}; 11 | 12 | // Gets a random item 13 | Debug.Log(items.RandomElement()); 14 | 15 | // Shuffles the array in place 16 | items.Shuffle(); 17 | 18 | // Outputs the array on one line. Great for debugging. 19 | // Example output: 20 | // "3", "5", "2", "1", "4" 21 | Debug.Log(items.ToOneLineString()); 22 | ``` 23 | 24 | # Nearest 25 | 26 | ```C# 27 | public class LINQExtensionsExamples : MonoBehaviour 28 | { 29 | [SerializeField] Transform[] elements; 30 | 31 | void Update() 32 | { 33 | if (Input.GetMouseButtonDown(0)) 34 | { 35 | // Get the x/y position of the click (for an orthographic camera) 36 | var mousePositionWorld = Camera.main.ScreenToWorldPoint(Input.mousePosition); 37 | 38 | // Gets the element closest to the mouse click 39 | var nearestElement = elements.Nearest(mousePositionWorld); 40 | 41 | Debug.Log("Nearest element " + nearestElement.name + " at position " + nearestElement.position); 42 | } 43 | } 44 | } 45 | ``` 46 | 47 | ## Dependencies 48 | 49 | None. -------------------------------------------------------------------------------- /LINQExtensions/README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e787f75ca3f464843a5c94d80d660b97 3 | timeCreated: 1463270294 4 | licenseType: Pro 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /MathHelper.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 08400f198e74a804e8df43d5dd377179 3 | folderAsset: yes 4 | timeCreated: 1477146251 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /MathHelper/MathHelper.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using Random = UnityEngine.Random; 3 | 4 | namespace UnityUtilities 5 | { 6 | /// 7 | /// The MathHelper contains methods to help with mapping and angles and a really nifty method for framerate-independent eased lerping. 8 | /// 9 | public static class MathHelper 10 | { 11 | #region Mapping 12 | 13 | /// 14 | /// Maps a value from [sourceFrom..sourceTo] to [targetFrom..targetTo] with clamping. 15 | /// 16 | /// This is basically Mathf.Lerp(targetFrom, targetTo, Mathf.InverseLerp(sourceFrom, sourceTo, sourceValue)). 17 | /// 18 | /// The value in the range of [sourceFrom..sourceTo]. Will be clamped if not in that range. 19 | /// The lower end of the source range. 20 | /// The higher end of the source range. 21 | /// The lower end of the target range. 22 | /// The higher end of the target range. 23 | /// The mapped value. 24 | public static float MapClamped(float sourceValue, float sourceFrom, float sourceTo, float targetFrom, float targetTo) 25 | { 26 | var sourceRange = sourceTo - sourceFrom; 27 | var targetRange = targetTo - targetFrom; 28 | var percent = Mathf.Clamp01((sourceValue - sourceFrom) / sourceRange); 29 | return targetFrom + targetRange * percent; 30 | } 31 | 32 | /// 33 | /// Applies a deadzone [-deadzone..deadzone] in which the value will be set to 0. 34 | /// 35 | /// The joystick value. 36 | /// A value between for which all results [-deadzone..deadzone] will be set to 0. 37 | /// If this is true, the values between [-1..-deadzone] and [deadzone..1] will be mapped to [-1..0] and [0..1] respectively. 38 | /// The result value between [-1..1]. 39 | public static float ApplyJoystickDeadzone(float value, float deadzone, bool fullRangeBetweenDeadzoneAndOne = false) 40 | { 41 | if (Mathf.Abs(value) <= deadzone) 42 | return 0; 43 | 44 | if (fullRangeBetweenDeadzoneAndOne && (deadzone > 0f)) 45 | { 46 | if (value < 0) 47 | { 48 | return MapClamped(value, -1f, -deadzone, -1f, 0f); 49 | } 50 | else 51 | { 52 | return MapClamped(value, deadzone, 1f, 0f, 1f); 53 | } 54 | } 55 | 56 | return value; 57 | } 58 | 59 | /// 60 | /// Maps a joystick input from [sourceFrom..sourceTo] to [-1..1] with clamping. 61 | /// Applies a deadzone [-deadzone..deadzone] in which the value will be set to 0. 62 | /// 63 | /// The value in the range of [sourceFrom..sourceTo]. Will be clamped if not in that range. 64 | /// The lower end of the source range. 65 | /// The higher end of the source range. 66 | /// A value between 0 and 1 for which all results [-deadzone..deadzone] will be set to 0. 67 | /// If this is true, the values between [-1..-deadzone] and [deadzone..1] will be mapped to [-1..0] and [0..1] respectively. 68 | /// The result value between [-1..1]. 69 | public static float MapClampedJoystick(float sourceValue, float sourceFrom, float sourceTo, float deadzone = 0f, bool fullRangeBetweenDeadzoneAndOne = false) 70 | { 71 | var percent = MapClamped(sourceValue, sourceFrom, sourceTo, -1, 1); 72 | 73 | if (deadzone > 0) 74 | percent = ApplyJoystickDeadzone(percent, deadzone, fullRangeBetweenDeadzoneAndOne); 75 | 76 | return percent; 77 | } 78 | 79 | #endregion 80 | 81 | #region Angles 82 | 83 | /// 84 | /// Returns the closer center between two angles. 85 | /// 86 | /// The first angle. 87 | /// The second angle. 88 | /// The closer center. 89 | public static float GetCenterAngleDeg(float angle1, float angle2) 90 | { 91 | return angle1 + Mathf.DeltaAngle(angle1, angle2) / 2f; 92 | } 93 | 94 | /// 95 | /// Normalizes an angle between 0 (inclusive) and 360 (exclusive). 96 | /// 97 | /// The input angle. 98 | /// The result angle. 99 | public static float NormalizeAngleDeg360(float angle) 100 | { 101 | while (angle < 0) 102 | angle += 360; 103 | 104 | if (angle >= 360) 105 | angle %= 360; 106 | 107 | return angle; 108 | } 109 | 110 | /// 111 | /// Normalizes an angle between -180 (inclusive) and 180 (exclusive). 112 | /// 113 | /// The input angle. 114 | /// The result angle. 115 | public static float NormalizeAngleDeg180(float angle) 116 | { 117 | while (angle < -180) 118 | angle += 360; 119 | 120 | while (angle >= 180) 121 | angle -= 360; 122 | 123 | return angle; 124 | } 125 | 126 | #endregion 127 | 128 | #region Framerate-Independent Lerping 129 | 130 | /// 131 | /// Provides a framerate-independent t for lerping towards a target. 132 | /// 133 | /// Example: 134 | /// 135 | /// currentValue = Mathf.Lerp(currentValue, 1f, MathHelper.EasedLerpFactor(0.75f); 136 | /// 137 | /// will cover 75% of the remaining distance between currentValue and 1 each second. 138 | /// 139 | /// There are essentially two ways of lerping a value over time: linear (constant speed) or 140 | /// eased (e.g. getting slower the closer you are to the target, see http://easings.net.) 141 | /// 142 | /// For linear lerping (and most of the easing functions), you need to track the start and end 143 | /// positions and the time that elapsed. 144 | /// 145 | /// Calling something like 146 | /// 147 | /// currentValue = Mathf.Lerp(currentValue, 1f, 0.95f); 148 | /// 149 | /// every frame provides an easy way of eased lerping without tracking elapsed time or the 150 | /// starting value, but since it's called every frame, the actual traversed distance per 151 | /// second changes the higher the framerate is. 152 | /// 153 | /// This function replaces the lerp T to make it framerate-independent and easier to estimate. 154 | /// 155 | /// For more info, see https://www.scirra.com/blog/ashley/17/using-lerp-with-delta-time. 156 | /// 157 | /// How much % the lerp should cover per second. 158 | /// How much time passed since the last call. 159 | /// The framerate-independent lerp t. 160 | public static float EasedLerpFactor(float factor, float deltaTime = 0f) 161 | { 162 | if (deltaTime == 0f) 163 | deltaTime = Time.deltaTime; 164 | 165 | return 1 - Mathf.Pow(1 - factor, deltaTime); 166 | } 167 | 168 | /// 169 | /// Framerate-independent eased lerping to a target value, slowing down the closer it is. 170 | /// 171 | /// If you call 172 | /// 173 | /// currentValue = MathHelper.EasedLerp(currentValue, 1f, 0.75f); 174 | /// 175 | /// each frame (e.g. in Update()), starting with a currentValue of 0, then after 1 second 176 | /// it will be approximately 0.75 - which is 75% of the way between 0 and 1. 177 | /// 178 | /// Adjusting the target or the percentPerSecond between calls is also possible. 179 | /// 180 | /// The current value. 181 | /// The target value. 182 | /// How much of the distance between current and target should be covered per second? 183 | /// How much time passed since the last call. 184 | /// The interpolated value from current to target. 185 | public static float EasedLerp(float current, float target, float percentPerSecond, float deltaTime = 0f) 186 | { 187 | var t = EasedLerpFactor(percentPerSecond, deltaTime); 188 | return Mathf.Lerp(current, target, t); 189 | } 190 | 191 | #endregion 192 | } 193 | } -------------------------------------------------------------------------------- /MathHelper/MathHelper.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 479e0b35c08d2124a92e7b31e5685665 3 | timeCreated: 1465253549 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /MathHelper/README.md: -------------------------------------------------------------------------------- 1 | # MathHelper 2 | 3 | The MathHelper contains methods to help with mapping and angles and a really nifty method for framerate-independent eased lerping. 4 | 5 | ## Examples 6 | 7 | ### Framerate-Independent Eased Lerping 8 | 9 | There are essentially two ways of lerping a value over time: linear (constant speed) or eased (e.g. getting slower the closer you are to the target, see http://easings.net.) 10 | 11 | For linear lerping (and most of the easing functions), you need to track the start and end positions and the time that elapsed. 12 | 13 | Calling something like `currentValue = Mathf.Lerp(currentValue, 1f, 0.95f);` every frame provides an easy way of eased lerping without tracking elapsed time or the starting value, but since it's called every frame, the actual traversed distance per second changes the higher the framerate is. 14 | 15 | EasedLerpFactor replaces the lerp parameter t to make it framerate-independent and easier to estimate. 16 | 17 | You can find more information about the formula used [here](https://www.scirra.com/blog/ashley/17/using-lerp-with-delta-time). 18 | 19 | You can use `MathHelper.EasedLerpFactor` to get the t used for a lerp or call `MathHelper.EasedLerp`, `UnityHelper.EasedLerpVector2`, `UnityHelper.EasedLerpVector3`, `UnityHelper.EasedLerpVector4` or `UnityHelper.EasedLerpColor` directly. 20 | 21 | ![EasedLerpFactorExample Editor Screenshot](https://raw.githubusercontent.com/TobiasWehrum/unity-utilities/master/_Images/EasedLerpFactorExample.gif) 22 | 23 | ```C# 24 | void Update() 25 | { 26 | // Get the world position of the mouse pointer 27 | Vector3 mousePositionWorld = Camera.main.ScreenToWorldPoint(Input.mousePosition); 28 | mousePositionWorld.z = 0f; 29 | 30 | // Set the runner position to the mouse pointer 31 | runner.position = mousePositionWorld; 32 | 33 | // Move the follower 75% of the remaining distance to the runner per second 34 | follower.position = UnityHelper.EasedLerpVector3(follower.position, runner.position, 0.75f); 35 | 36 | // ...which is the same as: 37 | 38 | //float t = MathHelper.EasedLerpFactor(0.75f); 39 | //follower.position = Vector3.Lerp(follower.position, mousePositionWorld, t); 40 | } 41 | ``` 42 | 43 | ### Mapping 44 | 45 | ```C# 46 | // Maps 10 from [-250..250] to [0..10] 47 | Debug.Log(MathHelper.MapClamped(10f, -250f, 250f, 0f, 10f)); // => 5.2 48 | 49 | // Applies a deadzone to a joystick input (positive and negative) to make sure that 50 | // little imperfections in the stick resting position don't make the character move 51 | Debug.Log(MathHelper.ApplyJoystickDeadzone(0.1f, 0.2f)); // => 0 52 | Debug.Log(MathHelper.ApplyJoystickDeadzone(0.2f, 0.2f)); // => 0 53 | Debug.Log(MathHelper.ApplyJoystickDeadzone(0.21f, 0.2f)); // => 0.21 54 | Debug.Log(MathHelper.ApplyJoystickDeadzone(0.3f, 0.2f)); // => 0.3 55 | Debug.Log(MathHelper.ApplyJoystickDeadzone(1f, 0.2f)); // => 1 56 | Debug.Log(MathHelper.ApplyJoystickDeadzone(-0.1f, 0.2f)); // => 0 57 | Debug.Log(MathHelper.ApplyJoystickDeadzone(-0.2f, 0.2f)); // => 0 58 | Debug.Log(MathHelper.ApplyJoystickDeadzone(-0.21f, 0.2f)); // => -0.21 59 | Debug.Log(MathHelper.ApplyJoystickDeadzone(-0.3f, 0.2f)); // => -0.3 60 | Debug.Log(MathHelper.ApplyJoystickDeadzone(-1f, 0.2f)); // => -1 61 | ``` 62 | 63 | ### Angles 64 | 65 | ```C# 66 | // Gets the center angle between two angles 67 | Debug.Log(MathHelper.GetCenterAngleDeg(20f, 160f)); // => 90 68 | Debug.Log(MathHelper.GetCenterAngleDeg(20f, 220f)); // => -60 69 | Debug.Log(MathHelper.GetCenterAngleDeg(20f, -140f)); // => -60 70 | 71 | // Normalizes an angle between 0 (inclusive) and 360 (exclusive). 72 | Debug.Log(MathHelper.NormalizeAngleDeg360(-180f)); // => 180 73 | Debug.Log(MathHelper.NormalizeAngleDeg360(180f)); // => 180 74 | Debug.Log(MathHelper.NormalizeAngleDeg360(0f)); // => 0 75 | Debug.Log(MathHelper.NormalizeAngleDeg360(360f)); // => 0 76 | Debug.Log(MathHelper.NormalizeAngleDeg360(340f)); // => 340 77 | 78 | // Normalizes an angle between -180 (inclusive) and 180 (exclusive). 79 | Debug.Log(MathHelper.NormalizeAngleDeg180(-180f)); // => -180 80 | Debug.Log(MathHelper.NormalizeAngleDeg180(180f)); // => -180 81 | Debug.Log(MathHelper.NormalizeAngleDeg180(0f)); // => 0 82 | Debug.Log(MathHelper.NormalizeAngleDeg180(360f)); // => 0 83 | Debug.Log(MathHelper.NormalizeAngleDeg180(340f)); // => -20 84 | ``` 85 | 86 | ## Dependencies 87 | 88 | None. -------------------------------------------------------------------------------- /MathHelper/README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 080955fafb9c14244a8daa5a727aefe5 3 | timeCreated: 1477157197 4 | licenseType: Free 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /MeshCreator.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 458e81fc1d8b9314aad15e31165ca9d0 3 | folderAsset: yes 4 | timeCreated: 1467495335 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /MeshCreator/MeshCreator.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEngine; 3 | 4 | namespace UnityUtilities 5 | { 6 | /// 7 | /// Stores the vertex/triangle data of a mesh in easily modifiable form - which can 8 | /// then be used to actually create or update a mesh. 9 | /// 10 | public class MeshCreator 11 | { 12 | /// 13 | /// The triangles of the mesh. 14 | /// 15 | List meshTriangles; 16 | 17 | /// 18 | /// The vertices of a mesh. 19 | /// 20 | List meshVertices; 21 | 22 | /// 23 | /// A temporary array of the vertex positions for filling the mesh. 24 | /// 25 | Vector3[] tempVertices; 26 | 27 | /// 28 | /// A temporary array of the vertex normals for filling the mesh. 29 | /// 30 | Vector3[] tempNormals; 31 | 32 | /// 33 | /// A temporary array of the vertex UV coordinates for filling the mesh. 34 | /// 35 | Vector2[] tempUV; 36 | 37 | /// 38 | /// A temporary array of the vertex colors for filling the mesh. 39 | /// 40 | Color[] tempColors; 41 | 42 | /// 43 | /// A temporary array of the triangle vertex indizes for filling the mesh. 44 | /// 45 | int[] tempTriangles; 46 | 47 | /// 48 | /// The amount of registered vertices. 49 | /// 50 | public int MeshVerticesCount 51 | { 52 | get { return meshVertices.Count; } 53 | } 54 | 55 | /// 56 | /// The registered vertices. Use to add a vertex. 57 | /// Content of existing vertices can be freely updated. 58 | /// 59 | public List MeshVertices 60 | { 61 | get { return meshVertices; } 62 | } 63 | 64 | /// 65 | /// The registered triangles. Use or 66 | /// to add triangles. 67 | /// 68 | public List MeshTriangles 69 | { 70 | get { return meshTriangles; } 71 | } 72 | 73 | /// 74 | /// Creates a new MeshCreator. 75 | /// 76 | /// Used to create the initial size of the MeshTriangles list. 77 | /// Used to set the initial size of the MeshVertices list. 78 | public MeshCreator(int estimatedTriangleCount, int estimatedVertexCount) 79 | { 80 | meshTriangles = new List(estimatedTriangleCount); 81 | meshVertices = new List(estimatedVertexCount); 82 | } 83 | 84 | /// 85 | /// Clear the triangles and vertices. 86 | /// 87 | public void Clear() 88 | { 89 | meshTriangles.Clear(); 90 | 91 | for (var i = 0; i < meshVertices.Count; i++) 92 | meshVertices[i].VertexIndex = -1; 93 | 94 | meshVertices.Clear(); 95 | } 96 | 97 | /// 98 | /// Adds a vertex to the MeshCreator and assigns it a VertexIndex. 99 | /// If it already has a VertexIndex, this method does nothing. 100 | /// 101 | /// The vertex to be added. 102 | public MeshVertex AddVertex(MeshVertex vertex) 103 | { 104 | if (vertex.VertexIndex != -1) 105 | return vertex; 106 | 107 | vertex.VertexIndex = meshVertices.Count; 108 | meshVertices.Add(vertex); 109 | return vertex; 110 | } 111 | 112 | /// 113 | /// Adds a triangle to the MeshCreator. Adds the vertices to the MeshCreator 114 | /// if they aren't already added. 115 | /// 116 | /// The first vertex in clockwise order. 117 | /// The second vertex in clockwise order. 118 | /// The third vertex in clockwise order. 119 | /// The created triangle. 120 | public MeshTriangle AddTriangle(MeshVertex a, MeshVertex b, MeshVertex c) 121 | { 122 | AddVertex(a); 123 | AddVertex(b); 124 | AddVertex(c); 125 | 126 | var triangle = new MeshTriangle(a, b, c); 127 | meshTriangles.Add(triangle); 128 | return triangle; 129 | } 130 | 131 | /// 132 | /// Adds two triangles forming a quad to the MeshCreator. Adds the vertices 133 | /// to the MeshCreator if they aren't already added. 134 | /// 135 | /// The first vertex in clockwise order. 136 | /// The second vertex in clockwise order. 137 | /// The third vertex in clockwise order. 138 | /// The fourth vertex in clockwise order. 139 | public void AddQuad(MeshVertex a, MeshVertex b, MeshVertex c, MeshVertex d) 140 | { 141 | AddTriangle(a, b, c); 142 | AddTriangle(a, c, d); 143 | } 144 | 145 | /// 146 | /// Creates a new mesh and fills it via . 147 | /// 148 | /// The filled mesh. 149 | public Mesh CreateMesh() 150 | { 151 | var mesh = new Mesh(); 152 | mesh.MarkDynamic(); 153 | return UpdateMesh(mesh); 154 | } 155 | 156 | /// 157 | /// Takes an existing mesh previously created with or 158 | /// and fills it with the data from this MeshCreator. 159 | /// 160 | /// The mesh to be updated. 161 | /// Should the vertex positions be updated? 162 | /// Should the vertex normals be updated? 163 | /// Should the vertex colors be updated? 164 | /// Should the UVs be updated? 165 | /// 166 | /// Should the triangle composition be updated? This only needs to be called when 167 | /// you assigned new vertices to triangles, not when vertices changed their data 168 | /// internally. Calling this also triggers updating every other property. 169 | /// 170 | /// The filled mesh. 171 | public Mesh UpdateMesh(Mesh mesh, bool updatePositions = true, bool updateNormals = true, bool updateColors = true, bool updateUVs = true, bool updateTriangles = true) 172 | { 173 | if (updateTriangles) 174 | { 175 | mesh.Clear(); 176 | updatePositions = true; 177 | updateNormals = true; 178 | updateColors = true; 179 | updateUVs = true; 180 | } 181 | 182 | var verticesCount = meshVertices.Count; 183 | if ((tempVertices == null) || (tempVertices.Length != verticesCount)) 184 | { 185 | tempVertices = new Vector3[verticesCount]; 186 | tempNormals = new Vector3[verticesCount]; 187 | tempUV = new Vector2[verticesCount]; 188 | tempColors = new Color[verticesCount]; 189 | } 190 | 191 | for (var i = 0; i < verticesCount; i++) 192 | { 193 | var meshVertex = meshVertices[i]; 194 | 195 | if (updatePositions) 196 | tempVertices[i] = meshVertex.Position; 197 | 198 | if (updateNormals) 199 | tempNormals[i] = meshVertex.Normal; 200 | 201 | if (updateColors) 202 | tempColors[i] = meshVertex.Color; 203 | 204 | if (updateUVs) 205 | tempUV[i] = meshVertex.UV; 206 | } 207 | 208 | if (updatePositions) 209 | mesh.vertices = tempVertices; 210 | 211 | if (updateColors) 212 | mesh.colors = tempColors; 213 | 214 | if (updateNormals) 215 | mesh.normals = tempNormals; 216 | 217 | if (updateUVs) 218 | mesh.uv = tempUV; 219 | 220 | if (updateTriangles) 221 | { 222 | var trianglesCount = meshTriangles.Count; 223 | var triangleArrayLength = trianglesCount * 3; 224 | 225 | if ((tempTriangles == null) || (tempTriangles.Length != triangleArrayLength)) 226 | tempTriangles = new int[triangleArrayLength]; 227 | 228 | for (var i = 0; i < trianglesCount; i++) 229 | { 230 | var meshTriangleVertices = meshTriangles[i].MeshVertices; 231 | for (var j = 0; j < 3; j++) 232 | { 233 | tempTriangles[i * 3 + j] = meshTriangleVertices[j].VertexIndex; 234 | } 235 | } 236 | 237 | mesh.triangles = tempTriangles; 238 | } 239 | 240 | if (updatePositions) 241 | mesh.RecalculateBounds(); 242 | 243 | return mesh; 244 | } 245 | } 246 | } -------------------------------------------------------------------------------- /MeshCreator/MeshCreator.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3246b9e96565833498f07158c6df11be 3 | timeCreated: 1467495335 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /MeshCreator/MeshTriangle.cs: -------------------------------------------------------------------------------- 1 | namespace UnityUtilities 2 | { 3 | /// 4 | /// A triangle, consisting of three . 5 | /// Used in a . Cannot be part of more than one MeshCreator. 6 | /// 7 | public class MeshTriangle 8 | { 9 | /// 10 | /// The three vertices that make up this triangle in clockwise order. 11 | /// 12 | public MeshVertex[] MeshVertices { get; private set; } 13 | 14 | /// 15 | /// Creates a new triangle. 16 | /// 17 | /// The first vertex in clockwise order. 18 | /// The second vertex in clockwise order. 19 | /// The third vertex in clockwise order. 20 | public MeshTriangle(MeshVertex a, MeshVertex b, MeshVertex c) 21 | { 22 | MeshVertices = new[] {a, b, c}; 23 | } 24 | 25 | /// 26 | /// Convenience method to update all three vertices at once. 27 | /// 28 | /// The first vertex in clockwise order. 29 | /// The second vertex in clockwise order. 30 | /// The third vertex in clockwise order. 31 | public void Update(MeshVertex a, MeshVertex b, MeshVertex c) 32 | { 33 | MeshVertices[0] = a; 34 | MeshVertices[1] = b; 35 | MeshVertices[2] = c; 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /MeshCreator/MeshTriangle.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 87e3e75024f9b5f449c84e56724c6b4b 3 | timeCreated: 1467495335 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /MeshCreator/MeshVertex.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace UnityUtilities 4 | { 5 | /// 6 | /// A vertex in a . Can -and should!- be shared by multiple triangles. 7 | /// Used in a . Cannot be part of more than one MeshCreator. 8 | /// 9 | public class MeshVertex 10 | { 11 | /// 12 | /// The local position of the vertex inside the mesh. 13 | /// 14 | public Vector3 Position { get; set; } 15 | 16 | /// 17 | /// The normal of the vertex. 18 | /// 19 | public Vector3 Normal { get; set; } 20 | 21 | /// 22 | /// The color of the vertex. 23 | /// 24 | public Color Color { get; set; } 25 | 26 | /// 27 | /// The first UV coordinates of the vertex. 28 | /// 29 | public Vector2 UV { get; set; } 30 | 31 | /// 32 | /// The index of this vertex inside the MeshCreator.MeshVertices list. Automatically assigned 33 | /// by MeshCreator.AddVertex(), MeshCreator.AddTriangle() and MeshCreator.AddQuad(). 34 | /// Used to identify the vertex for actually creating the Mesh in MeshCreator.UpdateMesh(). 35 | /// 36 | public int VertexIndex { get; internal set; } 37 | 38 | /// 39 | /// Creates a new MeshVertex. 40 | /// 41 | /// The local position of the vertex inside the mesh. 42 | /// The normal of the vertex. 43 | /// The color of the vertex. 44 | /// The first UV coordinates of the vertex. 45 | public MeshVertex(Vector3 position, Vector3 normal = default(Vector3), Color color = new Color(), Vector2 uv = new Vector2()) 46 | { 47 | Position = position; 48 | Normal = normal; 49 | Color = color; 50 | UV = uv; 51 | VertexIndex = -1; 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /MeshCreator/MeshVertex.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7017d87be61c0fe4ba4f573f38980a23 3 | timeCreated: 1467495335 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /MeshCreator/README.md: -------------------------------------------------------------------------------- 1 | # MeshCreator 2 | 3 | Stores the vertex/triangle data of a mesh in easily modifiable form - which can then be used to actually create or update a mesh. 4 | 5 | ## Example 6 | 7 | ```C# 8 | [RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))] 9 | public class MeshCreatorExample : MonoBehaviour 10 | { 11 | MeshCreator meshCreator; 12 | MeshFilter meshFilter; 13 | 14 | void Awake() 15 | { 16 | // We will create a cube, so we set 12 triangles (6 sides á 2 triangles) and 8 vertices. 17 | // To do a cube with proper shading, we should actually create 4 vertices per side and have 18 | // normals pointing outwards, but for this example let's keep it simple. 19 | meshCreator = new MeshCreator(12, 8); 20 | 21 | // Create the vertices 22 | var behindUpperLeft = new MeshVertex(new Vector3(-1, 1, 1), Vector3.zero, Color.white); 23 | var behindUpperRight = new MeshVertex(new Vector3(1, 1, 1), Vector3.zero, Color.white); 24 | var behindLowerLeft = new MeshVertex(new Vector3(-1, -1, 1), Vector3.zero, Color.white); 25 | var behindLowerRight = new MeshVertex(new Vector3(1, -1, 1), Vector3.zero, Color.white); 26 | var frontUpperLeft = new MeshVertex(new Vector3(-1, 1, -1), Vector3.zero, Color.white); 27 | var frontUpperRight = new MeshVertex(new Vector3(1, 1, -1), Vector3.zero, Color.white); 28 | var frontLowerLeft = new MeshVertex(new Vector3(-1, -1, -1), Vector3.zero, Color.white); 29 | var frontLowerRight = new MeshVertex(new Vector3(1, -1, -1), Vector3.zero, Color.white); 30 | 31 | // Create the quads 32 | meshCreator.AddQuad(frontUpperLeft, frontUpperRight, frontLowerRight, frontLowerLeft); 33 | meshCreator.AddQuad(behindUpperRight, behindUpperLeft, behindLowerLeft, behindLowerRight); 34 | meshCreator.AddQuad(frontUpperRight, behindUpperRight, behindLowerRight, frontLowerRight); 35 | meshCreator.AddQuad(frontUpperLeft, frontLowerLeft, behindLowerLeft, behindUpperLeft); 36 | meshCreator.AddQuad(frontUpperLeft, behindUpperLeft, behindUpperRight, frontUpperRight); 37 | meshCreator.AddQuad(frontLowerLeft, frontLowerRight, behindLowerRight, behindLowerLeft); 38 | 39 | // Create a new mesh and set it in the mesh filter 40 | meshFilter = GetComponent(); 41 | meshFilter.sharedMesh = meshCreator.CreateMesh(); 42 | meshFilter.sharedMesh.RecalculateNormals(); 43 | } 44 | 45 | void Update() 46 | { 47 | // Make the vertices wobble independently 48 | for (int i = 0; i < meshCreator.MeshVerticesCount; i++) 49 | { 50 | MeshVertex meshVertex = meshCreator.MeshVertices[i]; 51 | float scale = Mathf.Lerp(2f, 4f, Mathf.Abs(Mathf.Cos(Time.time + i / 4f))); 52 | meshVertex.Position = meshVertex.Position.normalized * scale; 53 | } 54 | 55 | // Update the mesh 56 | meshCreator.UpdateMesh(meshFilter.sharedMesh); 57 | } 58 | } 59 | ``` 60 | 61 | ## Dependencies 62 | 63 | None. -------------------------------------------------------------------------------- /MeshCreator/README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7615b80d7d59f42438b505c1da0b9986 3 | timeCreated: 1467505581 4 | licenseType: Pro 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /NoiseOutputValue.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a5375a70fdbb70d47a640acffd4367b5 3 | folderAsset: yes 4 | timeCreated: 1462732529 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /NoiseOutputValue/NoiseOutputValue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | using Random = UnityEngine.Random; 4 | 5 | namespace UnityUtilities 6 | { 7 | /// 8 | /// Provides a fluidly changing output value using . 9 | /// 10 | [Serializable] 11 | public class NoiseOutputValue 12 | { 13 | /// 14 | /// The range of the output value. 15 | /// 16 | [Tooltip("The range of the output value.")] 17 | [SerializeField] RangeFloat range = new RangeFloat(0f, 1f); 18 | 19 | /// 20 | /// How fast to scroll over the perlin noise. 21 | /// 22 | [Tooltip("How fast to scroll over the perlin noise.")] 23 | [SerializeField] float speed = 0.1f; 24 | 25 | /// 26 | /// Has the random seed already been initialized? 27 | /// 28 | bool initialized; 29 | 30 | /// 31 | /// The scrolling time used as x for the 2d perlin function. 32 | /// 33 | float perlinTime; 34 | 35 | /// 36 | /// The seed used as y for the 2d perlin function. This scrolls too, but very slowly, to avoid early looping. 37 | /// 38 | float perlinSeed; 39 | 40 | /// 41 | /// The current output value between range.from and range.to. 42 | /// 43 | public float OutputValue { get; private set; } 44 | 45 | /// 46 | /// The range of the output value. 47 | /// 48 | public RangeFloat Range { get { return range; } } 49 | 50 | /// 51 | /// The range of the output speed. 52 | /// 53 | public float Speed { get { return speed; } } 54 | 55 | /// 56 | /// Updates the . Should be called once per frame before using for the first time. 57 | /// 58 | /// Optional: Provide the deltaTime for the scrolling if it isn't Time.deltaTime. 59 | public void Progress(float? deltaTime = null) 60 | { 61 | if (!initialized) 62 | { 63 | initialized = true; 64 | 65 | // Gets a random seed to use as y for the 2d perlin function. 66 | perlinSeed = Random.value * 10f; 67 | } 68 | 69 | if (deltaTime == null) 70 | deltaTime = Time.deltaTime; 71 | 72 | var delta = deltaTime.Value * speed; 73 | 74 | // Scroll forward in time. 75 | perlinTime += delta; 76 | 77 | // Scroll very slowing through the seed to avoid looping at PerlinTime values of 10 and higher. 78 | // It will still loop, but only after 79 | perlinSeed += delta / 100f; 80 | 81 | // Update the output value. 82 | OutputValue = range.Lerp(Mathf.PerlinNoise(perlinTime, perlinSeed)); 83 | } 84 | } 85 | } -------------------------------------------------------------------------------- /NoiseOutputValue/NoiseOutputValue.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 13a0a570d62aefd4bb1bdd3645b36f78 3 | timeCreated: 1462647054 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /NoiseOutputValue/README.md: -------------------------------------------------------------------------------- 1 | # NoiseOutputValue 2 | 3 | Enter a range and a speed in the editor and you will get an output value in that ranges that fluctuates fluently over time using [Perlin Noise](http://docs.unity3d.com/ScriptReference/Mathf.PerlinNoise.html). 4 | 5 | ## Example 6 | 7 | ![NoiseOutputValueExample Editor Screenshot](https://raw.githubusercontent.com/TobiasWehrum/unity-utilities/master/_Images/NoiseOutputValueExample.gif) 8 | 9 | ```C# 10 | public class NoiseOutputValueExample : MonoBehaviour 11 | { 12 | [SerializeField] NoiseOutputValue positionNoise; 13 | [SerializeField] Transform sphere; 14 | 15 | private void Update() 16 | { 17 | // Updates the value with Time.deltaTime*speed 18 | positionNoise.Update(); 19 | 20 | // Sets the y position at the current output value 21 | sphere.transform.position = new Vector3(0, positionNoise.OutputValue, 0f); 22 | } 23 | } 24 | ``` 25 | 26 | ## Dependencies 27 | 28 | * [Range](https://github.com/TobiasWehrum/unity-utilities/tree/master/Range) -------------------------------------------------------------------------------- /NoiseOutputValue/README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7280d3c5926236545ad43635ad87a21f 3 | timeCreated: 1462746256 4 | licenseType: Free 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tobi's Unity Utilities 2 | 3 | Over the years, I've worked on a lot of [projects](http://dragonlab.de/portfolio) and [game jam prototypes](http://blog.dragonlab.de/tag/unity) with Unity3D and there are some pieces of code that I've needed time and time again. I'm sharing them here in the hopes that they are useful for you too! 4 | 5 | Everything is released under the [MIT License](https://opensource.org/licenses/MIT). 6 | 7 | If you find any bugs or have suggestions, please add an [Issue](https://github.com/TobiasWehrum/unity-utilities/issues) here or send me a mail at Tobias.Wehrum@dragonlab.de. 8 | 9 | ## Overview 10 | * [Countdown](https://github.com/TobiasWehrum/unity-utilities/tree/master/Countdown): Useful for things like cooldowns or spawn delays. It is also helpful for tweening things by using the `PercentElapsed` property. 11 | * [EditorHelper](https://github.com/TobiasWehrum/unity-utilities/tree/master/EditorHelper): Gets the `[Tooltip]` attribute content of fields for editor classes. Might get more helper methods in the future. 12 | * [LINQExtensions](https://github.com/TobiasWehrum/unity-utilities/tree/master/LINQExtensions): A collection of extension methods for `IEnumerable`, `List` and arrays. 13 | * [MathHelper](https://github.com/TobiasWehrum/unity-utilities/tree/master/MathHelper): Helper methods for framerate-independent eased lerping, mapping and angles. 14 | * [MeshCreator](https://github.com/TobiasWehrum/unity-utilities/tree/master/MeshCreator): Makes it more convenient to create meshes via code. 15 | * [NoiseOutputValue](https://github.com/TobiasWehrum/unity-utilities/tree/master/NoiseOutputValue): Enter a range and a speed in the editor, get an output value that fluctuates over time using [Perlin Noise](http://docs.unity3d.com/ScriptReference/Mathf.PerlinNoise.html). 16 | * [RandomBag](https://github.com/TobiasWehrum/unity-utilities/tree/master/RandomBag): A `RandomBag` gives you random items from a group while ensuring that in a certain interval every item was given back the same number of times. 17 | * [Range](https://github.com/TobiasWehrum/unity-utilities/tree/master/Range): Editable data types that take an `int`/`float` range. Used for things like "Spawn 2 to 4 enemies." 18 | * [RollingArray](https://github.com/TobiasWehrum/unity-utilities/tree/master/RollingArray): Collection that keeps the last x elements that are added to it. 19 | * [Singleton](https://github.com/TobiasWehrum/unity-utilities/tree/master/Singleton): Allows easy and convenient creation of a Singleton. Optionally makes a Singleton persist between scenes while ensuring that only one exists. 20 | * [UnityHelper](https://github.com/TobiasWehrum/unity-utilities/tree/master/UnityHelper): Contains a plethora of useful extensions and helpers for Transform, GameObject, Vector2/3/4, Rect and more. 21 | * [XmlHelper](https://github.com/TobiasWehrum/unity-utilities/tree/master/XmlHelper): Serializes data to XML strings and makes accessing optional element content and attributes in general XMLs easier. 22 | 23 | ## Usage 24 | 25 | To use the scripts, just drop them into the Assets folder of your projects. Or better yet, make an "Assets/Extensions/TobisUnityUtitilites" folder and drop them there. Hurray for proper organisation. 26 | 27 | You can also just use selected scripts, but you should check the "Dependencies" section in the respective folder to make sure you copy everything you need. 28 | 29 | ## Documentation 30 | 31 | The class documentation is available [here](http://tobiaswehrum.github.io/UnityUtilities/html/annotated.html). 32 | 33 | ## Changelog 34 | * 2016-10-23: Fixed bugs/improved [Singleton](https://github.com/TobiasWehrum/unity-utilities/tree/master/Singleton). Added EasedLerp methods for float in [MathHelper](https://github.com/TobiasWehrum/unity-utilities/tree/master/MathHelper) and Vector2, Vector3 and Color in [UnityHelper](https://github.com/TobiasWehrum/unity-utilities/tree/master/UnityHelper). Added CalculateCentroid in [UnityHelper](https://github.com/TobiasWehrum/unity-utilities/tree/master/UnityHelper) for arrays/lists of Vector2/3/4. 35 | * 2016-10-22: Added [MathHelper](https://github.com/TobiasWehrum/unity-utilities/tree/master/MathHelper). Added randomization helper methods to [UnityHelper](https://github.com/TobiasWehrum/unity-utilities/tree/master/UnityHelper). 36 | * 2016-07-03: Added [MeshCreator](https://github.com/TobiasWehrum/unity-utilities/tree/master/MeshCreator). 37 | * 2016-06-19: Added [XmlHelper](https://github.com/TobiasWehrum/unity-utilities/tree/master/XmlHelper). 38 | * 2016-06-08: Added [UnityHelper](https://github.com/TobiasWehrum/unity-utilities/tree/master/UnityHelper). 39 | * 2016-06-05: Added [EditorHelper](https://github.com/TobiasWehrum/unity-utilities/tree/master/EditorHelper) and [RollingArray](https://github.com/TobiasWehrum/unity-utilities/tree/master/RollingArray). Added `[Tooltip]` for `NoiseOutputValue` and edited the existing `PropertyDrawer` to use tooltips. 40 | * 2016-05-15: Added [LINQExtensions](https://github.com/TobiasWehrum/unity-utilities/tree/master/LINQExtensions) and [RandomBag](https://github.com/TobiasWehrum/unity-utilities/tree/master/RandomBag). 41 | * 2016-05-09: Added the [class documentation website](http://tobiaswehrum.github.io/UnityUtilities/html/annotated.html). 42 | * 2016-05-08: Added [Countdown](https://github.com/TobiasWehrum/unity-utilities/tree/master/Countdown), [NoiseOutputValue](https://github.com/TobiasWehrum/unity-utilities/tree/master/NoiseOutputValue), [Range](https://github.com/TobiasWehrum/unity-utilities/tree/master/Range) and [Singleton](https://github.com/TobiasWehrum/unity-utilities/tree/master/Singleton). 43 | * 2017-09-26: Removed .gitignore. Update note: This will break any scene you use example scripts in and might cause one-time .meta conflicts if you track your projects with git and didn't remove the .gitignore yourself. I'm sorry. -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: eff04722ac3129d42bfdb29011dc918d 3 | timeCreated: 1462727730 4 | licenseType: Free 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /RandomBag.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 07f4be29056efd74a915c53e4b50707d 3 | folderAsset: yes 4 | timeCreated: 1463231729 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /RandomBag/README.md: -------------------------------------------------------------------------------- 1 | # RandomBag 2 | 3 | A RandomBag ensures that per interval [fillings * itemCount], every different item in it will be given back [fillings] times. Once the bag is empty, it is automatically refilled, either from a fixed array or by calling a delegate. 4 | 5 | An example for that is used in [some implementations of Tetris](http://tetris.wikia.com/wiki/Random_Generator). The bag is filled with one instance (fillings=1) of each of the seven different pieces. Every time the next piece is needed, a random one is taken out of the bag until the bag is empty. That way, any two pieces are never longer than 14 pulls apart - and even that is only the case if the first and the last piece in the bag are the same one. 6 | 7 | ## Example 8 | 9 | ```C# 10 | public class RandomBagExample : MonoBehaviour 11 | { 12 | private enum TetrisPiece 13 | { 14 | Line, Square, T, J, L, S, Z 15 | } 16 | 17 | RandomBag pieceBag; 18 | 19 | void Awake() 20 | { 21 | // Get an array with each value of TetrisPiece 22 | TetrisPiece[] tetrisPieceArray = (TetrisPiece[]) Enum.GetValues(typeof (TetrisPiece)); 23 | 24 | // Create the bag containing two instances of every value of TetrisPiece 25 | pieceBag = new RandomBag(tetrisPieceArray, 2); 26 | 27 | // Gets 50 items from the bag. The bag will be filled with 14 TetrisPieces and 28 | // automatically refilled with 14 more when needed. No two pieces will ever be 29 | // more than 14 calls apart - and even that will only happen if that piece was 30 | // the first and last item in the current 14 piece bag filling. 31 | StringBuilder str = new StringBuilder(); 32 | for (var i = 0; i < 50; i++) 33 | { 34 | str.Append(pieceBag.PopRandomItem()); 35 | str.Append(", "); 36 | } 37 | 38 | Debug.Log(str); 39 | 40 | // Example output: 41 | // T, Z, J, Square, Z, S, L, L, S, Line, J, Line, Square, T, Square, Z, 42 | // S, T, Line, Square, Z, T, J, Line, S, L, J, L, S, Z, Line, J, Line, 43 | // J, L, L, S, T, T, Z, Square, Square, Z, T, S, Z, J, J, L, Line, 44 | } 45 | } 46 | ``` 47 | 48 | ## Dependencies 49 | 50 | * [LINQExtensions](https://github.com/TobiasWehrum/unity-utilities/tree/master/LINQExtensions) -------------------------------------------------------------------------------- /RandomBag/README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c42cfaa3e607e084c88ab39259f6fc0c 3 | timeCreated: 1463270294 4 | licenseType: Pro 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /RandomBag/RandomBag.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace UnityUtilities 5 | { 6 | /// 7 | /// A RandomBag ensures that per interval [fillings * itemCount], every different item in it 8 | /// will be given back [fillings] times. Once the bag is empty, it is automatically refilled, 9 | /// either from a fixed array or by calling a delegate. 10 | /// 11 | /// An example for that is used in some implementations of Tetris. The bag is filled with one 12 | /// instance (fillings=1) of each of the seven different pieces. Every time the next piece is 13 | /// needed, a random one is taken out of the bag until the bag is empty. That way, any two pieces 14 | /// are never longer than 7 pulls apart - and even that is only the case if the first and the last 15 | /// piece in the bag are the same one. 16 | /// 17 | /// The type to be put in the bag. 18 | public class RandomBag 19 | { 20 | /// 21 | /// A temporary array used internally for shuffling. 22 | /// 23 | List tempList; 24 | 25 | /// 26 | /// The bag itself. 27 | /// 28 | Stack bag; 29 | 30 | /// 31 | /// One of the two methods of filling the bag. When empty, this array is added to the bag [fillings] times. 32 | /// 33 | T[] refillItems; 34 | 35 | /// 36 | /// One of the two methods of filling the bag. When empty, this delegate is called [fillings] times to fill the bag. 37 | /// 38 | Action> refillDelegate; 39 | 40 | /// 41 | /// How often / is used to fill the bag when the bag is empty. 42 | /// 43 | public int Fillings { get; private set; } 44 | 45 | /// 46 | /// Initializes the bag with a array of items to refill it and the number of times the array is added to the bag. 47 | /// Once the bag is empty, the array will automatically be added to the bag [fillings] times again. 48 | /// 49 | /// The array to fill the bag [fillings] times with. 50 | /// The number of times the array is added to the bag. 51 | public RandomBag(T[] refillItems, int fillings) 52 | { 53 | bag = new Stack(); 54 | tempList = new List(); 55 | 56 | SetRefillItems(refillItems); 57 | Fillings = fillings; 58 | 59 | Reset(); 60 | } 61 | 62 | /// 63 | /// Initializes the bag by calling the refillDelegate multiple times. 64 | /// Once the bag is empty, the delegate will automatically be called [fillings] times again. 65 | /// 66 | /// The delegate that will be called [fillings] times to fill the bag. 67 | /// The number of times refillDelegate is called. 68 | public RandomBag(Action> refillDelegate, int fillings) 69 | { 70 | bag = new Stack(); 71 | tempList = new List(); 72 | 73 | SetRefillDelegate(refillDelegate); 74 | Fillings = fillings; 75 | 76 | Reset(); 77 | } 78 | 79 | /// 80 | /// Sets the refill method to "When empty, this delegate is called [fillings] times to fill the bag." 81 | /// 82 | /// The delegate that will be called [fillings] times to fill the bag once it's empty. 83 | public void SetRefillDelegate(Action> refillDelegate) 84 | { 85 | this.refillDelegate = refillDelegate; 86 | refillItems = null; 87 | } 88 | 89 | /// 90 | /// Sets the refill method to "When empty, this array is added to the bag [fillings] times." 91 | /// 92 | /// The array to fill the bag [fillings] times with once it's empty. 93 | public void SetRefillItems(T[] refillItems) 94 | { 95 | if (refillItems.Length == 0) 96 | throw new ArgumentOutOfRangeException("refillItems", "RefillItems needs to contain at least one item."); 97 | 98 | this.refillItems = refillItems; 99 | refillDelegate = null; 100 | } 101 | 102 | /// 103 | /// Clears the bag and refills it. 104 | /// 105 | public void Reset() 106 | { 107 | // Empty the bag 108 | bag.Clear(); 109 | 110 | // Call the delegate/add the array [Fillings] times 111 | for (var i = 0; i < Fillings; i++) 112 | { 113 | if (refillDelegate != null) 114 | { 115 | refillDelegate(tempList); 116 | 117 | if (tempList.Count == 0) 118 | throw new Exception("RandomBag.refillDelegate didn't add any items to the bag."); 119 | } 120 | else 121 | { 122 | tempList.AddRange(refillItems); 123 | } 124 | } 125 | 126 | // Shuffle the temporary list 127 | tempList.Shuffle(); 128 | 129 | // Fill the temporary list into the bag 130 | for (var i = 0; i < tempList.Count; i++) 131 | { 132 | bag.Push(tempList[i]); 133 | } 134 | 135 | // Clear the temporary list 136 | tempList.Clear(); 137 | } 138 | 139 | /// 140 | /// Gets a random item from the bag. If there is no item in the bag, it is automatically refilled first. 141 | /// 142 | /// A random item from the bag. 143 | public T PopRandomItem() 144 | { 145 | // If the bag is empty, refill it 146 | if (bag.Count == 0) 147 | Reset(); 148 | 149 | // Return an item 150 | return bag.Pop(); 151 | } 152 | 153 | /// 154 | /// Gets multiple random items from the bag. If there aren't enough item in the bag, it is automatically refilled. 155 | /// 156 | /// How many items to pull from the bag. 157 | /// A number of random items from the bag. 158 | public T[] PopRandomItems(int count) 159 | { 160 | var randomItems = new T[count]; 161 | 162 | // Add [count] items to the bag 163 | for (var i = 0; i < count; i++) 164 | { 165 | // If the bag is empty, refill it 166 | if (bag.Count == 0) 167 | Reset(); 168 | 169 | // Add a random item to the result array 170 | randomItems[i] = bag.Pop(); 171 | } 172 | 173 | return randomItems; 174 | } 175 | 176 | /// 177 | /// Gets an enumerator returning an endless number of items, automatically refilling the bag when empty. 178 | /// 179 | /// An enumerator returning an endless number of items from the bag. 180 | public IEnumerator GetEndlessEnumerator() 181 | { 182 | while (true) 183 | yield return PopRandomItem(); 184 | } 185 | 186 | /// 187 | /// Gets an enumerator returning the items remaining in the bag. 188 | /// 189 | /// An enumerator returning the items currently remaining in the bag. 190 | public IEnumerator GetRemainderEnumerator() 191 | { 192 | while (bag.Count > 0) 193 | yield return bag.Pop(); 194 | } 195 | 196 | /// 197 | /// The items currently remaining in the bag. 198 | /// 199 | public int RemainderCount 200 | { 201 | get { return bag.Count; } 202 | } 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /RandomBag/RandomBag.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0e9e6b1d3fe40544ca29829e80233895 3 | timeCreated: 1463231729 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Range.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 99780946aee1802469d2804ec9d050be 3 | folderAsset: yes 4 | timeCreated: 1462732529 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Range/Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 326c0927f62b6ae4a8175ede59eb3f8a 3 | folderAsset: yes 4 | timeCreated: 1462732529 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Range/Editor/RangeFloatPropertyDrawer.cs: -------------------------------------------------------------------------------- 1 | using UnityEditor; 2 | 3 | namespace UnityUtilities 4 | { 5 | [CustomPropertyDrawer(typeof (RangeFloat))] 6 | public class RangeFloatPropertyDrawer : RangePropertyDrawerBase 7 | { 8 | } 9 | } -------------------------------------------------------------------------------- /Range/Editor/RangeFloatPropertyDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 114c3186bf56f39459f8a6cf4e44e5bf 3 | timeCreated: 1462732529 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Range/Editor/RangeIntPropertyDrawer.cs: -------------------------------------------------------------------------------- 1 | using UnityEditor; 2 | 3 | namespace UnityUtilities 4 | { 5 | [CustomPropertyDrawer(typeof (RangeInt))] 6 | public class RangeIntPropertyDrawer : RangePropertyDrawerBase 7 | { 8 | } 9 | } -------------------------------------------------------------------------------- /Range/Editor/RangeIntPropertyDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b0fd8cdb8b637574284efbe5ae471625 3 | timeCreated: 1462732529 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Range/Editor/RangePropertyDrawerBase.cs: -------------------------------------------------------------------------------- 1 | using UnityEditor; 2 | using UnityEngine; 3 | 4 | namespace UnityUtilities 5 | { 6 | public class RangePropertyDrawerBase : PropertyDrawer 7 | { 8 | public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) 9 | { 10 | // Using BeginProperty / EndProperty on the parent property means that 11 | // prefab override logic works on the entire property. 12 | EditorGUI.BeginProperty(position, label, property); 13 | 14 | // Set tooltip, if any 15 | label.tooltip = EditorHelper.GetTooltip(fieldInfo); 16 | 17 | // Draw label 18 | position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label); 19 | 20 | // Don't make child fields be indented 21 | var indent = EditorGUI.indentLevel; 22 | EditorGUI.indentLevel = 0; 23 | 24 | // Calculate rects 25 | var fromLabelWidth = 40; 26 | var toLabelWidth = 20; 27 | var afterValueFieldOffset = 5; 28 | var valuesWidth = (position.width - fromLabelWidth - toLabelWidth - afterValueFieldOffset) / 2f; 29 | var fromLabelRect = new Rect(position.x, position.y, fromLabelWidth, position.height); 30 | var fromRect = new Rect(fromLabelRect.xMax, position.y, valuesWidth, position.height); 31 | var toLabelRect = new Rect(fromRect.xMax + afterValueFieldOffset, position.y, toLabelWidth, position.height); 32 | var toRect = new Rect(toLabelRect.xMax, position.y, valuesWidth, position.height); 33 | 34 | // Draw fields - passs GUIContent.none to each so they are drawn without labels 35 | EditorGUI.LabelField(fromLabelRect, "From"); 36 | EditorGUI.PropertyField(fromRect, property.FindPropertyRelative("from"), GUIContent.none); 37 | EditorGUI.LabelField(toLabelRect, "to"); 38 | EditorGUI.PropertyField(toRect, property.FindPropertyRelative("to"), GUIContent.none); 39 | 40 | // Set indent back to what it was 41 | EditorGUI.indentLevel = indent; 42 | 43 | EditorGUI.EndProperty(); 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /Range/Editor/RangePropertyDrawerBase.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e5ea3b62097d6bc45bce230ae024eb93 3 | timeCreated: 1462732534 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Range/README.md: -------------------------------------------------------------------------------- 1 | # RangeInt/Float 2 | 3 | `RangeInt`/`RangeFloat` are editable data types that take an int/float range. Used for things like "Spawn 2 to 4 enemies." 4 | 5 | ## Example 6 | ![RangeExample Editor Screenshot](https://raw.githubusercontent.com/TobiasWehrum/unity-utilities/master/_Images/RangeExample.png) 7 | 8 | ```C# 9 | public class RangeExample : MonoBehaviour 10 | { 11 | [SerializeField] RangeInt amountRange; 12 | [SerializeField] RangeFloat numberRange; 13 | 14 | private void Awake() 15 | { 16 | // Get a random number in amountRange 17 | int amount = amountRange.RandomInclusive; 18 | 19 | // Output [amount] numbers 20 | for (int i = 0; i < amount; i++) 21 | { 22 | // Transform [i..(amount-1)] to [0..1] 23 | var t = ((float)i / (amount - 1)); 24 | 25 | // Mathf.Lerp(numberRange.From, numberRange.To, t) 26 | Debug.Log(numberRange.Lerp(t)); 27 | } 28 | } 29 | } 30 | } 31 | ``` 32 | 33 | ## Dependencies 34 | 35 | None. -------------------------------------------------------------------------------- /Range/README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1f519e37ca0101f479accde1398c5e06 3 | timeCreated: 1462746256 4 | licenseType: Free 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Range/RangeFloat.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | namespace UnityUtilities 5 | { 6 | /// 7 | /// Provides input for a float value range in the editor and various convencience functions to work with that range. 8 | /// 9 | [Serializable] 10 | public class RangeFloat 11 | { 12 | /// 13 | /// The range start. 14 | /// 15 | [SerializeField] float from; 16 | 17 | /// 18 | /// The range end. 19 | /// 20 | [SerializeField] float to; 21 | 22 | /// 23 | /// The range start. 24 | /// 25 | public float From { get { return from; } } 26 | 27 | /// 28 | /// The range end. 29 | /// 30 | public float To { get { return to; } } 31 | 32 | /// 33 | /// Returns how wide the range between from and to is. 34 | /// 35 | public float Range { get { return to - from; } } 36 | 37 | /// 38 | /// Returns a random number between from [inclusive] and to [inclusive]. 39 | /// 40 | public float RandomInclusive { get { return UnityEngine.Random.Range(from, to); } } 41 | 42 | /// 43 | /// Create a RangeFloat with 0-0 as the range. Needed for the editor. 44 | /// 45 | public RangeFloat() 46 | { 47 | } 48 | 49 | /// 50 | /// Create a RangeFloat with a certain range. 51 | /// 52 | /// Lower range bound. 53 | /// Upper range bound. 54 | public RangeFloat(float @from, float to) 55 | { 56 | this.@from = @from; 57 | this.to = to; 58 | } 59 | 60 | /// 61 | /// Linearly interpolates between to and from by t. 62 | /// 63 | /// How much to interpolate. Clamped between 0 and 1. 0 is [to] and 1 is [from]. 64 | /// 65 | public float Lerp(float t) 66 | { 67 | return Mathf.Lerp(from, to, t); 68 | } 69 | 70 | /// 71 | /// Linearly interpolates between to and from by t. 72 | /// 73 | /// How much to interpolate. 0 is [to] and 1 is [from]. 74 | /// 75 | public float LerpUnclamped(float t) 76 | { 77 | return Mathf.LerpUnclamped(from, to, t); 78 | } 79 | 80 | /// 81 | /// Calculates the linear parameter t that produces the interpolant value within the range [from, to]. 82 | /// 83 | /// 84 | /// 85 | public float InverseLerp(float value) 86 | { 87 | return Mathf.InverseLerp(from, to, value); 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /Range/RangeFloat.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 94ef56b85cf1a22498b4299d49dcedeb 3 | timeCreated: 1462732529 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Range/RangeInt.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | namespace UnityUtilities 5 | { 6 | /// 7 | /// Provides input for a value range in the editor and various convencience functions to work with that range. 8 | /// 9 | [Serializable] 10 | public class RangeInt 11 | { 12 | /// 13 | /// The range start. 14 | /// 15 | [SerializeField] int from; 16 | 17 | /// 18 | /// The range end. 19 | /// 20 | [SerializeField] int to; 21 | 22 | /// 23 | /// The range start. 24 | /// 25 | public int From { get { return from; } } 26 | 27 | /// 28 | /// The range end. 29 | /// 30 | public int To { get { return to; } } 31 | 32 | /// 33 | /// Returns how wide the range between from and to is. 34 | /// 35 | public int Range { get { return to - from; } } 36 | 37 | /// 38 | /// Returns a random number between from [inclusive] and to [inclusive]. 39 | /// 40 | public int RandomInclusive { get { return UnityEngine.Random.Range(from, to + 1); } } 41 | 42 | /// 43 | /// Returns a random number between from [inclusive] and to [exclusive]. 44 | /// 45 | public int RandomExclusive { get { return UnityEngine.Random.Range(from, to); } } 46 | 47 | /// 48 | /// Create a RangeFloat with 0-0 as the range. Needed for the editor. 49 | /// 50 | public RangeInt() 51 | { 52 | } 53 | 54 | /// 55 | /// Create a RangeFloat with a certain range. 56 | /// 57 | /// Lower range bound. 58 | /// Upper range bound. 59 | public RangeInt(int from, int to) 60 | { 61 | this.from = from; 62 | this.to = to; 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /Range/RangeInt.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0a7ceba396b3b4b42ab9086be9ec2528 3 | timeCreated: 1462732529 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /RollingArray.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 786224cc203cfd347a3b735a5fcceef9 3 | folderAsset: yes 4 | timeCreated: 1465072662 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /RollingArray/README.md: -------------------------------------------------------------------------------- 1 | # RollingArray 2 | 3 | `RollingArray` is a container that always stores the last arraySize elements added. New elements are added via `Append()`, which automatically rolls over once the maximum number of elements is reached, overwriting the oldest element. `array[i]` always returns the oldest element that still exists + i. That way, this container always stores the last arraySize elements added. 4 | 5 | Adding is O(1), retrieving is O(1) and (with the exception of `GetEnumerator()`) no new memory is allocated after the initial creation. 6 | 7 | You can read more about the `GetEnumerator()` memory problem (and Unity's dreadful memory allocation troubles) here under "Should you avoid foreach loops?": http://www.gamasutra.com/blogs/WendelinReich/20131109/203841/C_Memory_Management_for_Unity_Developers_part_1_of_3.php 8 | 9 | ## Example 10 | 11 | ```C# 12 | public class RollingArrayExample : MonoBehaviour 13 | { 14 | [SerializeField] Transform indicatorObject; 15 | 16 | RollingArray mousePositions; 17 | Camera mainCamera; 18 | 19 | void Awake() 20 | { 21 | // Save the last 50 elements 22 | mousePositions = new RollingArray(50); 23 | 24 | // Cache a reference to the main camera 25 | mainCamera = Camera.main; 26 | } 27 | 28 | void FixedUpdate() 29 | { 30 | // Get the mouse position in a fixed interval 31 | // If we get to 50 positions, the oldest position will be replaced 32 | mousePositions.Append(mainCamera.ScreenToWorldPoint(Input.mousePosition)); 33 | } 34 | 35 | void Update() 36 | { 37 | // Only continue if we have at least one mouse position 38 | if (mousePositions.IsEmpty) 39 | return; 40 | 41 | // Go through all the saved mouse positions from oldest to newest to get the average 42 | Vector2 averagePosition = new Vector2(); 43 | for (var i = 0; i < mousePositions.Count; i++) 44 | { 45 | averagePosition += mousePositions[i]; 46 | } 47 | averagePosition /= mousePositions.Count; 48 | 49 | // Set the indicator object to the average position 50 | indicatorObject.position = averagePosition; 51 | } 52 | } 53 | ``` 54 | 55 | ## Dependencies 56 | 57 | None. -------------------------------------------------------------------------------- /RollingArray/README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 567fa8418a92e63488ee3716c54d54ef 3 | timeCreated: 1465141999 4 | licenseType: Pro 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /RollingArray/RollingArray.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | 6 | namespace UnityUtilities 7 | { 8 | /// 9 | /// A container that always stores the last arraySize elements added. New elements are added 10 | /// via Append(), which automatically rolls over once the maximum number of elements is reached, 11 | /// overwriting the oldest element. array[i] always returns the oldest element that still 12 | /// exists + i. That way, this container always stores the last arraySize elements added. 13 | /// 14 | /// Adding is O(1), retrieving is O(1) and (with the exception of GetEnumerator()) no new memory 15 | /// is allocated after the initial creation. 16 | /// 17 | /// The collection element type. 18 | public class RollingArray : IEnumerable 19 | { 20 | /// 21 | /// The internal storage array. 22 | /// 23 | T[] array; 24 | 25 | /// 26 | /// The index of the next element to be added. 27 | /// 28 | int nextElementIndex; 29 | 30 | /// 31 | /// The current count of filled elements. 32 | /// 33 | public int Count { get; private set; } 34 | 35 | /// 36 | /// Creates a new rolling array with the defined arraySize. 37 | /// 38 | /// 39 | public RollingArray(int arraySize) 40 | { 41 | array = new T[arraySize]; 42 | } 43 | 44 | /// 45 | /// Appends an element to the RollingArray. Automatically rolls over once the maximum number 46 | /// of elements is reached, overwriting the oldest element 47 | /// 48 | /// The element to be added. 49 | public void Append(T element) 50 | { 51 | array[nextElementIndex] = element; 52 | nextElementIndex = (nextElementIndex + 1) % array.Length; 53 | 54 | if (Count < array.Length) 55 | Count++; 56 | } 57 | 58 | /// 59 | /// Is the array empty? 60 | /// 61 | public bool IsEmpty 62 | { 63 | get { return Count == 0; } 64 | } 65 | 66 | /// 67 | /// Gets the oldest element. 68 | /// 69 | /// Thrown when the array is empty. 70 | public T OldestElement 71 | { 72 | get 73 | { 74 | if (IsEmpty) 75 | throw new IndexOutOfRangeException("The array is current empty."); 76 | 77 | return this[0]; 78 | } 79 | } 80 | 81 | /// 82 | /// Gets the latest added element. 83 | /// 84 | /// Thrown when the array is empty. 85 | public T LatestElement 86 | { 87 | get 88 | { 89 | if (IsEmpty) 90 | throw new IndexOutOfRangeException("The array is current empty."); 91 | 92 | return this[Count - 1]; 93 | } 94 | } 95 | 96 | /// 97 | /// Gets/sets an element from/in the array. 0 is always the oldest element that is still in the array, 98 | /// [Count-1] is always the latest added element. 99 | /// 100 | /// This method should NOT be used to add elements - used Append() for that. Only read and write elements 101 | /// that are already in the array. 102 | /// 103 | /// The index. 0 is the oldest, [Count-1] the newest element. 104 | /// The accessed elements. 105 | /// Thrown when accessing an element outside of [0..Count-1]. 106 | public T this[int i] 107 | { 108 | get 109 | { 110 | if ((i < 0) || (i >= Count)) 111 | throw new IndexOutOfRangeException("Index " + i + " (current count: " + Count + ") is out of range."); 112 | 113 | i = (i + nextElementIndex - Count + array.Length) % array.Length; 114 | return array[i]; 115 | } 116 | 117 | set 118 | { 119 | if ((i < 0) || (i >= Count)) 120 | throw new IndexOutOfRangeException("Index " + i + " (current count: " + Count + ") is out of range."); 121 | 122 | i = (i + nextElementIndex - Count + array.Length) % array.Length; 123 | array[i] = value; 124 | } 125 | } 126 | 127 | /// 128 | /// Clears the RollingArray. 129 | /// 130 | public void Clear() 131 | { 132 | nextElementIndex = 0; 133 | Count = 0; 134 | } 135 | 136 | /// 137 | /// Returns an enumerator that iterates through the collection from oldest to newest element. 138 | /// 139 | /// Caveat: Due to the outdated Mono version used in Unity3D, this allocates a small amount of 140 | /// memory, leading to memory fragmentation if called often. You might want to use array[i] instead. 141 | /// 142 | /// Read more about this under "Should you avoid foreach loops?" at: 143 | /// http://www.gamasutra.com/blogs/WendelinReich/20131109/203841/C_Memory_Management_for_Unity_Developers_part_1_of_3.php 144 | /// 145 | /// An enumerator that iterates through the collection from oldest to newest element. 146 | public IEnumerator GetEnumerator() 147 | { 148 | if (IsEmpty) 149 | yield break; 150 | 151 | var index = nextElementIndex - Count; 152 | if (index < 0) 153 | index += array.Length; 154 | 155 | for (int counter = 0; counter < Count; counter++) 156 | { 157 | yield return array[index]; 158 | 159 | index++; 160 | if (index >= array.Length) 161 | index -= array.Length; 162 | } 163 | } 164 | 165 | /// 166 | /// Returns an enumerator that iterates through the collection from oldest to newest element. 167 | /// 168 | /// Caveat: Due to the outdated Mono version used in Unity3D, this allocates a small amount of 169 | /// memory, leading to memory fragmentation if called often. You might want to use array[i] instead. 170 | /// 171 | /// Read more about this under "Should you avoid foreach loops?" at: 172 | /// http://www.gamasutra.com/blogs/WendelinReich/20131109/203841/C_Memory_Management_for_Unity_Developers_part_1_of_3.php 173 | /// 174 | /// An enumerator that iterates through the collection from oldest to newest element. 175 | IEnumerator IEnumerable.GetEnumerator() 176 | { 177 | return GetEnumerator(); 178 | } 179 | } 180 | } -------------------------------------------------------------------------------- /RollingArray/RollingArray.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a0a426c557878204e9bd54dae0a02fc1 3 | timeCreated: 1465072797 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Singleton.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 25de148f6c9456445ada831981d8d467 3 | folderAsset: yes 4 | timeCreated: 1462732529 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Singleton/PersistentSingletonMonoBehaviour.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace UnityUtilities 4 | { 5 | /// 6 | /// Any class that extends from this one will persist through scenes (via ) and can be handily accessed 7 | /// via the static property. 8 | /// If a new scene is loaded with a new instance of this class while another persistent instance already exists, the new instance in the scene is destroyed. 9 | /// This allows you to make this a prefab and have it in all scenes that you are working on. 10 | /// 11 | /// The subclass that extends from this class. 12 | public class PersistentSingletonMonoBehaviour : MonoBehaviour where TSubclass : PersistentSingletonMonoBehaviour 13 | { 14 | static TSubclass instance; 15 | 16 | /// 17 | /// Returns the singleton instance. 18 | /// 19 | public static TSubclass Instance 20 | { 21 | get { return instance; } 22 | } 23 | 24 | /// 25 | /// Returns true if a singleton instance is registered. 26 | /// 27 | protected bool InstanceExists 28 | { 29 | get { return instance != null; } 30 | } 31 | 32 | /// 33 | /// Used to skip the first "OnLevelWasLoaded". 34 | /// 35 | bool skipOnLevelWasLoaded; 36 | 37 | /// 38 | /// If this is the first instance of this type: 39 | /// 1. Register it as the instance. 40 | /// 2. Mark it as . 41 | /// 3. Call . 42 | /// 4. Call . 43 | /// 44 | /// If an instance of this already exists, destroy this instance. 45 | /// 46 | void Awake() 47 | { 48 | // If an instance already exists, destroy this instance. 49 | if (InstanceExists) 50 | { 51 | Destroy(gameObject); 52 | return; 53 | } 54 | 55 | // Register, make persistent and call event methods 56 | instance = (TSubclass) this; 57 | DontDestroyOnLoad(gameObject); 58 | OnPersistentSingletonAwake(); 59 | OnAwakeOrSwitch(); 60 | 61 | // Make sure to skip "OnLevelWasLoaded" for this first Awake - 62 | // we only want to go there when the scene changes. 63 | skipOnLevelWasLoaded = true; 64 | } 65 | 66 | /// 67 | /// If this is the persistent instance and it was destroyed (manually), 68 | /// remove the instance registration. 69 | /// 70 | /// Note: This also means that you need to use 71 | /// 72 | /// protected override void OnDestroy() 73 | /// { 74 | /// base.OnDestroy(); 75 | /// // [Your code] 76 | /// } 77 | /// 78 | /// in subclasses. 79 | /// 80 | protected virtual void OnDestroy() 81 | { 82 | if (instance == this) 83 | { 84 | instance = null; 85 | OnPersistentSingletonDestroyed(); 86 | } 87 | } 88 | 89 | /// 90 | /// Called when the scene is first loaded or was switched. 91 | /// 92 | void OnLevelWasLoaded() 93 | { 94 | // First time? Skip it - it's not a scene switch. 95 | if (skipOnLevelWasLoaded) 96 | return; 97 | 98 | if (Instance == this) 99 | { 100 | // Call the event methods. 101 | OnSceneSwitched(); 102 | OnAwakeOrSwitch(); 103 | } 104 | } 105 | 106 | /// 107 | /// Disable . 108 | /// 109 | protected virtual void Start() 110 | { 111 | // The first "OnLevelWasLoaded" was called already; disable the skipping. 112 | skipOnLevelWasLoaded = false; 113 | } 114 | 115 | /// 116 | /// This method is called when the Awake() method of the first instance of the persistent 117 | /// singleton is done. This is not called if this is a second instance (which is destroyed 118 | /// automatically immediately). 119 | /// 120 | protected virtual void OnPersistentSingletonAwake() 121 | { 122 | } 123 | 124 | /// 125 | /// This method is called when the registered instance of the persistent singleton is either 126 | /// destroyed manually by calling Destroy() or the application is closed. This is not called 127 | /// if this is a second instance (which is destroyed automatically immediately). 128 | /// 129 | protected virtual void OnPersistentSingletonDestroyed() 130 | { 131 | } 132 | 133 | /// 134 | /// This method is called after switching to a new scene. 135 | /// 136 | protected virtual void OnSceneSwitched() 137 | { 138 | } 139 | 140 | /// 141 | /// This method is called immediately after 142 | /// or . 143 | /// 144 | protected virtual void OnAwakeOrSwitch() 145 | { 146 | } 147 | } 148 | } -------------------------------------------------------------------------------- /Singleton/PersistentSingletonMonoBehaviour.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1327f4476a85f614e8d3c49a1412bcb2 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Singleton/README.md: -------------------------------------------------------------------------------- 1 | # SingletonMonoBehaviour and PersistentSingletonMonoBehaviour 2 | 3 | SingletonMonoBehaviour can be used to provide static access to single instances like an EntityManager easily from anywhere in the code. 4 | 5 | PersistentSingletonMonoBehaviour also provides static instance, but additionally marks the object with [`DontDestroyOnLoad`](http://docs.unity3d.com/ScriptReference/Object.DontDestroyOnLoad.html). That is useful for objects like a MusicManager that should continue playing even when switching levels, or a PlayerData singleton that carries the name and statistics of a player between scenes. 6 | 7 | ## A word of caution 8 | 9 | Note that the Singleton pattern has quite a few shortcomings. For example, the instance binding is hardwired in the code, which makes providing an alternative implementation for testing impossible. Unless you are using this for a prototype like a jam game, you might considering using a framework like [Zenject](https://github.com/modesttree/Zenject) or [StrangeIoC](http://strangeioc.github.io/strangeioc) instead. 10 | 11 | ## Examples 12 | 13 | ### SingletonEntityManager 14 | 15 | If the following component is added on a game object in the scene, it could be accessed from anywhere via `SingletonEntityManager.Instance`, e.g.: `SingletonEntityManager.Instance.AddEntity(newEntity);`. This is available even before `SingletonEntityManager.Awake()` is called. 16 | 17 | If you want to use OnDestroy(), you have to override it like shown in the example below. All other MonoBehaviour callbacks can be used as usual. 18 | 19 | ```C# 20 | public class SingletonEntityManager : SingletonMonoBehaviour 21 | { 22 | List entities; 23 | 24 | public IEnumerable Entities 25 | { 26 | get { return entities; } 27 | } 28 | 29 | void Awake() 30 | { 31 | entities = new List(); 32 | } 33 | 34 | // If you want to use OnDestroy(), you have to override it like this 35 | protected override void OnDestroy() 36 | { 37 | base.OnDestroy(); 38 | Debug.Log("Destroyed"); 39 | } 40 | 41 | public void AddEntity(GameObject entity) 42 | { 43 | entities.Add(entity); 44 | } 45 | 46 | public void RemoveEntity(GameObject entity) 47 | { 48 | entities.Remove(entity); 49 | } 50 | } 51 | ``` 52 | 53 | ### SingletonMusicManager 54 | 55 | The `SingletonMusicManager` in the following example can be accessed in the same way, but it is not destroyed when the scenes switches. 56 | 57 | You could make this SingletonMusicManager a prefab and drop it in multiple scenes that you work on. If at any time there are two `SingletonMusicManager`, 58 | the one from the previous scene survives and the new one is destroyed. (For that reason, you should never create an `Awake()` method in a 59 | PersistentSingletonMonoBehaviour. Instead, use `OnPersistentSingletonAwake()` because it is only called on "surviving" instances. Similarily, you shouldn't 60 | have an `OnDestroy()` method which would be called if this is ever destroyed via `Destroy()`; instead, use `OnPersistentSingletonDestroyed()`.) 61 | 62 | Note that `SingletonMusicManager.Instance` is only available after `SingletonMusicManager.Awake()` was called, so if you need it in another `Awake()` 63 | call, you should put the `SingletonMusicManager` higher in the [Script Execution Order](http://docs.unity3d.com/Manual/class-ScriptExecution.html). 64 | 65 | ```C# 66 | public class SingletonMusicManager : PersistentSingletonMonoBehaviour 67 | { 68 | protected override void OnPersistentSingletonAwake() 69 | { 70 | base.OnPersistentSingletonAwake(); 71 | 72 | // Start playing the music the first time Awake() is called 73 | PlayMusic(); 74 | } 75 | 76 | protected override void OnSceneSwitched() 77 | { 78 | base.OnSceneSwitched(); 79 | 80 | // Fade to random song once a new scene is loaded 81 | FadeToRandomSong(); 82 | } 83 | 84 | protected override void OnPersistentSingletonDestroyed() 85 | { 86 | base.OnPersistentSingletonDestroyed(); 87 | 88 | // Stop the music when Destroy() was called on the active instance. 89 | StopMusic(); 90 | } 91 | 92 | public void PlayMusic() 93 | { 94 | Debug.Log("Play music"); 95 | } 96 | 97 | public void StopMusic() 98 | { 99 | Debug.Log("Stop music"); 100 | } 101 | 102 | public void FadeToRandomSong() 103 | { 104 | Debug.Log("Fade to a random song"); 105 | } 106 | } 107 | ``` 108 | 109 | ## Dependencies 110 | 111 | None. -------------------------------------------------------------------------------- /Singleton/README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 17aff1e8f544d534197e512a71211a76 3 | timeCreated: 1462746256 4 | licenseType: Free 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Singleton/SelfCreatingSingletonMonoBehaviour.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace UnityUtilities 4 | { 5 | /// 6 | /// A that should only exist once and provides easy access to its instance. If none exists in 7 | /// the secene at the time it is requested, it will be automatically created. 8 | /// 9 | /// The subclass that extends from this class. 10 | public class SelfCreatingSingletonMonoBehaviour : MonoBehaviour where TSubclass : SelfCreatingSingletonMonoBehaviour 11 | { 12 | static TSubclass instance; 13 | 14 | /// 15 | /// Returns the sole instance of this class in the current scene. 16 | /// If the instance is not yet found cached, it searches for it and then caches it. 17 | /// If no instance is found, it creates one. 18 | /// 19 | public static TSubclass Instance 20 | { 21 | get 22 | { 23 | if (instance == null) 24 | { 25 | UpdateInstance(); 26 | } 27 | 28 | return instance; 29 | } 30 | } 31 | 32 | 33 | /// 34 | /// Searches for the instance and fills with it. Creates an instance if there is none and 35 | /// outputs an error message if there are too many instances. 36 | /// 37 | static void UpdateInstance() 38 | { 39 | var instances = FindObjectsOfType(); 40 | if (instances.Length == 1) 41 | { 42 | instance = instances[0]; 43 | } 44 | else if (instances.Length == 0) 45 | { 46 | instance = new GameObject(typeof(TSubclass).Name).AddComponent(); 47 | } 48 | else 49 | { 50 | Debug.LogError("Requested singleton of type " + typeof (TSubclass).Name + " has " + instances.Length + "instances."); 51 | } 52 | } 53 | 54 | /// 55 | /// If this is the instance and it was destroyed, 56 | /// remove the instance registration. 57 | /// 58 | /// Note: This also means that you need to use 59 | /// 60 | /// protected override void OnDestroy() 61 | /// { 62 | /// base.OnDestroy(); 63 | /// // [Your code] 64 | /// } 65 | /// 66 | /// in subclasses. 67 | /// 68 | protected virtual void OnDestroy() 69 | { 70 | if (instance == this) 71 | { 72 | instance = null; 73 | } 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /Singleton/SelfCreatingSingletonMonoBehaviour.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 349795bb315cf0b46ac82409025e062f 3 | timeCreated: 1477779343 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Singleton/SingletonMonoBehaviour.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace UnityUtilities 4 | { 5 | /// 6 | /// A that should only exist once and provides easy access to its instance. However, it isn't automatically 7 | /// created and still needs to be attached to a GameObject in the scene. 8 | /// 9 | /// The subclass that extends from this class. 10 | public class SingletonMonoBehaviour : MonoBehaviour where TSubclass : SingletonMonoBehaviour 11 | { 12 | static TSubclass instance; 13 | 14 | /// 15 | /// Returns the sole instance of this class in the current scene. 16 | /// If the instance is not yet found cached, it searches for it and then caches it. 17 | /// 18 | public static TSubclass Instance 19 | { 20 | get 21 | { 22 | if (instance == null) 23 | { 24 | UpdateInstance(false); 25 | } 26 | 27 | return instance; 28 | } 29 | } 30 | 31 | /// 32 | /// Returns the sole instance of this class in the current scene. 33 | /// If the instance is not already cached, it searches for it and then caches it. 34 | /// Note that if the instance is not found at all, InstanceOptional will try to 35 | /// find it on every call. For that reason, it is better to only call this once 36 | /// per script in Awake() or Start() and cache the result. 37 | /// 38 | public static TSubclass InstanceOptional 39 | { 40 | get 41 | { 42 | if (instance == null) 43 | { 44 | // If the instance is not in the current scene, UpdateInstance will be called on every call. 45 | // I could fix this by introducting a static "instanceUpdated" boolean, but then I would 46 | // need to set instanceUpdated to false in OnDestroy() (for scene changes) and I don't want 47 | // to introduce the complexity of virtual MonoBehaviour event methods to this class. 48 | UpdateInstance(true); 49 | } 50 | 51 | return instance; 52 | } 53 | } 54 | 55 | /// 56 | /// Searches for the instance and fills with it. Outputs an error message if there is no 57 | /// instance (unless the parameter is set) or if there are too many instances. 58 | /// 59 | /// If this is set to false, and error message will be shown if there is no instance found. 60 | static void UpdateInstance(bool optional) 61 | { 62 | var instances = FindObjectsOfType(); 63 | if (instances.Length == 1) 64 | { 65 | instance = instances[0]; 66 | } 67 | else if (instances.Length == 0) 68 | { 69 | if (!optional) 70 | { 71 | Debug.LogError("Requested singleton of type " + typeof (TSubclass).Name + " not found."); 72 | } 73 | } 74 | else 75 | { 76 | Debug.LogError("Requested singleton of type " + typeof (TSubclass).Name + " has " + instances.Length + "instances."); 77 | } 78 | } 79 | 80 | /// 81 | /// If this is the instance and it was destroyed, 82 | /// remove the instance registration. 83 | /// 84 | /// Note: This also means that you need to use 85 | /// 86 | /// protected override void OnDestroy() 87 | /// { 88 | /// base.OnDestroy(); 89 | /// // [Your code] 90 | /// } 91 | /// 92 | /// in subclasses. 93 | /// 94 | protected virtual void OnDestroy() 95 | { 96 | if (instance == this) 97 | { 98 | instance = null; 99 | } 100 | } 101 | } 102 | } -------------------------------------------------------------------------------- /Singleton/SingletonMonoBehaviour.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4c4b96e5289472c41b1d65d001649e67 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /UnityHelper.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e5cf8213f892df34a890c84ce548c280 3 | folderAsset: yes 4 | timeCreated: 1465253549 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /UnityHelper/README.md: -------------------------------------------------------------------------------- 1 | # UnityHelper 2 | 3 | The UnityHelper contains a plethora of useful extensions and helpers for Transform, GameObject, Vector2/3/4, Rect and more. 4 | 5 | ## Examples 6 | 7 | ### Transform/Vector/Color 8 | 9 | ```C# 10 | /* Set the transform.position.x to 5 and z to 3. Keeps y. 11 | * Equivalent to: 12 | * var position = transform.position; 13 | * position.x = 5; 14 | * position.z = 3; 15 | * transform.position = position; 16 | */ 17 | transform.SetPosition(x: 5, z: 3); 18 | 19 | // Same as above; only sets transform.localEulerAngles.y. 20 | // There are extension methods for all position/rotation/scales. 21 | transform.SetLocalEulerAngles(y: 180); 22 | 23 | // Similar methods are available for Vector2/3/4 and Color: 24 | // Gets the transform.position, but with y set to 0. 25 | Vector3 floorPosition = transform.position.Change3(y: 0); 26 | 27 | // Gets the material color, but sets the color.a value to 0.5. 28 | Color halfTransparentColor = GetComponent().sharedMaterial.color.ChangeAlpha(0.5f); 29 | 30 | // Sets the position/rotation of enemyIndicator to someEnemyTransform.position/rotation 31 | enemyIndicator.CopyPositionAndRotatationFrom(someEnemyTransform); 32 | ``` 33 | 34 | ### Framerate-Independent Eased Lerping 35 | 36 | There are essentially two ways of lerping a value over time: linear (constant speed) or eased (e.g. getting slower the closer you are to the target, see http://easings.net.) 37 | 38 | For linear lerping (and most of the easing functions), you need to track the start and end positions and the time that elapsed. 39 | 40 | Calling something like `currentValue = Mathf.Lerp(currentValue, 1f, 0.95f);` every frame provides an easy way of eased lerping without tracking elapsed time or the starting value, but since it's called every frame, the actual traversed distance per second changes the higher the framerate is. 41 | 42 | EasedLerpFactor replaces the lerp parameter t to make it framerate-independent and easier to estimate. 43 | 44 | You can find more information about the formula used [here](https://www.scirra.com/blog/ashley/17/using-lerp-with-delta-time). 45 | 46 | You can use `MathHelper.EasedLerpFactor` to get the t used for a lerp or call `MathHelper.EasedLerp`, `UnityHelper.EasedLerpVector2`, `UnityHelper.EasedLerpVector3`, `UnityHelper.EasedLerpVector4` or `UnityHelper.EasedLerpColor` directly. 47 | 48 | ![EasedLerpFactorExample Editor Screenshot](https://raw.githubusercontent.com/TobiasWehrum/unity-utilities/master/_Images/EasedLerpFactorExample.gif) 49 | 50 | ```C# 51 | void Update() 52 | { 53 | // Get the world position of the mouse pointer 54 | Vector3 mousePositionWorld = Camera.main.ScreenToWorldPoint(Input.mousePosition); 55 | mousePositionWorld.z = 0f; 56 | 57 | // Set the runner position to the mouse pointer 58 | runner.position = mousePositionWorld; 59 | 60 | // Move the follower 75% of the remaining distance to the runner per second 61 | follower.position = UnityHelper.EasedLerpVector3(follower.position, runner.position, 0.75f); 62 | 63 | // ...which is the same as: 64 | 65 | //float t = MathHelper.EasedLerpFactor(0.75f); 66 | //follower.position = Vector3.Lerp(follower.position, mousePositionWorld, t); 67 | } 68 | ``` 69 | 70 | ### Get the centroid from an array/list of Vector2/3/4 71 | 72 | ```C# 73 | Vector3[] list = { 74 | new Vector3(-5, 10, 12), 75 | new Vector3(55, 32, 10), 76 | new Vector3(85, -40, 80) 77 | }; 78 | 79 | // Calculates the geometric center (the average) of the input list 80 | Debug.Log("Centroid: " + list.CalculateCentroid()); // => Centroid: (45.0, 0.7, 34.0) 81 | ``` 82 | 83 | ### Vector2 Rotation 84 | 85 | ```C# 86 | // Create a length 1 Vector2 pointing 40 degrees away from (1.0, 0.0) 87 | var vector = UnityHelper.CreateVector2AngleDeg(20f); 88 | Debug.Log(vector); // => (0.9, 0.3) 89 | 90 | // Rotate the vector 70 degrees 91 | vector = vector.RotateDeg(70f); 92 | Debug.Log(vector); // => (0.0, 1.0) 93 | 94 | // Output the current vector rotation 95 | Debug.Log(vector.GetAngleDeg()); // => 90 96 | ``` 97 | 98 | ### GameObject 99 | 100 | ```C# 101 | // Assigns layer 4 to this GameObject and all its children recursively 102 | gameObject.AssignLayerToHierarchy(4); 103 | 104 | // Create an instance of a prefab. When the prefab is named "Original", the instance will 105 | // be named "Original(Copy)" 106 | GameObject copiedGameObject = Instantiate(prefab); 107 | 108 | // Return the name without "(Copy)" 109 | Debug.Log(copiedGameObject.GetNameWithoutClone()); // => Original 110 | 111 | // Change the name back to "Original" 112 | copiedGameObject.StripCloneFromName(); 113 | ``` 114 | 115 | ### Rect 116 | 117 | ```C# 118 | // Make a rect from (10|20) to (60|120) 119 | Rect rect = new Rect(10, 20, 50, 100); 120 | 121 | // Gets a random position for an enemy in the rect, leaving a 5 unit border 122 | Vector2 enemySpawnPosition = rect.RandomPosition(-5); 123 | 124 | // Gets a random sub rect of size 10|10 in which we could spawn multiple enemies 125 | Rect enemySpawnSubrect = rect.RandomSubRect(10, 10); 126 | 127 | Vector2 enemyPosition = new Vector2(0, 500); 128 | 129 | // Clamp an enemy position to the rect 130 | enemyPosition = rect.Clamp2(enemyPosition); 131 | Debug.Log(enemyPosition); // Output: (10.0, 120.0) 132 | 133 | // Create a rect that is 10 units bigger to each side 134 | Rect biggerRect = rect.Extend(10); 135 | 136 | // Get the corner points 137 | Vector2[] cornerPoints = rect.GetCornerPoints(); 138 | ``` 139 | 140 | ### Random 141 | 142 | ```C# 143 | // Points in a random 2D direction 144 | var randomDirection2D = UnityHelper.RandomOnUnitCircle; 145 | 146 | // Either goes left or right 147 | var deltaX = 20 * UnityHelper.RandomSign; 148 | 149 | // Gets set to either choice 150 | var choice = UnityHelper.RandomBool ? "Choice A" : "Choice B"; 151 | ``` 152 | 153 | ### PlayerPrefs: Bool 154 | 155 | ```C# 156 | // Gets a PlayerPrefs key "FirstStart" or return true if not set 157 | bool isFirstStart = UnityHelper.PlayerPrefsGetBool("FirstStart", true); 158 | 159 | // Set the key FirstStart to false 160 | UnityHelper.PlayerPrefsSetBool("FirstStart", false); 161 | ``` 162 | 163 | ### Check if a layer is included in a LayerMask 164 | 165 | ```C# 166 | // Does the layer mask contain layer 4? 167 | bool containsLayer4 = someLayerMask.ContainsLayer(4); 168 | ``` 169 | 170 | ### Find out how big the level bounds are 171 | 172 | ```C# 173 | // Get the bounds of all colliders in the level to clamp the camera later on 174 | Collider[] allColliders = FindObjectsOfType(); 175 | Bounds levelBounds = UnityHelper.CombineColliderBounds(allColliders); 176 | ``` 177 | 178 | ### Calculate Camera viewport world size at distance 179 | 180 | ```C# 181 | // Find out how much the perspective camera can see at 10 unit away 182 | Vector2 viewportSizeAtDistance = Camera.main.CalculateViewportWorldSizeAtDistance(10); 183 | ``` 184 | 185 | ### CharacterController: CapsuleCast 186 | 187 | ```C# 188 | Vector3 point1; 189 | Vector3 point2; 190 | float radius; 191 | Vector3 origin = playerCharacterController.transform.position; 192 | 193 | // Get the data for the capsule cast from the current player position 194 | UnityHelper.GetCapsuleCastData(playerCharacterController, origin, out point1, out point2, out radius); 195 | 196 | // Cast 2 units forwards 197 | bool hitSomething = Physics.CapsuleCast(point1, point2, radius, Vector3.forward, 2f); 198 | ``` 199 | 200 | ## Dependencies 201 | 202 | * [MathHelper](https://github.com/TobiasWehrum/unity-utilities/tree/master/MathHelper) -------------------------------------------------------------------------------- /UnityHelper/README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b6e34e3dba2edc24bb08650aff4b9f20 3 | timeCreated: 1465339531 4 | licenseType: Pro 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /UnityHelper/UnityHelper.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5f9bb454e61aeef47baa33c3d1273e8e 3 | timeCreated: 1465253549 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /XmlHelper.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 10a1937e9ab43024e840c2beff334731 3 | folderAsset: yes 4 | timeCreated: 1466344872 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /XmlHelper/README.md: -------------------------------------------------------------------------------- 1 | # XmlHelper 2 | 3 | The XmlHelper serializes and deserializes to/from XML and allows convenient access to optional element content and attributes when reading general XMLs. 4 | 5 | With a few exceptions (e.g. arrays of ArrayList and arrays of List), all public attributes and fields of any public class should be serialized without any 6 | further need to tag the elements. The only thing needed is a public default constructor. 7 | 8 | For finer control, see the MSDN documentation on XmlSerializer: https://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer(v=vs.110).aspx 9 | 10 | ## Examples 11 | 12 | ### String serialization 13 | 14 | ```C# 15 | public class TestData 16 | { 17 | // A public field - will be serialized 18 | public int field; 19 | 20 | // A private field with public property - will be serialized 21 | float property; 22 | public float Property 23 | { 24 | get { return property; } 25 | set { property = value;} 26 | } 27 | 28 | // An auto property - will be serialized 29 | public bool AutoProperty { get; set; } 30 | 31 | // A private field - will *not* be serialized 32 | string privateField = "Test"; 33 | 34 | // A public field marked "XmlIgnore" - will *not* be serialized 35 | [XmlIgnore] 36 | public double publicNonSerialized = 5.5; 37 | 38 | // The public default constructor is needed for the XmlSerializer. 39 | public TestData() 40 | { 41 | } 42 | 43 | public TestData(int field, float property, bool autoProperty) 44 | { 45 | this.field = field; 46 | this.property = property; 47 | AutoProperty = autoProperty; 48 | } 49 | } 50 | ``` 51 | 52 | ```C# 53 | // Create a new TestData object 54 | TestData data = new TestData(1, 2.3f, true); 55 | 56 | // Serialize the TestData object into a string 57 | string xmlString = data.SerializeToXmlString(); 58 | 59 | /* Output: 60 | 61 | 62 | 63 | 1 64 | 2.3 65 | true 66 | 67 | */ 68 | Debug.Log(xmlString); 69 | 70 | // Get the data back from the string 71 | TestData deserializedData = xmlString.DeserializeFromXmlString(); 72 | ``` 73 | 74 | ### XmlNode content/attributes 75 | 76 | ```C# 77 | // Create an XmlDocument with test data 78 | XmlDocument xmlDocument = new XmlDocument(); 79 | xmlDocument.LoadXml("" + 80 | " " + 81 | " Grunt" + 82 | " " + 83 | " " + 84 | " " + 85 | " Tank" + 86 | " " + 87 | " true" + 88 | " " + 89 | ""); 90 | 91 | // Read each enemyData element in the enemyList 92 | foreach (XmlNode enemyData in xmlDocument["enemyList"].ChildNodes) 93 | { 94 | // Get the name element content, if it exists, else set "???" 95 | string name = enemyData.GetElementString("name", "???"); 96 | 97 | // Get the position element and then its attributes 98 | XmlNode position = enemyData["position"]; 99 | int x = position.GetAttributeInt("x"); 100 | int y = position.GetAttributeInt("y"); 101 | 102 | // Get the ranged element content, if it exists, else set "false" 103 | bool ranged = enemyData.GetElementBool("ranged", false); 104 | 105 | // Output the result 106 | Debug.Log(string.Format("{0} at {1}|{2} is {3}", 107 | name, 108 | x, 109 | y, 110 | ranged ? "ranged" : "not ranged")); 111 | } 112 | 113 | /* Grunt at 5|3 is not ranged 114 | Tank at 7|1 is ranged 115 | */ 116 | ``` 117 | 118 | ## Dependencies 119 | 120 | None. -------------------------------------------------------------------------------- /XmlHelper/README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 417d0b881713b7045a00a27b80b46b1b 3 | timeCreated: 1466352001 4 | licenseType: Pro 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /XmlHelper/XmlHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Xml; 5 | using System.Xml.Serialization; 6 | 7 | namespace UnityUtilities 8 | { 9 | /// 10 | /// The XmlHelper serializes and deserializes to/from XML and allows convenient 11 | /// access to optional element content and attributes when reading general XMLs. 12 | /// 13 | public static class XmlHelper 14 | { 15 | /// 16 | /// A dictionary of XmlSerializer, accessible by type. Used by . 17 | /// 18 | static Dictionary serializersByType = new Dictionary(); 19 | 20 | /// 21 | /// A thread lock object to make access to threadsafe. 22 | /// 23 | #pragma warning disable 414 24 | static Object serializersByTypeLock = new object(); 25 | #pragma warning restore 414 26 | 27 | /// 28 | /// Gets a cached XmlSerializer for the specified type. 29 | /// 30 | /// The type to get the XmlSerializer for. 31 | /// An XmlSerializer for the specified type. 32 | public static XmlSerializer GetSerializer() 33 | { 34 | lock (serializersByType) 35 | { 36 | var t = typeof (T); 37 | XmlSerializer serializer; 38 | if (!serializersByType.TryGetValue(t, out serializer)) 39 | { 40 | serializer = new XmlSerializer(t); 41 | serializersByType[t] = serializer; 42 | } 43 | return serializer; 44 | } 45 | } 46 | 47 | /// 48 | /// Uses the XmlSerializer to serialize data into a string that can later 49 | /// be deserialized again via . 50 | /// 51 | /// With a few exceptions (e.g. arrays of ArrayList and arrays of List<T>), 52 | /// all public attributes and fields of any public class should be serialized 53 | /// without any further need to tag the elements. The only thing needed is 54 | /// a public default constructor. 55 | /// 56 | /// For finer control, see the MSDN document on XmlSerializer: 57 | /// https://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer(v=vs.110).aspx 58 | /// 59 | /// The data type to be serialized. 60 | /// The data to be serialized. 61 | /// The serialized data string. 62 | public static string SerializeToXmlString(this T data) 63 | { 64 | using (var stringWriter = new StringWriter()) 65 | { 66 | GetSerializer().Serialize(stringWriter, data); 67 | return stringWriter.ToString(); 68 | } 69 | } 70 | 71 | /// 72 | /// Deserializes an object serialized with . 73 | /// 74 | /// Thedata type that was serialized. 75 | /// The serialized data string. 76 | /// The deserialized data. 77 | /// 78 | /// An error occurred during deserialization. The original exception is available 79 | /// using the InnerException property. 80 | /// 81 | public static T DeserializeFromXmlString(this string str) 82 | { 83 | using (var stringReader = new StringReader(str)) 84 | { 85 | return (T) GetSerializer().Deserialize(stringReader); 86 | } 87 | } 88 | 89 | /// 90 | /// Gets the content of first child element with the specified name. If no child 91 | /// with that name exists, the defaultValue is returned. 92 | /// 93 | /// The XMlNode to get the child from. 94 | /// The name of the child. 95 | /// The default value if no child with that name exists. 96 | /// The content of the child if it exists; else the default value. 97 | public static string GetElementString(this XmlNode xmlNode, string name, string defaultValue = "") 98 | { 99 | var element = xmlNode[name]; 100 | if (element == null) 101 | return defaultValue; 102 | 103 | return element.InnerText; 104 | } 105 | 106 | /// 107 | /// Gets the content of first child element with the specified name. If no child 108 | /// with that name exists, the defaultValue is returned. 109 | /// 110 | /// The XMlNode to get the child from. 111 | /// The name of the child. 112 | /// The default value if no child with that name exists. 113 | /// The content of the child if it exists; else the default value. 114 | /// The content is is not in the correct format. 115 | /// The content represents a number less than MinValue or greater than MaxValue. 116 | public static int GetElementInt(this XmlNode xmlNode, string name, int defaultValue = 0) 117 | { 118 | var element = xmlNode[name]; 119 | if (element == null) 120 | return defaultValue; 121 | 122 | return int.Parse(element.InnerText); 123 | } 124 | 125 | /// 126 | /// Gets the content of first child element with the specified name. If no child 127 | /// with that name exists, the defaultValue is returned. 128 | /// 129 | /// The XMlNode to get the child from. 130 | /// The name of the child. 131 | /// The default value if no child with that name exists. 132 | /// The content of the child if it exists; else the default value. 133 | /// The content is is not in the correct format. 134 | /// The content represents a number less than MinValue or greater than MaxValue. 135 | public static float GetElementFloat(this XmlNode xmlNode, string name, float defaultValue = 0) 136 | { 137 | var element = xmlNode[name]; 138 | if (element == null) 139 | return defaultValue; 140 | 141 | return float.Parse(element.InnerText); 142 | } 143 | 144 | /// 145 | /// Gets the content of first child element with the specified name. If no child 146 | /// with that name exists, the defaultValue is returned. 147 | /// 148 | /// The XMlNode to get the child from. 149 | /// The name of the child. 150 | /// The default value if no child with that name exists. 151 | /// The content of the child if it exists; else the default value. 152 | /// The content is is not in the correct format. 153 | /// The content represents a number less than MinValue or greater than MaxValue. 154 | public static bool GetElementBool(this XmlNode xmlNode, string name, bool defaultValue = false) 155 | { 156 | var element = xmlNode[name]; 157 | if (element == null) 158 | return defaultValue; 159 | 160 | return bool.Parse(element.InnerText); 161 | } 162 | 163 | /// 164 | /// Gets the content of first attribute with the specified name. If no attribute 165 | /// with that name exists, the defaultValue is returned. 166 | /// 167 | /// The XMlNode to get the attribute from. 168 | /// The name of the attribute. 169 | /// The default value if no attribute with that name exists. 170 | /// The content of the attribute if it exists; else the default value. 171 | public static string GetAttributeString(this XmlNode xmlNode, string name, string defaultValue = "") 172 | { 173 | var attribute = xmlNode.Attributes[name]; 174 | if (attribute == null) 175 | return defaultValue; 176 | 177 | return attribute.Value; 178 | } 179 | 180 | /// 181 | /// Gets the content of first attribute with the specified name. If no attribute 182 | /// with that name exists, the defaultValue is returned. 183 | /// 184 | /// The XMlNode to get the attribute from. 185 | /// The name of the attribute. 186 | /// The default value if no attribute with that name exists. 187 | /// The content of the attribute if it exists; else the default value. 188 | /// The content is is not in the correct format. 189 | /// The content represents a number less than MinValue or greater than MaxValue. 190 | public static int GetAttributeInt(this XmlNode xmlNode, string name, int defaultValue = 0) 191 | { 192 | var attribute = xmlNode.Attributes[name]; 193 | if (attribute == null) 194 | return defaultValue; 195 | 196 | return int.Parse(attribute.Value); 197 | } 198 | 199 | /// 200 | /// Gets the content of first attribute with the specified name. If no attribute 201 | /// with that name exists, the defaultValue is returned. 202 | /// 203 | /// The XMlNode to get the attribute from. 204 | /// The name of the attribute. 205 | /// The default value if no attribute with that name exists. 206 | /// The content of the attribute if it exists; else the default value. 207 | /// The content is is not in the correct format. 208 | /// The content represents a number less than MinValue or greater than MaxValue. 209 | public static int? GetAttributeIntNullable(this XmlNode xmlNode, string name, int? defaultValue = null) 210 | { 211 | var attribute = xmlNode.Attributes[name]; 212 | if (attribute == null) 213 | return defaultValue; 214 | 215 | return int.Parse(attribute.Value); 216 | } 217 | 218 | /// 219 | /// Gets the content of first attribute with the specified name. If no attribute 220 | /// with that name exists, the defaultValue is returned. 221 | /// 222 | /// The XMlNode to get the attribute from. 223 | /// The name of the attribute. 224 | /// The default value if no attribute with that name exists. 225 | /// The content of the attribute if it exists; else the default value. 226 | /// The content is is not in the correct format. 227 | /// The content represents a number less than MinValue or greater than MaxValue. 228 | public static float GetAttributeFloat(this XmlNode xmlNode, string name, float defaultValue = 0) 229 | { 230 | var attribute = xmlNode.Attributes[name]; 231 | if (attribute == null) 232 | return defaultValue; 233 | 234 | return float.Parse(attribute.Value); 235 | } 236 | 237 | /// 238 | /// Gets the content of first attribute with the specified name. If no attribute 239 | /// with that name exists, the defaultValue is returned. 240 | /// 241 | /// The XMlNode to get the attribute from. 242 | /// The name of the attribute. 243 | /// The default value if no attribute with that name exists. 244 | /// The content of the attribute if it exists; else the default value. 245 | /// The content is is not in the correct format. 246 | /// The content represents a number less than MinValue or greater than MaxValue. 247 | public static float? GetAttributeFloatNullable(this XmlNode xmlNode, string name, float? defaultValue = null) 248 | { 249 | var attribute = xmlNode.Attributes[name]; 250 | if (attribute == null) 251 | return defaultValue; 252 | 253 | return float.Parse(attribute.Value); 254 | } 255 | 256 | /// 257 | /// Gets the content of first attribute with the specified name. If no attribute 258 | /// with that name exists, the defaultValue is returned. 259 | /// 260 | /// The XMlNode to get the attribute from. 261 | /// The name of the attribute. 262 | /// The default value if no attribute with that name exists. 263 | /// The content of the attribute if it exists; else the default value. 264 | /// The content is is not in the correct format. 265 | public static bool GetAttributeBool(this XmlNode xmlNode, string name, bool defaultValue = false) 266 | { 267 | var attribute = xmlNode.Attributes[name]; 268 | if (attribute == null) 269 | return defaultValue; 270 | 271 | return bool.Parse(attribute.Value); 272 | } 273 | } 274 | } -------------------------------------------------------------------------------- /XmlHelper/XmlHelper.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3b2acd0274494544581360d5a51273ae 3 | timeCreated: 1466344872 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /_Images.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 913c49f316b8a5241b405c3300a5fb3c 3 | folderAsset: yes 4 | timeCreated: 1462736436 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /_Images/CountdownPropertyExample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TobiasWehrum/unity-utilities/c78da2928b1f7b73046a697185271e7effeddd1f/_Images/CountdownPropertyExample.png -------------------------------------------------------------------------------- /_Images/CountdownPropertyExample.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 77d7b8d5d2aa0b54c9511431d8cab308 3 | timeCreated: 1462736440 4 | licenseType: Free 5 | TextureImporter: 6 | fileIDToRecycleName: {} 7 | serializedVersion: 2 8 | mipmaps: 9 | mipMapMode: 0 10 | enableMipMap: 1 11 | linearTexture: 0 12 | correctGamma: 0 13 | fadeOut: 0 14 | borderMipMap: 0 15 | mipMapFadeDistanceStart: 1 16 | mipMapFadeDistanceEnd: 3 17 | bumpmap: 18 | convertToNormalMap: 0 19 | externalNormalMap: 0 20 | heightScale: 0.25 21 | normalMapFilter: 0 22 | isReadable: 0 23 | grayScaleToAlpha: 0 24 | generateCubemap: 0 25 | cubemapConvolution: 0 26 | cubemapConvolutionSteps: 7 27 | cubemapConvolutionExponent: 1.5 28 | seamlessCubemap: 0 29 | textureFormat: -1 30 | maxTextureSize: 2048 31 | textureSettings: 32 | filterMode: -1 33 | aniso: -1 34 | mipBias: -1 35 | wrapMode: 1 36 | nPOTScale: 0 37 | lightmap: 0 38 | rGBM: 0 39 | compressionQuality: 50 40 | allowsAlphaSplitting: 0 41 | spriteMode: 1 42 | spriteExtrude: 1 43 | spriteMeshType: 1 44 | alignment: 0 45 | spritePivot: {x: 0.5, y: 0.5} 46 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 47 | spritePixelsToUnits: 100 48 | alphaIsTransparency: 1 49 | textureType: 8 50 | buildTargetSettings: [] 51 | spriteSheet: 52 | sprites: [] 53 | outline: [] 54 | spritePackingTag: 55 | userData: 56 | assetBundleName: 57 | assetBundleVariant: 58 | -------------------------------------------------------------------------------- /_Images/EasedLerpFactorExample.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TobiasWehrum/unity-utilities/c78da2928b1f7b73046a697185271e7effeddd1f/_Images/EasedLerpFactorExample.gif -------------------------------------------------------------------------------- /_Images/EasedLerpFactorExample.gif.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a88f79c593407344a879cfbf8c0d2e94 3 | timeCreated: 1477158275 4 | licenseType: Free 5 | TextureImporter: 6 | fileIDToRecycleName: {} 7 | serializedVersion: 2 8 | mipmaps: 9 | mipMapMode: 0 10 | enableMipMap: 1 11 | linearTexture: 0 12 | correctGamma: 0 13 | fadeOut: 0 14 | borderMipMap: 0 15 | mipMapFadeDistanceStart: 1 16 | mipMapFadeDistanceEnd: 3 17 | bumpmap: 18 | convertToNormalMap: 0 19 | externalNormalMap: 0 20 | heightScale: 0.25 21 | normalMapFilter: 0 22 | isReadable: 0 23 | grayScaleToAlpha: 0 24 | generateCubemap: 0 25 | cubemapConvolution: 0 26 | cubemapConvolutionSteps: 7 27 | cubemapConvolutionExponent: 1.5 28 | seamlessCubemap: 0 29 | textureFormat: -1 30 | maxTextureSize: 2048 31 | textureSettings: 32 | filterMode: -1 33 | aniso: -1 34 | mipBias: -1 35 | wrapMode: 1 36 | nPOTScale: 0 37 | lightmap: 0 38 | rGBM: 0 39 | compressionQuality: 50 40 | allowsAlphaSplitting: 0 41 | spriteMode: 1 42 | spriteExtrude: 1 43 | spriteMeshType: 1 44 | alignment: 0 45 | spritePivot: {x: 0.5, y: 0.5} 46 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 47 | spritePixelsToUnits: 100 48 | alphaIsTransparency: 1 49 | spriteTessellationDetail: -1 50 | textureType: 8 51 | buildTargetSettings: [] 52 | spriteSheet: 53 | serializedVersion: 2 54 | sprites: [] 55 | outline: [] 56 | spritePackingTag: 57 | userData: 58 | assetBundleName: 59 | assetBundleVariant: 60 | -------------------------------------------------------------------------------- /_Images/NoiseOutputValueExample.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TobiasWehrum/unity-utilities/c78da2928b1f7b73046a697185271e7effeddd1f/_Images/NoiseOutputValueExample.gif -------------------------------------------------------------------------------- /_Images/NoiseOutputValueExample.gif.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 860c1464aa19c2848acd724336e8c20a 3 | timeCreated: 1462745341 4 | licenseType: Free 5 | TextureImporter: 6 | fileIDToRecycleName: {} 7 | serializedVersion: 2 8 | mipmaps: 9 | mipMapMode: 0 10 | enableMipMap: 1 11 | linearTexture: 0 12 | correctGamma: 0 13 | fadeOut: 0 14 | borderMipMap: 0 15 | mipMapFadeDistanceStart: 1 16 | mipMapFadeDistanceEnd: 3 17 | bumpmap: 18 | convertToNormalMap: 0 19 | externalNormalMap: 0 20 | heightScale: 0.25 21 | normalMapFilter: 0 22 | isReadable: 0 23 | grayScaleToAlpha: 0 24 | generateCubemap: 0 25 | cubemapConvolution: 0 26 | cubemapConvolutionSteps: 7 27 | cubemapConvolutionExponent: 1.5 28 | seamlessCubemap: 0 29 | textureFormat: -1 30 | maxTextureSize: 2048 31 | textureSettings: 32 | filterMode: -1 33 | aniso: 16 34 | mipBias: -1 35 | wrapMode: 1 36 | nPOTScale: 0 37 | lightmap: 0 38 | rGBM: 0 39 | compressionQuality: 50 40 | allowsAlphaSplitting: 0 41 | spriteMode: 1 42 | spriteExtrude: 1 43 | spriteMeshType: 1 44 | alignment: 0 45 | spritePivot: {x: 0.5, y: 0.5} 46 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 47 | spritePixelsToUnits: 100 48 | alphaIsTransparency: 1 49 | textureType: 8 50 | buildTargetSettings: [] 51 | spriteSheet: 52 | sprites: [] 53 | outline: [] 54 | spritePackingTag: 55 | userData: 56 | assetBundleName: 57 | assetBundleVariant: 58 | -------------------------------------------------------------------------------- /_Images/RangeExample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TobiasWehrum/unity-utilities/c78da2928b1f7b73046a697185271e7effeddd1f/_Images/RangeExample.png -------------------------------------------------------------------------------- /_Images/RangeExample.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d0c53fde2b1abc648b22728cef0f39b3 3 | timeCreated: 1462746431 4 | licenseType: Free 5 | TextureImporter: 6 | fileIDToRecycleName: {} 7 | serializedVersion: 2 8 | mipmaps: 9 | mipMapMode: 0 10 | enableMipMap: 1 11 | linearTexture: 0 12 | correctGamma: 0 13 | fadeOut: 0 14 | borderMipMap: 0 15 | mipMapFadeDistanceStart: 1 16 | mipMapFadeDistanceEnd: 3 17 | bumpmap: 18 | convertToNormalMap: 0 19 | externalNormalMap: 0 20 | heightScale: 0.25 21 | normalMapFilter: 0 22 | isReadable: 0 23 | grayScaleToAlpha: 0 24 | generateCubemap: 0 25 | cubemapConvolution: 0 26 | cubemapConvolutionSteps: 7 27 | cubemapConvolutionExponent: 1.5 28 | seamlessCubemap: 0 29 | textureFormat: -1 30 | maxTextureSize: 2048 31 | textureSettings: 32 | filterMode: -1 33 | aniso: -1 34 | mipBias: -1 35 | wrapMode: 1 36 | nPOTScale: 0 37 | lightmap: 0 38 | rGBM: 0 39 | compressionQuality: 50 40 | allowsAlphaSplitting: 0 41 | spriteMode: 1 42 | spriteExtrude: 1 43 | spriteMeshType: 1 44 | alignment: 0 45 | spritePivot: {x: 0.5, y: 0.5} 46 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 47 | spritePixelsToUnits: 100 48 | alphaIsTransparency: 1 49 | textureType: 8 50 | buildTargetSettings: [] 51 | spriteSheet: 52 | sprites: [] 53 | outline: [] 54 | spritePackingTag: 55 | userData: 56 | assetBundleName: 57 | assetBundleVariant: 58 | --------------------------------------------------------------------------------