├── BasicSimulation.cs ├── BoxTrigger.cs ├── CooldownAction.cs ├── CursorControl.cs ├── DirectionVector.cs ├── DynamicSort.cs ├── FuckingAngles.cs ├── GameWindow.cs ├── GarbageBuoyancy.cs ├── GrabBag.cs ├── GravityScale.cs ├── InputManager.cs ├── InvisibleBox.cs ├── LICENSE.txt ├── LissajousCurve.cs ├── Log Toggle ├── Editor │ └── LogToggleDrawer.cs └── LogToggle.cs ├── MeshSorter.cs ├── MoonCalculator.cs ├── MultiBool.cs ├── MultiEnum ├── Editor │ └── MultiEnumFlagsAttributeDrawer.cs ├── MultiEnumAttribute.cs └── MultiEnumHelper.cs ├── NAudio.cs ├── NEvent.cs ├── NameElements ├── Editor │ └── NameElementsDrawer.cs └── NameElementsAttribute.cs ├── ObjectInteraction.cs ├── ObjectPool.cs ├── OpenInFileBrowser.cs ├── Packages ├── Reslution Selector UI.unitypackage └── Simple Input Manager.unitypackage ├── Parallax.cs ├── PersistentScreenSize.cs ├── Pixel Shader ├── PixelCamera.cs └── Pixelation.shader ├── PronounSystem.cs ├── README.md ├── Randomizer.cs ├── RangeFloat ├── Editor.meta ├── Editor │ ├── RangeFloatDrawer.cs │ └── RangeFloatDrawer.cs.meta └── RangeFloat.cs ├── RndVal.cs ├── SORefer.cs ├── SaveSystem.cs ├── SceneSwitcher.cs ├── ScreenResolutionSetter.cs ├── ScreenShake.cs ├── Scriptable State System ├── Editor │ ├── state_callback_icon.png │ ├── state_nav_icon.png │ └── state_object_icon.png ├── State.cs ├── StateChangeCallbacks.cs └── StateNav.cs ├── SetAspectRatio.cs ├── Simple Animator ├── SimpleAnimator.cs ├── SimpleSpriteAnimator.cs ├── SimpleStateAnimator.cs └── SimpleUIAnimator.cs ├── SpeedrunClock.cs ├── Spring.cs ├── Tag.cs ├── UniqueID.cs └── Utils.cs /BasicSimulation.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | // grabbed from https://docs.unity3d.com/ScriptReference/Physics2D.Simulate.html 4 | 5 | public class BasicSimulation : MonoBehaviour { 6 | 7 | private float timer; 8 | 9 | private void Start() { 10 | SetGameSpeed(Settings.Actual.gameSpeed); 11 | } 12 | 13 | private void Update() { 14 | if (Physics2D.autoSimulation) 15 | return; 16 | 17 | timer += Time.deltaTime * Settings.Actual.gameSpeed; 18 | 19 | while (timer >= Time.fixedDeltaTime) { 20 | timer -= Time.fixedDeltaTime; 21 | Physics2D.Simulate(Time.fixedDeltaTime); 22 | } 23 | } 24 | 25 | public static void SetGameSpeed(float gameSpeed) { 26 | if (gameSpeed >= 1f) 27 | Time.fixedDeltaTime = .005f; 28 | else 29 | Time.fixedDeltaTime = .001f; 30 | } 31 | } -------------------------------------------------------------------------------- /BoxTrigger.cs: -------------------------------------------------------------------------------- 1 | using NaughtyAttributes; 2 | using UnityEngine; 3 | using UnityEngine.Events; 4 | 5 | [RequireComponent(typeof(BoxCollider2D))] 6 | public class BoxTrigger : MonoBehaviour { 7 | 8 | public new bool enabled = true; 9 | public bool visualize = true; 10 | [Tag] 11 | public string[] tags = new string[] { "Player" }; 12 | [Space] 13 | public Events[] events = new Events[1]; 14 | 15 | private BoxCollider2D boxCollider; 16 | 17 | private void Awake() { 18 | boxCollider = GetComponent(); 19 | } 20 | 21 | private void OnTriggerEnter2D(Collider2D other) { 22 | CheckTrigger(other, TInteraction.Enter); 23 | } 24 | 25 | private void OnTriggerExit2D(Collider2D other) { 26 | CheckTrigger(other, TInteraction.Exit); 27 | } 28 | 29 | private void CheckTrigger(Collider2D other, TInteraction interaction) { 30 | if (!enabled) 31 | return; 32 | 33 | foreach (string s in tags) { 34 | if (other.CompareTag(s)) { 35 | for (int i = 0; i < events.Length; i++) { 36 | if (events[i].interactions.Has(interaction)) { 37 | if (events[i].directions.Has(FindDirection(other.transform.localPosition))) 38 | events[i].Call.Invoke(); 39 | } 40 | } 41 | return; 42 | } 43 | } 44 | } 45 | 46 | private TDirection FindDirection(Vector2 pos) { 47 | TDirection dir = TDirection.Top; 48 | float smallestDist = Mathf.Abs(pos.y - boxCollider.bounds.max.y); 49 | 50 | float temp = Mathf.Abs(boxCollider.bounds.min.y - pos.y); 51 | if (temp < smallestDist) { 52 | dir = TDirection.Bottom; 53 | smallestDist = temp; 54 | } 55 | temp = Mathf.Abs(boxCollider.bounds.min.x - pos.x); 56 | if (temp < smallestDist) { 57 | dir = TDirection.Left; 58 | smallestDist = temp; 59 | } 60 | temp = Mathf.Abs(boxCollider.bounds.max.x - pos.x); 61 | if (temp < smallestDist) 62 | dir = TDirection.Right; 63 | 64 | return dir; 65 | } 66 | 67 | [Button] 68 | private void SetTransformFromBoxOffset() { 69 | transform.Translate(boxCollider.offset); 70 | boxCollider.offset = Vector2.zero; 71 | } 72 | 73 | private void OnDrawGizmos() { 74 | if (visualize) { 75 | Gizmos.color = new Color(enabled ? 0 : 1, enabled ? 1 : 0, 0, .3f); 76 | if (boxCollider == null) 77 | boxCollider = GetComponent(); 78 | Gizmos.DrawCube(boxCollider.offset + (Vector2)transform.localPosition, boxCollider.size); 79 | } 80 | } 81 | 82 | [System.Serializable] 83 | public class Events { 84 | public TInteraction interactions = (TInteraction)(-1); 85 | [MultiEnum] 86 | public TDirection directions = (TDirection)(-1); 87 | public UnityEvent Call; 88 | } 89 | 90 | [System.Flags] 91 | public enum TInteraction { 92 | Enter = 1 << 0, 93 | Exit = 1 << 1 94 | } 95 | 96 | [System.Flags] 97 | public enum TDirection { 98 | Top = 1 << 0, 99 | Bottom = 1 << 1, 100 | Left = 1 << 2, 101 | Right = 1 << 3 102 | } 103 | } -------------------------------------------------------------------------------- /CooldownAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | public class CooldownAction { 4 | 5 | /// 6 | /// No cooldown at 0, full cooldown at 1. 7 | /// 8 | public float percentRemaining => cooldownLeft / cooldownDuration; 9 | 10 | private Action action; 11 | public float cooldownDuration; 12 | public float cooldownLeft { get; private set; } 13 | private float bufferTime; 14 | private float bufferLeft; 15 | 16 | /// The action you want called. 17 | /// How long to cooldown between action calls. 18 | /// How long to hold onto an Invoke before losing it to the void. 19 | public CooldownAction(float cooldownDuration, Action action = null, float bufferTime = 0) { 20 | if (action != null) 21 | this.action += action; 22 | this.cooldownDuration = cooldownDuration; 23 | this.bufferTime = bufferTime; 24 | } 25 | 26 | public void AddAction(Action action) => this.action += action; 27 | public void RemoveAction(Action action) => this.action -= action; 28 | 29 | /// 30 | /// Updates the timers with deltaTime. 31 | /// Might also call the action if its been buffered. 32 | /// 33 | public void UpdateTime(float deltaTime) { 34 | if (cooldownLeft > 0) 35 | cooldownLeft -= Math.Min(deltaTime, cooldownLeft); 36 | 37 | if (bufferLeft > 0) { 38 | bufferLeft -= Math.Min(deltaTime, bufferLeft); 39 | if (bufferLeft > 0 && cooldownLeft == 0) 40 | Invoke(); 41 | } 42 | } 43 | 44 | /// 45 | /// Calls the action, returns true if the action can be called taking the buffer time into account. 46 | /// 47 | public bool Invoke() { 48 | if (action == null) 49 | return false; 50 | 51 | if (cooldownLeft <= 0) { 52 | action.Invoke(); 53 | bufferLeft = 0; 54 | cooldownLeft = cooldownDuration; 55 | return true; 56 | } else { 57 | bufferLeft = bufferTime; 58 | return cooldownLeft <= bufferTime; 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /CursorControl.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | public class CursorControl : MonoBehaviour { 4 | 5 | public bool changeSpeed; 6 | public bool useSmoothing; 7 | public float speed = 0; 8 | public float accel = 0; 9 | 10 | private Vector2 velRef; 11 | private Point lastMousePos; 12 | private Vector2 v2ToSet; 13 | 14 | private void FixedUpdate() { 15 | if (!changeSpeed) 16 | return; 17 | Vector2 direction = new Vector2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y")).normalized; 18 | 19 | //Point cursorSpeed = new Point(Mathf.RoundToInt(direction.x * speed), Mathf.RoundToInt(direction.y * speed)); 20 | 21 | ////Point difference = MouseUtils.GetSystemMousePos() - lastMousePos; 22 | ////Point newPos = MouseUtils.GetSystemMousePos() + new Point(Mathf.RoundToInt(difference.x * speed), Mathf.RoundToInt(difference.y * speed)); 23 | 24 | Point currentMousePosition = MouseUtils.GetSystemMousePos(); 25 | 26 | //Vector2 targetPos = new Vector2(currentMousePosition.x + cursorSpeed.x, currentMousePosition.y + cursorSpeed.y); 27 | 28 | //v2ToSet = Vector2.SmoothDamp(v2ToSet, targetPos, ref velRef, accel); 29 | 30 | //StartCoroutine(MouseUtils.SetRelativeMousePosUnconstrained(new Point(Mathf.RoundToInt(v2ToSet.x), Mathf.RoundToInt(v2ToSet.y)))); 31 | 32 | Point newPoint = currentMousePosition + new Point(Mathf.RoundToInt(direction.x * speed), Mathf.RoundToInt(direction.y * speed)); 33 | 34 | MouseUtils.SetSystemMousePos(newPoint); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /DirectionVector.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | public struct DirectionVector { 5 | 6 | /// 7 | /// The normalized direction. Set this property to change direction. 8 | /// 9 | public Vector2 Direction { 10 | get => dir; 11 | set { 12 | dir = value.normalized; 13 | if (dir != Vector2.zero) { 14 | if (additionalNonZeroCheck == null || additionalNonZeroCheck.Invoke()) 15 | nonZero = dir; 16 | } 17 | } 18 | } 19 | 20 | /// 21 | /// The last direction value that wasn't zero. 22 | /// 23 | public Vector2 NonZero { 24 | get => nonZero; 25 | } 26 | 27 | /// 28 | /// The closest cardinal vector direction. Can be zero. 29 | /// 30 | public Vector2 Cardinal { 31 | get { 32 | if (dir == Vector2.zero) 33 | return dir; 34 | return Mathf.Abs(dir.x) >= Mathf.Abs(dir.y) ? Vector2.right * Mathf.Sign(dir.x) : Vector2.up * Mathf.Sign(dir.y); 35 | } 36 | } 37 | 38 | /// 39 | /// The closest cardinal/intercardinal vector direction. Can be zero. 40 | /// 41 | public Vector2 EightDir { 42 | get { 43 | float[] snaps = new float[] {-1, 0, 1 }; 44 | return new Vector2(RoundToNearest(dir.x, snaps), RoundToNearest(dir.y, snaps)).normalized; 45 | } 46 | } 47 | 48 | private Vector2 dir; 49 | private Vector2 nonZero; 50 | private Func additionalNonZeroCheck; 51 | 52 | /// The direction to be normalized. 53 | public DirectionVector(Vector2 direction) : this(direction, Vector2.down, null) {} 54 | 55 | /// The direction to be normalized. 56 | /// The default non-zero direction if the direction has no magnitude. 57 | public DirectionVector(Vector2 direction, Vector2 nonZeroDefault) : this(direction, nonZeroDefault, null) {} 58 | 59 | /// The direction to be normalized. 60 | /// An optional additional check to allow the non-zero value to be updated. 61 | public DirectionVector(Vector2 direction, Func additionalNonZeroCheck) : this(direction, Vector2.down, additionalNonZeroCheck) {} 62 | 63 | /// The direction to be normalized. 64 | /// The default non-zero direction if the direction has no magnitude. 65 | /// An optional additional check to allow the non-zero value to be updated. 66 | public DirectionVector(Vector2 direction, Vector2 nonZeroDefault, Func additionalNonZeroCheck) { 67 | dir = direction.normalized; 68 | if (dir == Vector2.zero) 69 | nonZero = nonZeroDefault; 70 | else 71 | nonZero = dir; 72 | this.additionalNonZeroCheck = additionalNonZeroCheck; 73 | Direction = direction; 74 | } 75 | 76 | private float RoundToNearest(float value, float[] snaps) { 77 | float smallestDist = Mathf.Abs(value - snaps[0]); 78 | int index = 0; 79 | for (int i = 1; i < snaps.Length; i++) { 80 | float dist = Mathf.Abs(value - snaps[i]); 81 | if (dist < smallestDist) { 82 | smallestDist = dist; 83 | index = i; 84 | } 85 | } 86 | return snaps[index]; 87 | } 88 | 89 | public static implicit operator Vector2(DirectionVector dv) => dv.dir; 90 | 91 | public static Vector2 operator *(DirectionVector dv, float f) => dv.dir * f; 92 | public static Vector2 operator /(DirectionVector dv, float f) => dv.dir / f; 93 | public static bool operator ==(DirectionVector lhs, DirectionVector rhs) => lhs.dir == rhs.dir && lhs.nonZero == rhs.nonZero; 94 | public static bool operator ==(DirectionVector dv, Vector2 v) => dv.dir == v; 95 | public static bool operator !=(DirectionVector lhs, DirectionVector rhs) => !(lhs == rhs); 96 | public static bool operator !=(DirectionVector dv, Vector2 v) => !(dv == v); 97 | 98 | public override string ToString() => $"{dir} -> {nonZero}"; 99 | 100 | public override int GetHashCode() => dir.GetHashCode() + nonZero.GetHashCode(); 101 | public override bool Equals(object obj) { 102 | if ((obj == null) || !GetType().Equals(obj.GetType()) && !GetType().Equals(typeof(Vector2))) 103 | return false; 104 | return this == (DirectionVector)obj || (Vector2)this == (Vector2)obj; 105 | } 106 | 107 | } -------------------------------------------------------------------------------- /DynamicSort.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | public class DynamicSort : MonoBehaviour { 4 | 5 | [SerializeField] 6 | private bool staticSort; 7 | [SerializeField] 8 | private bool useLocalPos = true; 9 | [SerializeField] 10 | private int numParents; 11 | [SerializeField] 12 | private int offset; 13 | [SerializeField] 14 | private string sortingLayerName = "Dynamic"; 15 | 16 | private SpriteRenderer spriteRenderer; 17 | private Transform transUsing; 18 | 19 | private void Awake() { 20 | spriteRenderer = GetComponent(); 21 | transUsing = GetParent(transform, numParents); 22 | spriteRenderer.sortingLayerName = sortingLayerName; 23 | if (staticSort) { 24 | SetOrder(); 25 | enabled = false; 26 | } 27 | } 28 | 29 | private void Update() { 30 | if (!WorldControl.IsPaused) 31 | SetOrder(); 32 | } 33 | 34 | public void SetOrder() { 35 | spriteRenderer.sortingOrder = GetOrder(useLocalPos?transUsing.localPosition.y : transUsing.position.y); 36 | } 37 | 38 | public int GetOrder(float yVal) => Mathf.RoundToInt(yVal * -100) + offset; 39 | 40 | private Transform GetParent(Transform child, int num) { 41 | if (num == 0 || !child.parent) 42 | return child; 43 | return GetParent(child.parent, num - 1); 44 | } 45 | } -------------------------------------------------------------------------------- /FuckingAngles.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | public static class FuckingAngles { 4 | 5 | /// 6 | /// Converts any angle to an unsigned angle. 7 | /// (Range of 0º to 359º) 8 | /// 9 | /// Signed angle. 10 | public static float Angle(float angle) { 11 | angle = angle % 360f; 12 | if (angle < 0) 13 | angle = 360 + angle; 14 | return angle; 15 | } 16 | 17 | /// 18 | /// Adds degrees to an angle accounting for wrapping over 360º and under 0º 19 | /// 20 | /// The initial angle. 21 | /// Degrees to add. 22 | public static float AddToAngle(float angle, float amount) { 23 | return Angle(angle + amount); 24 | } 25 | 26 | /// 27 | /// Returns a normalized Vector2 direction. 28 | /// 29 | /// Angle in degrees. 30 | public static Vector2 DirFromAngle(float angleInDeg, float offset = 0) { 31 | if (offset != 0) 32 | angleInDeg = AddToAngle(angleInDeg, offset); 33 | return new Vector2(Mathf.Cos(angleInDeg * Mathf.Deg2Rad), Mathf.Sin(angleInDeg * Mathf.Deg2Rad)); 34 | } 35 | 36 | /// 37 | /// Returns the angle of the vector direction. 38 | /// (Range of 0º to 359º) 39 | /// 40 | /// The direction. 41 | /// Offset angle. 42 | public static float AngleFromDir(Vector2 dir, float offset = 0) { 43 | return Angle(Vector2.SignedAngle(Vector2.right, dir) + offset); 44 | } 45 | 46 | /// 47 | /// Returns the angle between two vector positions. 48 | /// (Range of 0º to 359º) 49 | /// 50 | /// The start position. 51 | /// The end position. 52 | /// Offset angle. 53 | public static float AngleBetweenPoints(Vector2 a, Vector2 b, float offset = 0) { 54 | Vector2 difference = b - a; 55 | return AngleFromDir(difference, offset); 56 | } 57 | 58 | /// 59 | /// Converts any angle to a signed angle. 60 | /// (Range of 180º to -179º) 61 | /// 62 | /// The signed angle. 63 | /// Angle. 64 | public static float SignedAngle(float angle) { 65 | angle = Angle(angle); 66 | if (angle > 180) 67 | angle -= 180; 68 | return angle; 69 | } 70 | 71 | /// 72 | /// Adds degrees to an angle accounting for wrapping over 180º and under -179º 73 | /// 74 | /// The initial angle. 75 | /// Degrees to add. 76 | public static float AddToSignedAngle(float angle, float amount) { 77 | return SignedAngle(angle + amount); 78 | } 79 | 80 | /// 81 | /// Returns the signed angle of the vector direction. 82 | /// (Range of -180º to 180º) 83 | /// 84 | /// The direction. 85 | public static float SignedAngleFromDir(Vector2 direction, float offset = 0) { 86 | return SignedAngle(AngleFromDir(direction, offset)); 87 | } 88 | 89 | /// 90 | /// Returns the signed angle between the two vector positions. 91 | /// (Range of -180º to 180º) 92 | /// 93 | /// The start position. 94 | /// The end position. 95 | public static float SignedAngleBetweenPoints(Vector2 a, Vector2 b) { 96 | Vector2 diference = b - a; 97 | return SignedAngleFromDir(diference); 98 | } 99 | 100 | /// 101 | /// Returns a perpendicular vector to the vector provided. 102 | /// 103 | /// The direction. 104 | /// Negative value for left, positive value for right. 105 | public static Vector2 GetPerpendicular(Vector2 dir, int direction = -1) => new Vector2(dir.y * Mathf.Sign(direction), dir.x * -Mathf.Sign(direction)); 106 | 107 | /// 108 | /// Rotates a Transform to point the right side towards the position of a target Transform. 109 | /// 110 | /// The transform to be rotated. 111 | /// The target position to be pointing towards. 112 | /// The maximum degrees to rotate towards. 113 | /// Degrees to offset transform forward. 0 = right, 90 = up, 180 = left, 270 = down 114 | /// Use the ref transform's worldspace position? 115 | /// Use the target transform's worldspace position? 116 | public static void RotateTowards(ref Transform trans, Transform target, float maxDegrees = Mathf.Infinity, float offset = 0, bool selfWorldSpace = false, bool targetWorldSpace = false) { 117 | RotateTowards(ref trans, targetWorldSpace ? target.position : target.localPosition, maxDegrees, offset, selfWorldSpace); 118 | } 119 | 120 | /// 121 | /// Rotates a Transform to point the right side towards a target position. 122 | /// 123 | /// The transform to be rotated. 124 | /// The target position to be pointing towards. 125 | /// The maximum degrees to rotate towards. 126 | /// Degrees to offset transform forward. 0 = right, 90 = up, 180 = left, 270 = down 127 | /// Use the transform's worldspace position? 128 | public static void RotateTowards(ref Transform trans, Vector2 target, float maxDegrees = Mathf.Infinity, float offset = 0, bool worldSpace = false) { 129 | Vector2 selfPos = worldSpace ? trans.position : trans.localPosition; 130 | Vector2 currentDir = DirFromAngle(worldSpace ? trans.eulerAngles.z : trans.localEulerAngles.z, offset); 131 | Vector2 targetDir = target - selfPos; 132 | trans.Rotate(0, 0, Mathf.Clamp(Vector2.SignedAngle(currentDir, targetDir), -maxDegrees, maxDegrees), Space.Self); 133 | } 134 | } -------------------------------------------------------------------------------- /GameWindow.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | public class GameWindow : MonoBehaviour { 4 | 5 | private static GameWindow control; 6 | 7 | public static Vector2Int Resolution => control.resolution; 8 | 9 | public Vector2Int resolution; 10 | [Range(0, 1)] 11 | public float targetSizePercent = .9f; 12 | 13 | private void Awake() { 14 | control = this; 15 | 16 | SetResolution(); 17 | } 18 | 19 | private void SetResolution() { 20 | 21 | float aspect = (float)resolution.x / resolution.y; 22 | int targetY = (int)(Screen.currentResolution.height * targetSizePercent); 23 | int targetX = (int)(Screen.currentResolution.width * targetSizePercent); 24 | Vector2Int size; 25 | 26 | if (targetY * aspect > targetX) { 27 | size = new Vector2Int(targetX, (int)(targetX / aspect)); 28 | } else { 29 | size = new Vector2Int((int)(targetY * aspect), targetY); 30 | } 31 | 32 | // Debug.LogAssertion($"screen size: {new Vector2(Screen.currentResolution.width, Screen.currentResolution.height)}\nrendering size: {new Vector2(Display.main.renderingWidth, Display.main.renderingHeight)}\nsystem size: {new Vector2(Display.main.systemWidth, Display.main.systemHeight)}\ngame size: {size}"); 33 | 34 | Screen.SetResolution(size.x, size.y, FullScreenMode.Windowed); 35 | 36 | PlayerPrefs.SetInt("Screenmanager Resolution Width", size.x); 37 | PlayerPrefs.SetInt("Screenmanager Resolution Height", size.y); 38 | PlayerPrefs.SetInt("Screenmanager Fullscreen Mode", 0); 39 | PlayerPrefs.Save(); 40 | } 41 | } -------------------------------------------------------------------------------- /GarbageBuoyancy.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | public class GarbageBuoyancy : MonoBehaviour { 4 | 5 | public RangeFloat force = new RangeFloat(20, 30); 6 | [Range(0, 1)] 7 | public float drag; 8 | public bool disableGravityScale; 9 | 10 | private BoxCollider boxCollider; 11 | 12 | private void Awake() { 13 | boxCollider = GetComponent(); 14 | } 15 | 16 | private void OnTriggerStay(Collider other) { 17 | 18 | float height = other.bounds.size.y; 19 | float percentSubmerged = Mathf.InverseLerp(other.bounds.min.y, other.bounds.max.y, transform.position.y); 20 | float percentUnder = Mathf.InverseLerp(boxCollider.bounds.min.y, boxCollider.bounds.max.y, other.bounds.center.y); 21 | 22 | other.attachedRigidbody.AddTorque(other.attachedRigidbody.angularVelocity * -drag, ForceMode.VelocityChange); 23 | other.attachedRigidbody.AddForce(other.attachedRigidbody.velocity * -drag, ForceMode.Impulse); 24 | // other.attachedRigidbody.velocity *= drag; 25 | if (percentSubmerged < 1) 26 | other.attachedRigidbody.AddForce(Vector3.up * force.min * percentSubmerged, ForceMode.Acceleration); 27 | else 28 | other.attachedRigidbody.AddForce(Vector3.up * force.GetAt(1 - percentUnder), ForceMode.Acceleration); 29 | } 30 | 31 | private void OnTriggerEnter(Collider other) { 32 | if (disableGravityScale) { 33 | GravityScale gravity = other.GetComponent(); 34 | if (gravity != null) 35 | gravity.enabled = false; 36 | } 37 | 38 | WaterDetector water = other.GetComponent(); 39 | if (water != null) 40 | water.isUnderWater = true; 41 | } 42 | 43 | private void OnTriggerExit(Collider other) { 44 | if (disableGravityScale) { 45 | GravityScale gravity = other.GetComponent(); 46 | if (gravity != null) 47 | gravity.enabled = true; 48 | } 49 | 50 | WaterDetector water = other.GetComponent(); 51 | if (water != null) 52 | water.isUnderWater = false; 53 | } 54 | } -------------------------------------------------------------------------------- /GrabBag.cs: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | Copyright (c) 2021 Noé Charron 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 15 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 16 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 17 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 18 | SOFTWARE. 19 | */ 20 | 21 | using System; 22 | 23 | public class GrabBag { 24 | 25 | private int[] indecesPulled; 26 | private int numListInstances; 27 | private Random rnd; 28 | private int pullsLeft; 29 | 30 | public GrabBag(int listLength) : this(listLength, 1) {} 31 | 32 | public GrabBag(int listLength, int numListInstances) : this(listLength, numListInstances, new Random()) {} 33 | 34 | public GrabBag(int listLength, int numListInstances, int randomSeed) : this(listLength, numListInstances, new Random(randomSeed)) {} 35 | 36 | private GrabBag(int listLength, int numListInstances, Random randomInstance) { 37 | if (numListInstances < 1) 38 | throw new ArgumentException($"{nameof(numListInstances)} must be greater than or equal to 1"); 39 | if (listLength < 1) 40 | throw new ArgumentException($"{nameof(listLength)} must be greater than or equal to 1"); 41 | 42 | indecesPulled = new int[listLength]; 43 | this.numListInstances = numListInstances; 44 | rnd = randomInstance; 45 | 46 | Reset(); 47 | } 48 | 49 | public int Pull() { 50 | if (pullsLeft == 0) 51 | Reset(); 52 | 53 | int nextIndex; 54 | do { 55 | nextIndex = rnd.Next(indecesPulled.Length); 56 | } while (indecesPulled[nextIndex] == numListInstances); 57 | 58 | indecesPulled[nextIndex]++; 59 | pullsLeft--; 60 | 61 | return nextIndex; 62 | } 63 | 64 | private void Reset() { 65 | pullsLeft = indecesPulled.Length * numListInstances; 66 | for (int i = 0; i < indecesPulled.Length; i++) 67 | indecesPulled[i] = 0; 68 | } 69 | } 70 | 71 | public class GrabBag : GrabBag { 72 | private T[] list; 73 | 74 | public GrabBag(T[] list) : this(list, 1) {} 75 | 76 | public GrabBag(T[] list, int numListInstances) : base(list.Length, numListInstances) { 77 | this.list = list; 78 | } 79 | 80 | public GrabBag(T[] list, int numListInstances, int randomSeed) : base(list.Length, numListInstances, randomSeed) { 81 | this.list = list; 82 | } 83 | 84 | public new T Pull() => list[base.Pull()]; 85 | } -------------------------------------------------------------------------------- /GravityScale.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | [RequireComponent(typeof(Rigidbody))] 4 | public class GravityScale : MonoBehaviour { 5 | 6 | public float gravityScale = 1; 7 | 8 | private Rigidbody rb; 9 | 10 | private void Awake() { 11 | rb = GetComponent(); 12 | } 13 | 14 | private void FixedUpdate() { 15 | rb.AddForce(Vector3.down * (-gravityScale + 1) * Physics.gravity.y * rb.mass); 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /InputManager.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using NaughtyAttributes; 4 | using UnityEngine; 5 | using UnityEngine.Events; 6 | 7 | public class InputManager : MonoBehaviour { 8 | 9 | private static InputManager control; 10 | 11 | public bool isRebinding; 12 | public KeyCode cancelRebindKey = KeyCode.Escape; 13 | [SerializeField] 14 | private Keybinds keyBinds; 15 | [SerializeField] 16 | [ValidateInput("SetSingleton")] 17 | private List events = new List(); 18 | 19 | private Dictionary lookup; 20 | private int numKeyCodes; 21 | 22 | private void Awake() { 23 | control = this; 24 | 25 | numKeyCodes = System.Enum.GetNames(typeof(KeyCode)).Length; 26 | 27 | LoadKeyBinds(); 28 | BuildLookup(); 29 | } 30 | 31 | private bool SetSingleton() { 32 | if (control == this) 33 | return true; 34 | control = this; 35 | return true; 36 | } 37 | 38 | private void Update() { 39 | foreach (InputEvent e in events) { 40 | switch (e.type) { 41 | case InputEventType.OnKeyDown: 42 | if (GetKeyDown(e.name, e.negative)) 43 | e.callback.Invoke(); 44 | break; 45 | case InputEventType.OnKey: 46 | if (GetKey(e.name, e.negative)) 47 | e.callback.Invoke(); 48 | break; 49 | case InputEventType.OnKeyUp: 50 | if (GetKeyUp(e.name, e.negative)) 51 | e.callback.Invoke(); 52 | break; 53 | case InputEventType.OnAxisDown: 54 | if (GetAxisDown(e.name)) 55 | e.callback.Invoke(); 56 | break; 57 | case InputEventType.OnAxisUp: 58 | if (GetAxisUp(e.name)) 59 | e.callback.Invoke(); 60 | break; 61 | } 62 | } 63 | } 64 | 65 | private string[] GetInputNames() { 66 | string[] names = new string[keyBinds.actions.Length]; 67 | for (int i = 0; i < names.Length; i++) 68 | names[i] = keyBinds.actions[i].name; 69 | return names; 70 | } 71 | 72 | public bool AddListener(string name, bool negative, InputEventType type, UnityAction callback) { 73 | if (lookup == null) 74 | BuildLookup(); 75 | if (lookup.ContainsKey(name)) { 76 | events.Add(new InputEvent(name, negative, type, callback)); 77 | return true; 78 | } else 79 | return false; 80 | } 81 | 82 | /// 83 | /// Has the key been pressed this frame? 84 | /// 85 | /// The name of the input. 86 | /// Should access the negative value? Default is positive. 87 | public static bool GetKeyDown(string name, bool negative = false) { 88 | control.CheckNameValid(name); 89 | InputAction action = control.lookup[name]; 90 | 91 | return Input.GetKeyDown(negative?action.modifiedNegative1 : action.modifiedPositive1) || 92 | Input.GetKeyDown(negative?action.modifiedNegative2 : action.modifiedPositive2); 93 | } 94 | 95 | /// 96 | /// Is the key being pressed right now? 97 | /// 98 | /// The name of the input. 99 | /// Should access the negative value? Default is positive. 100 | public static bool GetKey(string name, bool negative = false) { 101 | control.CheckNameValid(name); 102 | InputAction action = control.lookup[name]; 103 | 104 | return Input.GetKey(negative?action.modifiedNegative1 : action.modifiedPositive1) || 105 | Input.GetKey(negative?action.modifiedNegative2 : action.modifiedPositive2); 106 | } 107 | 108 | /// 109 | /// Has the key been released this frame? 110 | /// 111 | /// The name of the input. 112 | /// Should access the negative value? Default is positive. 113 | public static bool GetKeyUp(string name, bool negative = false) { 114 | control.CheckNameValid(name); 115 | InputAction action = control.lookup[name]; 116 | 117 | return Input.GetKeyUp(negative?action.modifiedNegative1 : action.modifiedPositive1) || 118 | Input.GetKeyUp(negative?action.modifiedNegative1 : action.modifiedPositive1); 119 | } 120 | 121 | /// 122 | /// Get the keycode of the input. 123 | /// 124 | /// The name of the input. 125 | /// Should query the primary bindings? 126 | /// Should access the negative value? Default is positive. 127 | public static KeyCode GetBinding(string name, bool primary = true, bool negative = false) { 128 | if (primary) 129 | return negative ? control.lookup[name].modifiedNegative1 : control.lookup[name].modifiedPositive1; 130 | else 131 | return negative ? control.lookup[name].modifiedNegative2 : control.lookup[name].modifiedPositive2; 132 | } 133 | 134 | /// 135 | /// Returns -1 if the negative key is pressed, 1 if the positive key is pressed, and 0 if both or neither. 136 | /// 137 | /// The name of the input. 138 | public static int GetAxis(string name) => (GetKey(name) ? 1 : 0) + (GetKey(name, true) ? -1 : 0); 139 | 140 | /// 141 | /// Has either of the keys on the axis been pressed this frame? 142 | /// 143 | /// The name of the input. 144 | public static bool GetAxisDown(string name) => GetKeyDown(name) || GetKeyDown(name, true); 145 | 146 | /// 147 | /// Has either of the keys on the axis been released this frame? 148 | /// 149 | /// The name of the input. 150 | public static bool GetAxisUp(string name) => GetKeyUp(name) || GetKeyUp(name, true); 151 | 152 | /// 153 | /// Sets the bindings of all keys back to their defaults. 154 | /// 155 | public static void ResetAllBindings() { 156 | for (int i = 0; i < control.keyBinds.actions.Length; i++) 157 | control.keyBinds.actions[i].ResetBindings(); 158 | SaveKeyBinds(); 159 | } 160 | 161 | /// 162 | /// Resets a single input binding. 163 | /// 164 | /// The name of the input. 165 | /// Should access the negative value? Default is positive. 166 | public static void ResetBinding(string name, bool negative = false) { 167 | control.CheckNameValid(name); 168 | 169 | control.lookup[name].ResetBinding(negative); 170 | SaveKeyBinds(); 171 | } 172 | 173 | private void CheckNameValid(string name) { 174 | if (!lookup.ContainsKey(name)) 175 | throw new System.Exception($"hmm fuck, dont seem to have any binding w the name {name}???"); 176 | } 177 | 178 | // to b used w SaveSystem.cs on github: 179 | // https://github.com/celechii/Unity-Tools/blob/7d1182ea0f34b9b517bdbd67f88cb275316f1c2e/SaveSystem.cs 180 | // <3 181 | /// 182 | /// Uses the save system to save the keybind JSON to /Resources/Saves/Keybinds.txt 183 | /// 184 | public static void SaveKeyBinds() { 185 | SaveSystem.SaveTxt("Keybinds", control.keyBinds); 186 | } 187 | 188 | /// 189 | /// Uses the save system to load the keybind JSON from /Resources/Saves/Keybinds.txt 190 | /// If nothing is found, it will load the defaults instead. 191 | /// 192 | public void LoadKeyBinds() { 193 | if (SaveSystem.SaveExists("Keybinds.txt")) 194 | keyBinds = SaveSystem.LoadTxt("Keybinds"); 195 | else 196 | ResetAllBindings(); 197 | BuildLookup(); 198 | } 199 | 200 | private void BuildLookup() { 201 | if (lookup == null) 202 | lookup = new Dictionary(); 203 | else 204 | lookup.Clear(); 205 | 206 | for (int i = 0; i < keyBinds.actions.Length; i++) 207 | lookup.Add(keyBinds.actions[i].name, keyBinds.actions[i]); 208 | } 209 | 210 | /// 211 | /// Start the rebind process for a single input. 212 | /// 213 | /// The name of the input. 214 | /// Method to call when the rebinding is complete. 215 | /// Should access the negative value? Default is positive. 216 | public static void RebindKey(string name, System.Action callback = null, bool negative = false) { 217 | control.CheckNameValid(name); 218 | Debug.Log(control); 219 | control.StartCoroutine(control.RebindInput(name, callback, negative)); 220 | } 221 | 222 | private IEnumerator RebindInput(string name, System.Action callback = null, bool negative = false) { 223 | isRebinding = true; 224 | yield return null; 225 | 226 | while (isRebinding) { 227 | if (Input.GetKeyDown(cancelRebindKey)) { 228 | isRebinding = false; 229 | break; 230 | } 231 | 232 | if (Input.anyKeyDown) { 233 | for (int i = 0; i < numKeyCodes; i++) { 234 | if (Input.GetKeyDown((KeyCode)i)) { 235 | lookup[name].SetKey((KeyCode)i, negative); 236 | isRebinding = false; 237 | } 238 | } 239 | } 240 | yield return null; 241 | } 242 | SaveKeyBinds(); 243 | callback?.Invoke(); 244 | } 245 | 246 | public static string GetKeyCodeNiceName(KeyCode key) { 247 | switch (key) { 248 | case KeyCode.Mouse0: 249 | return "Left Mouse Button"; 250 | case KeyCode.Mouse1: 251 | return "Right Mouse Button"; 252 | case KeyCode.Mouse2: 253 | return "Middle Mouse Button"; 254 | case KeyCode.Mouse3: 255 | case KeyCode.Mouse4: 256 | case KeyCode.Mouse5: 257 | case KeyCode.Mouse6: 258 | return "Mouse Button " + ((KeyCode)(int)key - 323); 259 | case KeyCode.Alpha0: 260 | case KeyCode.Alpha1: 261 | case KeyCode.Alpha2: 262 | case KeyCode.Alpha3: 263 | case KeyCode.Alpha4: 264 | case KeyCode.Alpha5: 265 | case KeyCode.Alpha6: 266 | case KeyCode.Alpha7: 267 | case KeyCode.Alpha8: 268 | case KeyCode.Alpha9: 269 | return ((int)key - 48).ToString(); 270 | case KeyCode.Keypad0: 271 | case KeyCode.Keypad1: 272 | case KeyCode.Keypad2: 273 | case KeyCode.Keypad3: 274 | case KeyCode.Keypad4: 275 | case KeyCode.Keypad5: 276 | case KeyCode.Keypad6: 277 | case KeyCode.Keypad7: 278 | case KeyCode.Keypad8: 279 | case KeyCode.Keypad9: 280 | return "NUM " + ((KeyCode)(int)key - 256); 281 | default: 282 | return MakeKeyReadable(key); 283 | } 284 | } 285 | 286 | public static string MakeKeyReadable(KeyCode key) { 287 | string entry = key.ToString(); 288 | for (int i = 1; i < entry.Length; i++) { 289 | if (entry[i] >= 'A' && entry[i] <= 'Z') { 290 | entry = entry.Insert(i, " "); 291 | i++; 292 | } 293 | } 294 | return entry; 295 | } 296 | 297 | [System.Serializable] 298 | private struct Keybinds { 299 | public InputAction[] actions; 300 | } 301 | 302 | [System.Serializable] 303 | public class InputAction { 304 | public string name; 305 | public KeyCode positive1; 306 | public KeyCode negative1; 307 | [Space] 308 | public KeyCode positive2; 309 | public KeyCode negative2; 310 | [HideInInspector] 311 | public KeyCode modifiedPositive1; 312 | [HideInInspector] 313 | public KeyCode modifiedNegative1; 314 | [HideInInspector] 315 | public KeyCode modifiedPositive2; 316 | [HideInInspector] 317 | public KeyCode modifiedNegative2; 318 | 319 | public void SetKey(KeyCode key, bool primary = true, bool negative = false) { 320 | if (primary) { 321 | if (negative) 322 | modifiedNegative1 = key; 323 | else 324 | modifiedPositive1 = key; 325 | } else { 326 | if (negative) 327 | modifiedNegative2 = key; 328 | else 329 | modifiedPositive2 = key; 330 | } 331 | } 332 | 333 | public void ResetBindings() { 334 | modifiedPositive1 = positive1; 335 | modifiedNegative1 = negative1; 336 | modifiedPositive2 = positive2; 337 | modifiedNegative2 = negative2; 338 | } 339 | 340 | public void ResetBinding(bool primary = true, bool negative = false) { 341 | if (primary) { 342 | if (negative) 343 | modifiedNegative1 = this.negative1; 344 | else 345 | modifiedPositive1 = positive1; 346 | } else { 347 | if (negative) 348 | modifiedNegative2 = this.negative2; 349 | else 350 | modifiedPositive2 = positive2; 351 | } 352 | } 353 | } 354 | 355 | [System.Serializable] 356 | private class InputEvent { 357 | [Dropdown("GetInputNames")] 358 | public string name; 359 | [AllowNesting] 360 | [HideIf("hideNegative")] 361 | public bool negative; 362 | public InputEventType type; 363 | [Space] 364 | public UnityEvent callback; 365 | 366 | private bool hideNegative => type == InputEventType.OnAxisDown || type == InputEventType.OnAxisUp; 367 | 368 | public InputEvent(string name, bool negative, InputEventType type, UnityAction callback) { 369 | this.name = name; 370 | this.negative = negative; 371 | this.type = type; 372 | this.callback = new UnityEvent(); 373 | this.callback.AddListener(callback); 374 | } 375 | 376 | private string[] GetInputNames() => InputManager.control.GetInputNames(); 377 | } 378 | 379 | public enum InputEventType { 380 | OnKeyDown, 381 | OnKey, 382 | OnKeyUp, 383 | OnAxisDown, 384 | OnAxisUp 385 | } 386 | } -------------------------------------------------------------------------------- /InvisibleBox.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | [ExecuteInEditMode] 4 | public class InvisibleBox : MonoBehaviour { 5 | 6 | public Vector2 size = Vector2.one * 4; 7 | public Vector2 offset; 8 | public bool useCentreOrigin = true; 9 | [SerializeField] 10 | private TransformPosition useTransformPosition = TransformPosition.Local; 11 | [SerializeField] 12 | private bool showBox = true; 13 | 14 | public bool IsInBox(Vector2 position) { 15 | 16 | // if (useTransformPosition == TransformPosition.World) 17 | // position -= (Vector2)transform.position; 18 | 19 | if (useTransformPosition == TransformPosition.Local) 20 | position -= (Vector2)transform.localPosition; 21 | 22 | position -= offset; 23 | 24 | if (useCentreOrigin) 25 | position += size / 2f; 26 | 27 | return position.IsInsideInvisibleBox2D(Vector2.zero, size); 28 | } 29 | 30 | public bool IsInBox(Transform trans, bool useLocal = true) => IsInBox((useLocal?trans.localPosition : Vector3.zero)); 31 | 32 | private void OnDrawGizmos() { 33 | if (showBox) { 34 | Gizmos.color = Color.green; 35 | 36 | Vector2 worldOffset = Vector2.zero; 37 | if (useTransformPosition == TransformPosition.Local) 38 | worldOffset = transform.localPosition; 39 | 40 | if (useCentreOrigin) 41 | Gizmos.DrawWireCube(offset + worldOffset, size); 42 | else 43 | Gizmos.DrawWireCube(offset + worldOffset + size / 2f, size); 44 | 45 | } 46 | } 47 | 48 | private enum TransformPosition { 49 | World, 50 | Local 51 | } 52 | } -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2020 Noé Charron 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 12 | 13 | -------------------------------------------------------------------------------- /LissajousCurve.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | [System.Serializable] 4 | public class LissajousCurve { 5 | public Vector2 size = Vector2.one; 6 | public int ratio = 1; 7 | 8 | private const float tau = Mathf.PI * 2f; 9 | 10 | public Vector2 Evaluate(float percent) { 11 | return size * new Vector2( 12 | Mathf.Sin(tau * percent), 13 | Mathf.Cos(tau * percent * ratio) 14 | ); 15 | } 16 | 17 | public void DrawCurve(Vector3 position) { 18 | Gizmos.color = Color.white; 19 | int segments = 100; 20 | float increment = 1f / segments; 21 | for (float i = 0; i < 1; i += increment) 22 | Gizmos.DrawLine(position + (Vector3)Evaluate(i), position + (Vector3)Evaluate(i + increment)); 23 | Gizmos.color = new Color(1, 1, 1, .35f); 24 | Gizmos.DrawWireCube(position, size * 2f); 25 | Gizmos.DrawLine(position + Vector3.right * size.x, position + Vector3.left * size.x); 26 | Gizmos.DrawLine(position + Vector3.up * size.y, position + Vector3.down * size.y); 27 | } 28 | } -------------------------------------------------------------------------------- /Log Toggle/Editor/LogToggleDrawer.cs: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | Copyright (c) 2022 Noé Charron 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 15 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 16 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 17 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 18 | SOFTWARE. 19 | */ 20 | 21 | using UnityEditor; 22 | using UnityEngine; 23 | 24 | [CustomPropertyDrawer(typeof(LogToggle))] 25 | public class LogToggleDrawer : PropertyDrawer { 26 | 27 | public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { 28 | label.text = $"Show {label}"; 29 | EditorGUILayout.PropertyField(property.FindPropertyRelative("show"), label); 30 | } 31 | 32 | public override float GetPropertyHeight(SerializedProperty property, GUIContent label) => 0; 33 | } -------------------------------------------------------------------------------- /Log Toggle/LogToggle.cs: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | Copyright (c) 2022 Noé Charron 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 15 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 16 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 17 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 18 | SOFTWARE. 19 | */ 20 | 21 | using UnityEngine; 22 | 23 | [System.Serializable] 24 | public struct LogToggle { 25 | 26 | [SerializeField] 27 | private bool show; 28 | 29 | public LogToggle(bool show) { 30 | this.show = show; 31 | } 32 | 33 | public void Log(object message) => Log(message, null); 34 | public void Log(object message, Object context) { 35 | if (show) 36 | Debug.Log(message, context); 37 | } 38 | 39 | public void LogWarning(object message) => LogWarning(message, null); 40 | public void LogWarning(object message, Object context) { 41 | if (show) 42 | Debug.LogWarning(message, context); 43 | } 44 | 45 | public void LogError(string message) => LogError(message, null); 46 | public void LogError(object message, Object context) { 47 | if (show) 48 | Debug.LogError(message, context); 49 | } 50 | 51 | public static implicit operator bool(LogToggle tl) => tl.show; 52 | 53 | public static implicit operator LogToggle(bool value) => new LogToggle(value); 54 | } -------------------------------------------------------------------------------- /MeshSorter.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | [RequireComponent(typeof(MeshRenderer))] 4 | public class MeshSorter : MonoBehaviour { 5 | 6 | [SerializeField] 7 | private string layerName = "UI"; 8 | [SerializeField] 9 | private int sortingOrder = 1; 10 | private MeshRenderer meshRenderer; 11 | 12 | [ContextMenu("Update Shit")] 13 | void OnEnable() { 14 | meshRenderer = GetComponent(); 15 | Set(layerName, sortingOrder); 16 | } 17 | 18 | public void Set(string layerName, int sortingOrder) { 19 | this.layerName = layerName; 20 | this.sortingOrder = sortingOrder; 21 | 22 | meshRenderer.sortingLayerName = layerName; 23 | meshRenderer.sortingOrder = sortingOrder; 24 | } 25 | } -------------------------------------------------------------------------------- /MoonCalculator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | // derived from https://www.subsystems.us/uploads/9/8/9/4/98948044/moonphase.pdf initially 5 | // mostly from https://www.timeanddate.com/moon/phases/ 6 | 7 | public abstract class MoonCalculator : MonoBehaviour { 8 | 9 | public static float MoonCycle { get { return (float) 25101 / 850; } } 10 | 11 | public static float GetPercent() { 12 | DateTime calibrationDate = new DateTime(2019, 3, 6, 11, 3, 0); // mar 6, 2019 @ 11:03am 13 | double daysSince = (DateTime.Today - calibrationDate).TotalDays; 14 | return (float) daysSince / MoonCycle; 15 | } 16 | 17 | public static float DaysIntoCycle() { 18 | return GetPercent() * MoonCycle; 19 | } 20 | 21 | public static MoonPhase GetPhase() { 22 | float percent = GetPercent(); 23 | int numPhases = Enum.GetValues(typeof(MoonPhase)).Length; 24 | int closest = Mathf.RoundToInt(percent * (numPhases + 1)); 25 | return (MoonPhase) (closest % numPhases); 26 | } 27 | 28 | public enum MoonPhase { 29 | New, 30 | WaxingCrescent, 31 | FirstQuarter, 32 | WaxingGibbous, 33 | Full, 34 | WaningGibbous, 35 | ThirdQuarter, 36 | WaningCrescent 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /MultiBool.cs: -------------------------------------------------------------------------------- 1 | public struct MultiBool { 2 | 3 | private int count; 4 | private bool hasMin; 5 | private bool hasMax; 6 | private int max; 7 | private int min; 8 | private bool defaultValue; 9 | 10 | public MultiBool(bool defaultValue) : this(defaultValue, false, 0, false, 0) {} 11 | public MultiBool(bool defaultValue, bool hasMin, int min, bool hasMax, int max) { 12 | count = 0; 13 | this.defaultValue = defaultValue; 14 | this.hasMin = hasMin; 15 | this.min = min; 16 | this.hasMax = hasMax; 17 | this.max = max; 18 | } 19 | 20 | public static MultiBool WithMin(int min) => WithMin(min, true); 21 | public static MultiBool WithMin(int min, bool defaultValue) { 22 | MultiBool b = new MultiBool(defaultValue); 23 | b.hasMin = true; 24 | b.min = min; 25 | return b; 26 | } 27 | 28 | public static MultiBool WithMax(int max) => WithMax(max, true); 29 | public static MultiBool WithMax(int max, bool defaultValue) { 30 | MultiBool b = new MultiBool(defaultValue); 31 | b.hasMax = true; 32 | b.max = max; 33 | return b; 34 | } 35 | 36 | public static MultiBool WithMinAndMax(int min, int max) => WithMinAndMax(min, max, true); 37 | public static MultiBool WithMinAndMax(int min, int max, bool defaultValue) { 38 | MultiBool b = new MultiBool(defaultValue); 39 | b.hasMin = true; 40 | b.hasMax = true; 41 | if (min <= max) { 42 | b.min = min; 43 | b.max = max; 44 | } else { 45 | b.min = max; 46 | b.max = min; 47 | } 48 | return b; 49 | } 50 | 51 | public void Set(int amount) { 52 | count = amount; 53 | ClampCount(); 54 | } 55 | 56 | public void Reset() { 57 | count = 0; 58 | ClampCount(); 59 | } 60 | 61 | public void AddTrue() { 62 | count++; 63 | ClampCount(); 64 | } 65 | 66 | public void AddFalse() { 67 | count--; 68 | ClampCount(); 69 | } 70 | 71 | public void Add(bool value) { 72 | Add(value ? 1 : -1); 73 | } 74 | 75 | public void Add(int amount) { 76 | count += amount; 77 | ClampCount(); 78 | } 79 | 80 | private void ClampCount() { 81 | if (hasMin && count < min) 82 | count = min; 83 | else if (hasMax && count > max) 84 | count = max; 85 | } 86 | 87 | public static implicit operator bool(MultiBool b) => b.count > 0 || b.defaultValue && b.count == 0; 88 | public static implicit operator MultiBool(bool b) => new MultiBool(b); 89 | 90 | public static MultiBool operator ++(MultiBool b) { 91 | b.AddTrue(); 92 | return b; 93 | } 94 | 95 | public static MultiBool operator --(MultiBool b) { 96 | b.AddFalse(); 97 | return b; 98 | } 99 | 100 | public override string ToString() { 101 | int value = count; 102 | if (value >= 0 && defaultValue) 103 | value++; 104 | else if (value <= 0 && !defaultValue) { 105 | value--; 106 | value *= -1; 107 | } 108 | 109 | return $"{(bool)this} ({value})"; 110 | } 111 | } -------------------------------------------------------------------------------- /MultiEnum/Editor/MultiEnumFlagsAttributeDrawer.cs: -------------------------------------------------------------------------------- 1 | using UnityEditor; 2 | using UnityEngine; 3 | 4 | [CustomPropertyDrawer(typeof(MultiEnumAttribute))] 5 | public class EnumFlagsAttributeDrawer : PropertyDrawer { 6 | 7 | public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { 8 | 9 | property.intValue = EditorGUI.MaskField(position, label, property.intValue, property.enumNames); 10 | 11 | } 12 | } -------------------------------------------------------------------------------- /MultiEnum/MultiEnumAttribute.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | // by froodydude 4 | // https://answers.unity.com/questions/486694/default-editor-enum-as-flags-.html?childToView=514102#answer-514102 5 | 6 | // how to use cause ik you'll forget: 7 | // [System.Flags] 8 | // enum Weekday { 9 | // Su = 1 << 0, 10 | // M = 1 << 1, 11 | // Tu = 1 << 2, 12 | // W = 1 << 3, 13 | // Th = 1 << 4, 14 | // F = 1 << 5, 15 | // Sa = 1 << 6 16 | // } 17 | 18 | // [MultiEnum] 19 | // public Weekday day; 20 | 21 | public class MultiEnumAttribute : PropertyAttribute { 22 | public MultiEnumAttribute() {} 23 | } -------------------------------------------------------------------------------- /MultiEnum/MultiEnumHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | public static class MultiEnumHelper { 5 | 6 | /// 7 | /// Does the enum contain the flag? 8 | /// 9 | public static bool Has(this TEnum multiEnum, TEnum flag)where TEnum : struct, IConvertible { 10 | return (Convert.ToInt32(multiEnum) & Convert.ToInt32(flag)) > 0; 11 | } 12 | 13 | /// 14 | /// Does this enum have each flag? 15 | /// 16 | public static bool HasNone(this TEnum multiEnum)where TEnum : struct, IConvertible => Convert.ToInt32(multiEnum) == 0; 17 | 18 | /// 19 | /// Does this enum have each flag? 20 | /// 21 | public static bool HasAll(this TEnum multiEnum, TEnum flag)where TEnum : struct, IConvertible { 22 | return multiEnum.Has(flag); 23 | } 24 | 25 | /// 26 | /// Does this enum have each of the flags set? 27 | /// 28 | public static bool HasAll(this TEnum multiEnum, params TEnum[] flags)where TEnum : struct, IConvertible { 29 | foreach (TEnum flag in flags) 30 | if (!multiEnum.Has(flag)) 31 | return false; 32 | return true; 33 | } 34 | 35 | /// 36 | /// Does this enum have every flag set? 37 | /// 38 | public static bool HasAll(this TEnum multiEnum)where TEnum : struct, IConvertible { 39 | foreach (TEnum flag in Enum.GetValues(typeof(TEnum))) 40 | if (!multiEnum.Has(flag)) 41 | return false; 42 | return true; 43 | } 44 | 45 | /// 46 | /// Gets a combined enum with all the specified flags set 47 | /// 48 | public static TEnum Combine(params TEnum[] flags)where TEnum : struct, IConvertible { 49 | if (flags.Length == 0) 50 | throw new ArgumentOutOfRangeException(); 51 | if (flags == null) 52 | throw new ArgumentNullException(); 53 | 54 | TEnum multiEnum = flags[0]; 55 | for (int i = 1; i < flags.Length; i++) 56 | multiEnum.Add(flags[i]); 57 | return multiEnum; 58 | } 59 | 60 | /// 61 | /// Get an array of the individual flags. 62 | /// 63 | public static TEnum[] GetArray(this TEnum multiEnum)where TEnum : struct, IConvertible { 64 | int enumSize = Enum.GetValues(typeof(TEnum)).Length; 65 | List flags = new List(); 66 | 67 | TEnum[] allFlags = (TEnum[])Enum.GetValues(typeof(TEnum)); 68 | for (int i = 0; i < allFlags.Length; i++) { 69 | if (multiEnum.Has((TEnum)allFlags.GetValue(i))) 70 | flags.Add((TEnum)allFlags.GetValue(i)); 71 | } 72 | return flags.ToArray(); 73 | } 74 | 75 | /// 76 | /// Gets the number of flags that are currently set. 77 | /// 78 | public static int Count(this TEnum multiEnum)where TEnum : struct, IConvertible { 79 | int bits = Convert.ToInt32(multiEnum); 80 | int count = 0; 81 | for (int i = 0; i < 32; i++) 82 | if ((bits & (1 << i)) > 0) 83 | count++; 84 | return count; 85 | } 86 | 87 | /// 88 | /// Add a flag to the enum. 89 | /// 90 | public static void Add(ref this TEnum multiEnum, TEnum flag)where TEnum : struct, IConvertible { 91 | int result = Convert.ToInt32(multiEnum) | Convert.ToInt32(flag); 92 | multiEnum = (TEnum)Enum.ToObject(typeof(TEnum), result); 93 | } 94 | 95 | /// 96 | /// Sets all the flags. 97 | /// 98 | public static void AddAll(ref this TEnum multiEnum)where TEnum : struct, IConvertible { 99 | int value = 0; 100 | int enumSize = Enum.GetValues(typeof(TEnum)).Length; 101 | for (int i = 0; i < enumSize; i++) 102 | value += 1 << i; 103 | 104 | multiEnum = (TEnum)Enum.ToObject(typeof(TEnum), value); 105 | } 106 | 107 | /// 108 | /// Removes the flag on the enum if it has it. 109 | /// 110 | public static void Remove(ref this TEnum multiEnum, TEnum flag)where TEnum : struct, IConvertible { 111 | int result = Convert.ToInt32(multiEnum) & ~Convert.ToInt32(flag); 112 | multiEnum = (TEnum)Enum.ToObject(typeof(TEnum), result); 113 | } 114 | 115 | /// 116 | /// Removes all the flags on the enum. 117 | /// 118 | public static void RemoveAll(ref this TEnum multiEnum)where TEnum : struct, IConvertible { 119 | multiEnum = (TEnum)Enum.ToObject(typeof(TEnum), 0); 120 | } 121 | 122 | /// 123 | /// Either sets or removes all the flags on the enum. 124 | /// 125 | public static void SetAll(ref this TEnum multiEnum, bool on)where TEnum : struct, IConvertible { 126 | if (on) 127 | multiEnum.AddAll(); 128 | else 129 | multiEnum.RemoveAll(); 130 | } 131 | 132 | /// 133 | /// Flip a flag on the enum. 134 | /// 135 | public static void Toggle(ref this TEnum multiEnum, TEnum flag)where TEnum : struct, IConvertible { 136 | int result = Convert.ToInt32(multiEnum) & Convert.ToInt32(flag); 137 | multiEnum = (TEnum)Enum.ToObject(typeof(TEnum), result); 138 | } 139 | 140 | public static string ToString(this TEnum multiEnum)where TEnum : struct, IConvertible { 141 | TEnum[] array = multiEnum.GetArray(); 142 | if (array.Length == 0) 143 | return ""; 144 | string s = array[0].ToString(); 145 | for (int i = 1; i < array.Length; i++) 146 | s += ", " + array[i]; 147 | return s; 148 | } 149 | } -------------------------------------------------------------------------------- /NAudio.cs: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Noé Charron 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | using System.Collections.Generic; 26 | using NaughtyAttributes; 27 | using UnityEngine; 28 | using UnityEngine.Audio; 29 | 30 | public static class NAudio { 31 | 32 | private static List sources = new List(); 33 | 34 | private static Transform sourcePool; 35 | private const float DEFAULT_MIN_DIST = 1f; 36 | private const float DEFAULT_MAX_DIST = 500f; 37 | 38 | public static float RemapVolume(float targetVolume) => (1 - Mathf.Pow(targetVolume, 1f / 0.2f)) + 2f * targetVolume - 1; 39 | 40 | // multiple sounds, no pitch 41 | public static void PlaySound(AudioClip[] sounds, float volume = 1f, AudioMixerGroup mixerGroup = null) => PlaySound(sounds[Random.Range(0, sounds.Length)], volume, mixerGroup); 42 | public static void PlaySound(AudioClip[] sounds, Transform source, bool worldSpace, float volume = 1f, AudioMixerGroup mixerGroup = null) => PlaySound(sounds[Random.Range(0, sounds.Length)], source, worldSpace, volume, mixerGroup); 43 | public static void PlaySound(AudioClip[] sounds, Vector3 worldPosition, float volume = 1f, AudioMixerGroup mixerGroup = null) => PlaySound(sounds[Random.Range(0, sounds.Length)], worldPosition, volume, mixerGroup); 44 | 45 | // multiple sounds, pitch range 46 | public static void PlaySound(AudioClip[] sounds, float minPitch, float maxPitch, float volume = 1f, AudioMixerGroup mixerGroup = null) => PlaySound(sounds[Random.Range(0, sounds.Length)], minPitch, maxPitch, volume, mixerGroup); 47 | public static void PlaySound(AudioClip[] sounds, float minPitch, float maxPitch, Transform source, bool worldSpace, float volume = 1f, AudioMixerGroup mixerGroup = null) => PlaySound(sounds[Random.Range(0, sounds.Length)], minPitch, maxPitch, source, worldSpace, volume, mixerGroup); 48 | public static void PlaySound(AudioClip[] sounds, float minPitch, float maxPitch, Vector3 worldPosition, float volume = 1f, AudioMixerGroup mixerGroup = null) => PlaySound(sounds[Random.Range(0, sounds.Length)], minPitch, maxPitch, worldPosition, volume, mixerGroup); 49 | 50 | // multiple sounds, single pictch 51 | public static void PlaySound(AudioClip[] sounds, float pitch, float volume = 1f, AudioMixerGroup mixerGroup = null) => PlaySound(sounds[Random.Range(0, sounds.Length)], pitch, mixerGroup); 52 | public static void PlaySound(AudioClip[] sounds, float pitch, Transform source, bool worldSpace, float volume = 1f, AudioMixerGroup mixerGroup = null) => PlaySound(sounds[Random.Range(0, sounds.Length)], pitch, source, worldSpace, volume, mixerGroup); 53 | public static void PlaySound(AudioClip[] sounds, float pitch, Vector3 worldPosition, float volume = 1f, AudioMixerGroup mixerGroup = null) => PlaySound(sounds[Random.Range(0, sounds.Length)], pitch, worldPosition, volume, mixerGroup); 54 | 55 | // single sound, no pitch 56 | public static void PlaySound(AudioClip sound, float volume = 1f, AudioMixerGroup mixerGroup = null) => PlaySound(sound, 1, mixerGroup); 57 | public static void PlaySound(AudioClip sound, Transform source, bool worldSpace, float volume = 1f, AudioMixerGroup mixerGroup = null) => PlaySound(sound, 1, source, worldSpace, volume, mixerGroup); 58 | public static void PlaySound(AudioClip sound, Vector3 worldPosition, float volume = 1f, AudioMixerGroup mixerGroup = null) => PlaySound(sound, 1, worldPosition, volume, mixerGroup); 59 | 60 | // single sound, pitch range 61 | public static void PlaySound(AudioClip sound, float minPitch, float maxPitch, float volume = 1f, AudioMixerGroup mixerGroup = null) => PlaySound(sound, minPitch, maxPitch, sourcePool, true, volume, mixerGroup); 62 | public static void PlaySound(AudioClip sound, float minPitch, float maxPitch, Transform source, bool worldSpace, float volume = 1f, AudioMixerGroup mixerGroup = null) => PlaySound(sound, Random.Range(minPitch, maxPitch), source, worldSpace, volume, mixerGroup); 63 | public static void PlaySound(AudioClip sound, float minPitch, float maxPitch, Vector3 worldPosition, float volume = 1f, AudioMixerGroup mixerGroup = null) => PlaySound(sound, Random.Range(minPitch, maxPitch), worldPosition, volume, mixerGroup); 64 | 65 | // single sound, single pitch 66 | public static void PlaySound(AudioClip sound, float pitch, float volume = 1f, AudioMixerGroup mixerGroup = null, float minDist = DEFAULT_MIN_DIST, float maxDist = DEFAULT_MAX_DIST) { 67 | AudioSource2DLink link = FindLink(); 68 | if (link == null) { 69 | link = new AudioSource2DLink(mixerGroup); 70 | sources.Add(link); 71 | } else 72 | link.UpdateLink(mixerGroup); 73 | link.PlaySound(sound, volume, pitch, minDist, maxDist); 74 | } 75 | 76 | public static void PlaySound(AudioClip sound, float pitch, Transform source, bool worldSpace, float volume = 1f, AudioMixerGroup mixerGroup = null, float minDist = DEFAULT_MIN_DIST, float maxDist = DEFAULT_MAX_DIST) { 77 | AudioSourceTransformLink link = FindLink(); 78 | if (link == null) { 79 | link = new AudioSourceTransformLink(source, worldSpace, mixerGroup); 80 | sources.Add(link); 81 | } else 82 | link.UpdateLink(source, worldSpace, mixerGroup, minDist, maxDist); 83 | link.PlaySound(sound, volume, pitch, minDist, maxDist); 84 | } 85 | public static void PlaySound(AudioClip sound, float pitch, Vector3 position, float volume = 1f, AudioMixerGroup mixerGroup = null, float minDist = DEFAULT_MIN_DIST, float maxDist = DEFAULT_MAX_DIST) { 86 | AudioSourcePositionLink link = FindLink(); 87 | if (link == null) { 88 | link = new AudioSourcePositionLink(position, mixerGroup); 89 | sources.Add(link); 90 | } else 91 | link.UpdateLink(position, mixerGroup); 92 | link.PlaySound(sound, volume, pitch, minDist, maxDist); 93 | } 94 | 95 | private static T FindLink()where T : AudioSourceLink { 96 | foreach (AudioSourceLink source in sources) 97 | if (!source.Active && source.GetType() == typeof(T)) 98 | return (T)source; 99 | return null; 100 | } 101 | 102 | private class NAudioUpdater : MonoBehaviour { 103 | private void Start() { 104 | LateUpdate(); 105 | } 106 | 107 | private void LateUpdate() { 108 | foreach (AudioSourceLink sources in NAudio.sources) 109 | if (sources.Active) 110 | sources.UpdatePosition(); 111 | } 112 | 113 | private void OnDestroy() { 114 | sources.Clear(); 115 | } 116 | } 117 | 118 | private abstract class AudioSourceLink { 119 | private static int sourceCount; 120 | 121 | public AudioSource audioSource; 122 | public int id; 123 | public bool Active => audioSource.isPlaying; 124 | 125 | protected abstract void GoToPosition(); 126 | 127 | public AudioSourceLink() { 128 | id = sourceCount++; 129 | GameObject go = new GameObject($"Sound {id}", typeof(AudioSource)); 130 | audioSource = go.GetComponent(); 131 | audioSource.spatialBlend = 1f; 132 | 133 | if (NAudio.sourcePool == null) 134 | NAudio.sourcePool = new GameObject("Audio Pool", typeof(NAudioUpdater)).transform; 135 | go.transform.SetParent(NAudio.sourcePool); 136 | } 137 | 138 | public void UpdatePosition() { 139 | if (Active) 140 | GoToPosition(); 141 | } 142 | 143 | public void PlaySound(AudioClip clip, float volume, float pitch, float minDist, float maxDist) { 144 | audioSource.volume = NAudio.RemapVolume(volume); 145 | audioSource.minDistance = minDist; 146 | audioSource.maxDistance = maxDist; 147 | audioSource.clip = clip; 148 | audioSource.pitch = pitch; 149 | audioSource.Play(); 150 | } 151 | } 152 | 153 | private class AudioSource2DLink : AudioSourceLink { 154 | 155 | public AudioSource2DLink(AudioMixerGroup mixerGroup) : base() { 156 | audioSource.spatialBlend = 0; 157 | UpdateLink(mixerGroup); 158 | } 159 | 160 | protected override void GoToPosition() {} 161 | 162 | public void UpdateLink(AudioMixerGroup mixerGroup) { 163 | audioSource.outputAudioMixerGroup = mixerGroup; 164 | } 165 | } 166 | 167 | private class AudioSourceTransformLink : AudioSourceLink { 168 | private Transform source; 169 | private bool worldSpace; 170 | 171 | public AudioSourceTransformLink(Transform transformTracker, bool worldSpace, AudioMixerGroup mixerGroup = null, float minDist = DEFAULT_MIN_DIST, float maxDist = DEFAULT_MAX_DIST) : base() { 172 | UpdateLink(transformTracker, worldSpace, mixerGroup, minDist, maxDist); 173 | } 174 | 175 | protected override void GoToPosition() { 176 | base.audioSource.transform.localPosition = worldSpace ? source.position : source.localPosition; 177 | } 178 | 179 | public void UpdateLink(Transform newSource, bool worldSpace, AudioMixerGroup mixerGroup, float minDist, float maxDist) { 180 | source = newSource; 181 | audioSource.outputAudioMixerGroup = mixerGroup; 182 | audioSource.minDistance = minDist; 183 | audioSource.maxDistance = maxDist; 184 | this.worldSpace = worldSpace; 185 | UpdatePosition(); 186 | } 187 | } 188 | 189 | private class AudioSourcePositionLink : AudioSourceLink { 190 | public AudioSourcePositionLink(Vector3 worldPosition, AudioMixerGroup mixerGroup) : base() => 191 | UpdateLink(worldPosition, mixerGroup); 192 | 193 | protected override void GoToPosition() {} 194 | 195 | public void UpdateLink(Vector3 newPosition, AudioMixerGroup mixerGroup) { 196 | audioSource.transform.localPosition = newPosition; 197 | audioSource.outputAudioMixerGroup = mixerGroup; 198 | } 199 | } 200 | } 201 | 202 | [System.Serializable] 203 | public abstract class NAudioSound { 204 | [MinMaxSlider(0f, 3f)] 205 | public Vector2 pitchRange = Vector2.one; 206 | [Range(0, 1)] 207 | public float volume = 1f; 208 | public RangeFloat distRange = new RangeFloat(1, 500); 209 | [Space] 210 | public AudioMixerGroup mixerGroup; 211 | 212 | public abstract void Play(Transform source, bool worldSpace); 213 | public abstract void Play(Vector3 worldPos); 214 | public abstract void Play(); 215 | 216 | public float Pitch => Random.Range(pitchRange.x, pitchRange.y); 217 | } 218 | 219 | [System.Serializable] 220 | public class NSoundClip : NAudioSound { 221 | public AudioClip sound; 222 | 223 | public override void Play(Transform source, bool worldSpace) => NAudio.PlaySound(sound, Pitch, source, worldSpace, volume, mixerGroup, distRange.min, distRange.max); 224 | public override void Play(Vector3 worldPos) => NAudio.PlaySound(sound, Pitch, worldPos, volume, mixerGroup, distRange.min, distRange.max); 225 | public override void Play() => NAudio.PlaySound(sound, Pitch, volume, mixerGroup, distRange.min, distRange.max); 226 | } 227 | 228 | [System.Serializable] 229 | public class NSoundList : NAudioSound { 230 | public AudioClip[] sounds; 231 | 232 | private AudioClip Sound => sounds == null || sounds.Length == 0 ? null : sounds[Random.Range(0, sounds.Length)]; 233 | 234 | public(AudioClip, float)GetSoundAndPitch() => (Sound, Pitch); 235 | 236 | public override void Play(Transform source, bool worldSpace) => NAudio.PlaySound(Sound, Pitch, source, worldSpace, volume, mixerGroup, distRange.min, distRange.max); 237 | public override void Play(Vector3 worldPos) => NAudio.PlaySound(Sound, Pitch, worldPos, volume, mixerGroup, distRange.min, distRange.max); 238 | public override void Play() => NAudio.PlaySound(Sound, Pitch, volume, mixerGroup, distRange.min, distRange.max); 239 | } -------------------------------------------------------------------------------- /NEvent.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine.Events; 2 | 3 | [System.Serializable] 4 | public class NEvent : UnityEvent {} 5 | 6 | [System.Serializable] 7 | public class NIntEvent : UnityEvent {} 8 | 9 | [System.Serializable] 10 | public class NFloatEvent : UnityEvent {} 11 | 12 | [System.Serializable] 13 | public class NBoolEvent : UnityEvent {} 14 | 15 | [System.Serializable] 16 | public class NStringEvent : UnityEvent {} -------------------------------------------------------------------------------- /NameElements/Editor/NameElementsDrawer.cs: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Noé Charron 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | using System.Collections; 26 | using UnityEditor; 27 | using UnityEngine; 28 | 29 | [CustomPropertyDrawer(typeof(NameElementsAttribute))] 30 | public class NameElementsDrawer : PropertyDrawer { 31 | 32 | public override void OnGUI(Rect rect, SerializedProperty property, GUIContent label) { 33 | 34 | object obj = fieldInfo.GetValue(property.serializedObject.targetObject); 35 | 36 | if (obj != null && (obj.GetType().IsArray || typeof(IList).IsAssignableFrom(obj.GetType()))) { 37 | int index = int.Parse(property.propertyPath.Split('[', ']')[1]); 38 | string name = ""; 39 | if (((NameElementsAttribute)attribute).drawIndex) 40 | name = index + ": "; 41 | 42 | IList list = (IList)obj; 43 | 44 | if (index < list.Count && index >= 0) 45 | name += list[index] == null ? "Empty" : list[index].ToString(); 46 | else 47 | return; 48 | 49 | label.text = name; 50 | } 51 | 52 | EditorGUI.PropertyField(rect, property, label, true); 53 | } 54 | 55 | public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { 56 | return EditorGUI.GetPropertyHeight(property, true); 57 | } 58 | } -------------------------------------------------------------------------------- /NameElements/NameElementsAttribute.cs: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Noé Charron 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | using UnityEngine; 26 | 27 | public class NameElementsAttribute : PropertyAttribute { 28 | 29 | public readonly bool drawIndex; 30 | 31 | public NameElementsAttribute(bool drawIndex = false) { 32 | this.drawIndex = drawIndex; 33 | } 34 | } -------------------------------------------------------------------------------- /ObjectInteraction.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEngine.Events; 3 | 4 | public class ObjectInteraction : MonoBehaviour { 5 | 6 | private static Camera mainCam; 7 | private static float interactDist = 5; 8 | 9 | public UnityEvent OnInteract; 10 | 11 | /// 12 | /// Call the interact from the centre of the screen 13 | /// 14 | public static void Interact() { 15 | Interact(new Vector2(.5f, .5f)); 16 | } 17 | 18 | /// 19 | /// Call the interact from the position of the mouse on the screen 20 | /// 21 | public static void InteractMouse() { 22 | if (mainCam == null) 23 | mainCam = Camera.main; 24 | Interact(mainCam.ScreenToViewportPoint(Input.mousePosition)); 25 | } 26 | 27 | /// 28 | /// Call the interact from wherever tf u want in normalized viewport coords 29 | /// 30 | public static void Interact(Vector2 normalizedPos) { 31 | if (mainCam == null) 32 | mainCam = Camera.main; 33 | 34 | Ray ray = mainCam.ViewportPointToRay(normalizedPos); 35 | RaycastHit hit; 36 | if (Physics.Raycast(ray, out hit, interactDist)) 37 | hit.transform.GetComponent()?.OnInteract.Invoke(); 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /ObjectPool.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using NaughtyAttributes; 3 | using UnityEngine; 4 | 5 | public class ObjectPool : MonoBehaviour { 6 | 7 | private static ObjectPool control; 8 | 9 | private Dictionary> objects = new Dictionary>(); 10 | 11 | private void Awake() { 12 | control = this; 13 | } 14 | 15 | public static int NumObjects => control.transform.childCount; 16 | 17 | public static int NumActiveObjects { 18 | get { 19 | Transform trans = control.transform; 20 | int num = 0; 21 | for (int i = 0; i < trans.childCount; i++) 22 | if (trans.GetChild(i).gameObject.activeSelf) 23 | num++; 24 | return num; 25 | } 26 | } 27 | 28 | public static GameObject Spawn(GameObject prefab, Vector3 position) => control.SpawnObject(prefab, position); 29 | 30 | public GameObject SpawnObject(GameObject prefab, Vector3 position) => SpawnObject(prefab, position, transform); 31 | public GameObject SpawnObject(GameObject prefab, Vector3 position, Transform parent) { 32 | 33 | if (objects.ContainsKey(prefab)) { 34 | List instances = objects[prefab]; 35 | for (int i = 0; i < instances.Count; i++) { 36 | 37 | if (instances[i] == null) { 38 | instances.RemoveAt(i); 39 | i--; 40 | } else if (!instances[i].activeSelf) { 41 | instances[i].SetActive(true); 42 | instances[i].transform.localPosition = position; 43 | return instances[i]; 44 | } 45 | } 46 | 47 | GameObject spawn = Spawn(); 48 | instances.Add(spawn); 49 | return spawn; 50 | 51 | } else { 52 | objects.Add(prefab, new List() { Spawn() }); 53 | return objects[prefab][0]; 54 | } 55 | 56 | GameObject Spawn() => Instantiate(prefab, position, Quaternion.identity, parent); 57 | } 58 | 59 | } -------------------------------------------------------------------------------- /OpenInFileBrowser.cs: -------------------------------------------------------------------------------- 1 | // from http://wiki.unity3d.com/index.php/OpenInFileBrowser 2 | 3 | 4 | public static class OpenInFileBrowser { 5 | public static bool IsInMacOS { 6 | get { 7 | return UnityEngine.SystemInfo.operatingSystem.IndexOf("Mac OS") != -1; 8 | } 9 | } 10 | 11 | public static bool IsInWinOS { 12 | get { 13 | return UnityEngine.SystemInfo.operatingSystem.IndexOf("Windows") != -1; 14 | } 15 | } 16 | 17 | public static void OpenInMac(string path) { 18 | bool openInsidesOfFolder = false; 19 | 20 | // try mac 21 | string macPath = path.Replace("\\", "/"); // mac finder doesn't like backward slashes 22 | 23 | if (System.IO.Directory.Exists(macPath)) { // if path requested is a folder, automatically open insides of that folder 24 | openInsidesOfFolder = true; 25 | } 26 | 27 | if (!macPath.StartsWith("\"")) { 28 | macPath = "\"" + macPath; 29 | } 30 | 31 | if (!macPath.EndsWith("\"")) { 32 | macPath = macPath + "\""; 33 | } 34 | 35 | string arguments = (openInsidesOfFolder? "" : "-R ") + macPath; 36 | 37 | try { 38 | System.Diagnostics.Process.Start("open", arguments); 39 | } catch (System.ComponentModel.Win32Exception e) { 40 | // tried to open mac finder in windows 41 | // just silently skip error 42 | // we currently have no platform define for the current OS we are in, so we resort to this 43 | e.HelpLink = ""; // do anything with this variable to silence warning about not using it 44 | } 45 | } 46 | 47 | public static void OpenInWin(string path) { 48 | bool openInsidesOfFolder = false; 49 | 50 | // try windows 51 | string winPath = path.Replace("/", "\\"); // windows explorer doesn't like forward slashes 52 | 53 | if (System.IO.Directory.Exists(winPath)) { // if path requested is a folder, automatically open insides of that folder 54 | openInsidesOfFolder = true; 55 | } 56 | 57 | try { 58 | System.Diagnostics.Process.Start("explorer.exe", (openInsidesOfFolder? "/root," : "/select,") + winPath); 59 | } catch (System.ComponentModel.Win32Exception e) { 60 | // tried to open win explorer in mac 61 | // just silently skip error 62 | // we currently have no platform define for the current OS we are in, so we resort to this 63 | e.HelpLink = ""; // do anything with this variable to silence warning about not using it 64 | } 65 | } 66 | 67 | public static void Open(string path) { 68 | if (IsInWinOS) { 69 | OpenInWin(path); 70 | } else if (IsInMacOS) { 71 | OpenInMac(path); 72 | } else { // couldn't determine OS 73 | OpenInWin(path); 74 | OpenInMac(path); 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /Packages/Reslution Selector UI.unitypackage: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/celechii/Unity-Tools/7b3b37322aabdd230a3539360a658d8e155622cf/Packages/Reslution Selector UI.unitypackage -------------------------------------------------------------------------------- /Packages/Simple Input Manager.unitypackage: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/celechii/Unity-Tools/7b3b37322aabdd230a3539360a658d8e155622cf/Packages/Simple Input Manager.unitypackage -------------------------------------------------------------------------------- /Parallax.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | public class Parallax : MonoBehaviour { 4 | 5 | [Range(0, 1)] 6 | public float influence; 7 | public bool reverse; 8 | public Vector2 offset; 9 | 10 | private Vector2 initPos; 11 | private Transform mainCam; 12 | 13 | private void Awake() { 14 | mainCam = Camera.main.transform; 15 | initPos = transform.localPosition; 16 | } 17 | 18 | private void LateUpdate() { 19 | transform.localPosition = Vector2.Lerp(initPos, ((Vector2)mainCam.transform.localPosition * (reverse? - 1 : 1)) + offset, influence); 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /PersistentScreenSize.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | public class PersistentScreenSize : MonoBehaviour { 4 | 5 | 6 | private Camera mainCamera; 7 | private float defaultSize; 8 | private float scale = 1; 9 | 10 | private void Awake() { 11 | mainCamera = Camera.main; 12 | defaultSize = mainCamera.orthographicSize; 13 | scale = transform.localScale.x; 14 | } 15 | 16 | private void LateUpdate() { 17 | transform.localScale = (mainCamera.orthographicSize / defaultSize) * new Vector3(scale, scale, 0); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Pixel Shader/PixelCamera.cs: -------------------------------------------------------------------------------- 1 | // based on code by Dmitry Timofeev 2 | // https://assetstore.unity.com/packages/vfx/shaders/fullscreen-camera-effects/pixelation-65554 3 | 4 | using UnityEngine; 5 | 6 | [ExecuteAlways] 7 | [RequireComponent(typeof(Camera))] 8 | public class PixelCamera : MonoBehaviour { 9 | 10 | public Shader shader; 11 | [Range(64.0f, 512.0f)] 12 | public float BlockCount = 240; 13 | 14 | private Material Material { 15 | get { 16 | if (material == null) { 17 | material = new Material(shader); 18 | material.hideFlags = HideFlags.HideAndDontSave; 19 | } 20 | return material; 21 | } 22 | } 23 | private Material material; 24 | 25 | private Camera mainCam; 26 | 27 | private void Awake() { 28 | mainCam = GetComponent(); 29 | } 30 | 31 | private void OnRenderImage(RenderTexture source, RenderTexture destination) { 32 | if (mainCam == null) 33 | mainCam = GetComponent(); 34 | 35 | Vector2 count = new Vector2(BlockCount, BlockCount / mainCam.aspect); 36 | Vector2 size = new Vector2(1f / count.x, 1f / count.y); 37 | 38 | Material.SetVector("BlockCount", count); 39 | Material.SetVector("BlockSize", size); 40 | Graphics.Blit(source, destination, Material); 41 | } 42 | 43 | private void OnDisable() { 44 | if (material) 45 | DestroyImmediate(material); 46 | } 47 | 48 | } -------------------------------------------------------------------------------- /Pixel Shader/Pixelation.shader: -------------------------------------------------------------------------------- 1 | // by Dmitry Timofeev 2 | // https://assetstore.unity.com/packages/vfx/shaders/fullscreen-camera-effects/pixelation-65554 3 | 4 | Shader "Hidden/Pixelation" 5 | { 6 | Properties 7 | { 8 | _MainTex ("Texture", 2D) = "white" {} 9 | } 10 | SubShader 11 | { 12 | Pass 13 | { 14 | CGPROGRAM 15 | #pragma vertex vert_img 16 | #pragma fragment frag 17 | 18 | #include "UnityCG.cginc" 19 | 20 | sampler2D _MainTex; 21 | float2 BlockCount; 22 | float2 BlockSize; 23 | 24 | fixed4 frag (v2f_img i) : SV_Target 25 | { 26 | float2 blockPos = floor(i.uv * BlockCount); 27 | float2 blockCenter = blockPos * BlockSize + BlockSize * 0.5; 28 | 29 | float4 tex = tex2D(_MainTex, blockCenter); 30 | return tex; 31 | } 32 | ENDCG 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /PronounSystem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | public static class PronounSystem { 5 | public static PronounData[] customPronouns = new PronounData[3]; 6 | private static PronounData theyThem = new PronounData("they/them/their/theirs/themself", true); 7 | private static PronounData sheHer = new PronounData("she/her/her/hers/herself", false); 8 | private static PronounData heHim = new PronounData("he/him/his/his/himself", false); 9 | private static PronounData zeZir = new PronounData("ze/zir/zir/zirs/zirself", false); 10 | private static PronounData zeHir = new PronounData("ze/hir/hir/hirs/hirself", false); 11 | private static PronounData itIt = new PronounData("it/it/it/its/itself", false); 12 | private static PronounData xeyXem = new PronounData("xey/xem/xyr/xyrs/xemself", true); 13 | private static PronounData eyEm = new PronounData("ey/em/eir/eirs/eirself", true); 14 | private static PronounData nonePronounsLeftBeef = new PronounData("_____"); 15 | 16 | public static string GetShortForm(this Pronoun pronouns) { 17 | if ((int)pronouns == 0) 18 | return "No pronouns"; 19 | if (Count(pronouns) <= 1) 20 | return $"{GetPronounData(pronouns).they}/{GetPronounData(pronouns).them}"; 21 | 22 | Pronoun[] pronounArray = GetPronounArray(pronouns); 23 | string compilarion = GetThey(pronounArray[0]); 24 | for (int i = 1; i < pronounArray.Length; i++) 25 | compilarion += "/" + GetThey(pronounArray[i]); 26 | return compilarion; 27 | } 28 | 29 | public static string GetThey(this Pronoun pronoun) => GetPronounData(pronoun).they; 30 | public static string GetThem(this Pronoun pronoun) => GetPronounData(pronoun).them; 31 | public static string GetTheir(this Pronoun pronoun) => GetPronounData(pronoun).their; 32 | public static string GetTheirs(this Pronoun pronoun) => GetPronounData(pronoun).theirs; 33 | public static string GetThemself(this Pronoun pronoun) => GetPronounData(pronoun).themself; 34 | public static string GetTheyre(this Pronoun pronoun) => GetPronounData(pronoun).they + (GetPronounData(pronoun).pluralize? "\'re": "\'s"); 35 | public static string GetTheyve(this Pronoun pronoun) => GetPronounData(pronoun).they + (GetPronounData(pronoun).pluralize? "\'ve": "\'s"); 36 | public static string GetTheyd(this Pronoun pronoun) => GetPronounData(pronoun).they + "\'d"; 37 | public static string GetTheyll(this Pronoun pronoun) => GetPronounData(pronoun).they + "\'ll"; 38 | public static string GetTheyHave(this Pronoun pronoun) => GetPronounData(pronoun).they + (GetPronounData(pronoun).pluralize? " have": "has"); 39 | 40 | public static Pronoun GetRandomPronoun(this Pronoun pronouns) { 41 | Pronoun[] avaliable = GetPronounArray(pronouns); 42 | if (avaliable.Length == 0) 43 | return (Pronoun)0; 44 | if (avaliable.Length == 1) 45 | return avaliable[0]; 46 | return avaliable[UnityEngine.Random.Range(0, avaliable.Length)]; 47 | } 48 | 49 | private static PronounData GetPronounData(Pronoun pronoun) { 50 | switch (pronoun) { 51 | case Pronoun.TheyThem: 52 | return theyThem; 53 | case Pronoun.SheHer: 54 | return sheHer; 55 | case Pronoun.HeHim: 56 | return heHim; 57 | case Pronoun.ZeZir: 58 | return zeZir; 59 | case Pronoun.ZeHir: 60 | return zeHir; 61 | case Pronoun.ItIts: 62 | return itIt; 63 | case Pronoun.XeyXem: 64 | return xeyXem; 65 | case Pronoun.EyEm: 66 | return eyEm; 67 | case Pronoun.Custom1: 68 | return customPronouns[0]; 69 | case Pronoun.Custom2: 70 | return customPronouns[1]; 71 | case Pronoun.Custom3: 72 | return customPronouns[2]; 73 | default: 74 | return nonePronounsLeftBeef; 75 | } 76 | } 77 | 78 | private static int Count(Pronoun pronoun) { 79 | int bits = System.Convert.ToInt32(pronoun); 80 | int count = 0; 81 | for (int i = 0; i < 32; i++) 82 | if ((bits & (1 << i)) > 0) 83 | count++; 84 | return count; 85 | } 86 | 87 | private static Pronoun[] GetPronounArray(Pronoun pronuons) { 88 | int enumSize = Enum.GetValues(typeof(Pronoun)).Length; 89 | List flags = new List(); 90 | 91 | Pronoun[] allFlags = (Pronoun[])Enum.GetValues(typeof(Pronoun)); 92 | for (int i = 0; i < allFlags.Length; i++) { 93 | if (pronuons.HasFlag((Pronoun)allFlags.GetValue(i))) 94 | flags.Add((Pronoun)allFlags.GetValue(i)); 95 | } 96 | return flags.ToArray(); 97 | } 98 | } 99 | 100 | [System.Flags] 101 | public enum Pronoun { 102 | TheyThem = 1 << 0, 103 | SheHer = 1 << 1, 104 | HeHim = 1 << 2, 105 | ZeZir = 1 << 3, 106 | ZeHir = 1 << 4, 107 | ItIts = 1 << 5, 108 | XeyXem = 1 << 6, 109 | EyEm = 1 << 7, 110 | Custom1 = 1 << 8, 111 | Custom2 = 1 << 9, 112 | Custom3 = 1 << 10 113 | } 114 | 115 | public class PronounData { 116 | public string they; 117 | public string them; 118 | public string their; 119 | public string theirs; 120 | public string themself; 121 | public bool pluralize; 122 | public bool noPronouns; 123 | public string name; 124 | 125 | // they put some bait on their line 126 | // the fish looked at them 127 | // they realized the fish wasnt theirs 128 | // they laughed to themself 129 | 130 | /// The name to use in place of any pronouns. 131 | public PronounData(string name) { 132 | noPronouns = true; 133 | pluralize = false; 134 | they = them = themself = this.name = name; 135 | their = theirs = name + "\'s"; 136 | } 137 | 138 | /// Subjective pronoun 139 | /// Objective pronoun 140 | /// Possessive adjective 141 | /// Possessive pronoun 142 | /// Reflecive pronoun 143 | /// Would it be "themself" or "themselves"? 144 | public PronounData(string they, string them, string their, string theirs, string themselves, bool pluralize) { 145 | this.they = they; 146 | this.them = them; 147 | this.their = their; 148 | this.theirs = theirs; 149 | this.themself = themselves; 150 | this.pluralize = pluralize; 151 | } 152 | 153 | /// A single string in the form of "they/them/their/theirs/themself". 154 | /// Would it be "themself" or "themselves"? 155 | public PronounData(string singleString, bool pluralize) { 156 | string[] segs = singleString.Split('/'); 157 | if (segs.Length != 5) 158 | throw new System.Exception("u dont have enough shit for the pronouns!!"); 159 | they = segs[0]; 160 | them = segs[1]; 161 | their = segs[2]; 162 | theirs = segs[3]; 163 | themself = segs[4]; 164 | this.pluralize = pluralize; 165 | } 166 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NOÉ'S UNITY TOOLS N SHIT 2 | 3 | hi! here's a list of the coolest scripts u can just take n use for w/e! 4 | 5 | [

Simple Input Manager

](https://github.com/celechii/Unity-Tools/blob/master/InputManager.cs) 6 | an input manager build upon unity's default `Input.GetKey()` methods n ur `KeyCode` enum values! supports rebinding! looks like this! 7 | 8 | ![inspector](https://i.imgur.com/Wzb8wC1.png) 9 | 10 | there's also a .unitypackage that has examples n comes w a simple ui u can slap into ur settings menu [RIGHT HERE](https://github.com/celechii/Unity-Tools/blob/master/Packages/Simple%20Input%20Manager.unitypackage)! 11 | 12 | ![simple input manager ui](https://i.imgur.com/4ALBAnv.png) 13 | 14 | --- 15 | 16 | [

Resolution UI Selector UnityPackage

](https://github.com/celechii/Unity-Tools/blob/master/Packages/Reslution%20Selector%20UI.unitypackage) 17 | a .unitypackage with some basic ui for previewing n setting screen resolutions! modfies the player pref values to save the last resolution/fullscreen setting 18 | 19 | [click pic to see it working!] 20 | [![ui preview](https://i.imgur.com/Niif9R8.png)](https://twitter.com/celechii/status/1261181340713725952) 21 | 22 | --- 23 | 24 | [

Screen Shake

](https://github.com/celechii/Unity-Tools/blob/master/ScreenShake.cs) 25 | a script to handle screen shaking, but can shake anything! can set a static instance so u dont have to keep finding it in the scene every time u need to shake the camera 26 | 27 | `Push()` will push the camera away from its offset 28 | 29 | ![screen shake inspector](https://i.imgur.com/xv4OtoU.png) 30 | -------------------------------------------------------------------------------- /Randomizer.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | public class Randomizer { 4 | 5 | public static int GetRandomIndex(float[] probabilities) => GetRandomIndex(probabilities, Random.value); 6 | 7 | public static int GetRandomIndex(float[] probabilities, float randomVal) { 8 | if (probabilities.Length == 0) 9 | throw new System.Exception("hey fucker wanna give me literally anything to choose from"); 10 | 11 | float scalar = 0; 12 | for (int i = 0; i < probabilities.Length; i++) 13 | scalar += probabilities[i]; 14 | 15 | for (int i = 0; i < probabilities.Length; i++) { 16 | if (randomVal <= probabilities[i] / scalar) 17 | return i; 18 | else 19 | randomVal -= probabilities[i] / scalar; 20 | } 21 | throw new System.Exception("oh uh fuck, i couldnt pick an index for some reason oops"); 22 | } 23 | } -------------------------------------------------------------------------------- /RangeFloat/Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4c48bb6cf6c2c48868e72424747b267e 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /RangeFloat/Editor/RangeFloatDrawer.cs: -------------------------------------------------------------------------------- 1 | using UnityEditor; 2 | using UnityEngine; 3 | 4 | [CustomPropertyDrawer(typeof(RangeFloat))] 5 | public class RangeFloatDrawer : PropertyDrawer { 6 | 7 | public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { 8 | 9 | EditorGUI.BeginProperty(position, label, property); 10 | 11 | position = EditorGUI.PrefixLabel(position, label); 12 | SerializedProperty minProp = property.FindPropertyRelative("min"); 13 | SerializedProperty maxProp = property.FindPropertyRelative("max"); 14 | 15 | EditorGUI.BeginChangeCheck(); 16 | float[] values = new float[2] { minProp.floatValue, maxProp.floatValue }; 17 | EditorGUI.MultiFloatField(position, new GUIContent[] { new GUIContent("Min"), new GUIContent("Max") }, values); 18 | 19 | if (EditorGUI.EndChangeCheck()) { 20 | minProp.floatValue = values[0]; 21 | maxProp.floatValue = values[1]; 22 | } 23 | 24 | EditorGUI.EndProperty(); 25 | } 26 | 27 | public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { 28 | return EditorGUIUtility.singleLineHeight; 29 | } 30 | } -------------------------------------------------------------------------------- /RangeFloat/Editor/RangeFloatDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 651fac3ee97a849e082f6b9bb61635f3 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /RangeFloat/RangeFloat.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | [System.Serializable] 4 | public class RangeFloat { 5 | [SerializeField] 6 | public float min, max; 7 | private float value; 8 | 9 | public float Value { get { return value; } set { this.value = Mathf.Clamp(value, min, max); } } 10 | 11 | public RangeFloat() { 12 | min = 0; 13 | max = 1; 14 | } 15 | 16 | public RangeFloat(float min, float max) { 17 | this.min = min; 18 | this.max = max; 19 | } 20 | 21 | public RangeFloat(float min, float max, float value) { 22 | this.min = min; 23 | this.max = max; 24 | this.value = value; 25 | } 26 | 27 | /// 28 | /// Clamps the value between the min and max. 29 | /// 30 | public void ClampValue() { 31 | value = Mathf.Clamp(value, min, max); 32 | } 33 | 34 | /// 35 | /// Adds or subtracts from the value. 36 | /// 37 | private void AddToValue(float amount) { 38 | value += amount; 39 | } 40 | 41 | /// 42 | /// Adds or subtracts from the value. 43 | /// 44 | private void AddToValue(int amount) { 45 | value += amount; 46 | } 47 | 48 | /// 49 | /// Sets the value to the max and returns it. 50 | /// 51 | public float ToMax() { 52 | value = max; 53 | return max; 54 | } 55 | 56 | /// 57 | /// Sets the value to the min and returns it. 58 | /// 59 | public float ToMin() { 60 | value = min; 61 | return min; 62 | } 63 | 64 | /// 65 | /// Sets a random float value between the min and the max (inclusive). 66 | /// 67 | public void SetValueToRandomFloat() { 68 | value = GetRandomFloat(); 69 | } 70 | 71 | /// 72 | /// Sets a random int value between the min and the max (inclusive). 73 | /// 74 | public void SetValueToRandomInt() { 75 | value = (float)GetRandomInt(); 76 | } 77 | 78 | /// 79 | /// Sets the value to a percent lerp between the min and max. 80 | /// 81 | public void SetValueToPercent(float percent) { 82 | value = GetAt(percent); 83 | } 84 | 85 | /// 86 | /// Sets the value to an unclamped percent lerp between the min and max. 87 | /// 88 | public void SetValueToUnclampedPercent(float percent) { 89 | value = GetUnclampedAt(percent); 90 | } 91 | 92 | /// 93 | /// Returns the min and max range. 94 | /// 95 | public float GetRange() { 96 | return max - min; 97 | } 98 | 99 | /// 100 | /// Returns a value t% between the min and max. 101 | /// 102 | public float GetAt(float t) { 103 | return Mathf.Lerp(min, max, t); 104 | } 105 | 106 | /// 107 | /// Returns a value t% between the min and max according to an animation curve. 108 | /// 109 | public float GetAtWith(float t, AnimationCurve curve) { 110 | return GetAt(curve.Evaluate(t)); 111 | } 112 | 113 | /// 114 | /// Returns a value t% between the min and max, but also maybe outside of them, cause unclamped and stuff. 115 | /// 116 | public float GetUnclampedAt(float t) { 117 | return Mathf.LerpUnclamped(min, max, t); 118 | } 119 | 120 | /// 121 | /// Returns a value t% between the min and max, but also maybe outside of them, cause unclamped and stuff. 122 | /// 123 | public float GetUnclampedAtWith(float t, AnimationCurve curve) { 124 | return curve.Evaluate(GetUnclampedAt(t)); 125 | } 126 | 127 | /// 128 | /// Returns how far into the range a value is. 129 | /// 130 | public float GetPercent(float value) { 131 | return Mathf.InverseLerp(min, max, value); 132 | } 133 | 134 | /// 135 | /// Returns how far into the range a value is, then scales it according to an animation curve. 136 | /// 137 | public float GetPercentWith(float value, AnimationCurve curve) { 138 | return curve.Evaluate(GetPercent(value)); 139 | } 140 | 141 | /// 142 | /// Returns how far in % the value is between the min and max. 143 | /// 144 | public float GetPercentOfValue() { 145 | return GetPercent(value); 146 | } 147 | 148 | /// 149 | /// Returns how far in % the value is between the min and max, scaling with an animation curve. 150 | /// 151 | public float GetPercentOfValueWith(AnimationCurve curve) { 152 | return curve.Evaluate(GetPercent(value)); 153 | } 154 | 155 | /// 156 | /// Returns either min or the max, at 50/50 chance each. 157 | /// 158 | public float GetExtreme() { 159 | return GetExtreme(.5f); 160 | } 161 | 162 | /// 163 | /// Returns either the min or the max, given the chance distribution. 164 | /// 0 will give the min, 1 will give the max. 165 | /// 166 | public float GetExtreme(float distribution) { 167 | distribution = Mathf.Clamp01(distribution); 168 | return Random.value > distribution ? max : min; 169 | } 170 | 171 | /// 172 | /// Determines whether the value passed is in range the specified min and max (inclusive). 173 | /// 174 | public bool IsInRange(float value) { 175 | return value >= min && value <= max; 176 | } 177 | 178 | /// 179 | /// Returns a random float between the min and max (inclusive). 180 | /// 181 | public float GetRandomFloat() { 182 | return Mathf.Lerp(min, max, Random.value); 183 | } 184 | 185 | /// 186 | /// Returns a random int between the min and max (inclusive). 187 | /// Should the min or max be real numbers, this may return the number either above the max or below the min. 188 | /// 189 | public int GetRandomInt() { 190 | return Mathf.RoundToInt(GetRandomFloat()); 191 | } 192 | 193 | /// 194 | /// Retuns a Vector2 with x and y components at random positions between the min and max. 195 | /// 196 | public Vector2 GetRandomV2() { 197 | return new Vector2(GetRandomFloat(), GetRandomFloat()); 198 | } 199 | 200 | /// 201 | /// Retuns a Vector3 with x, y, and z components at random positions between the min and max. 202 | /// 203 | public Vector3 GetRandomV3() { 204 | return new Vector3(GetRandomFloat(), GetRandomFloat(), GetRandomFloat()); 205 | } 206 | 207 | /// 208 | /// Returns a Vector2 with a random direction, and a magnitude in between the min and max (inclusive). 209 | /// 210 | public Vector2 GetRandomDistance() { 211 | return (new Vector2(Random.value * 2 - 1, Random.value * 2 - 1)) * GetRandomFloat(); 212 | } 213 | 214 | public static RangeFloat operator +(RangeFloat r1, float val) { 215 | r1.AddToValue(val); 216 | return r1; 217 | } 218 | 219 | public static RangeFloat operator +(RangeFloat r1, int val) { 220 | r1.AddToValue(val); 221 | return r1; 222 | } 223 | 224 | public static RangeFloat operator +(RangeFloat r1, RangeFloat r2) { 225 | r1.AddToValue(r2.Value); 226 | return r1; 227 | } 228 | 229 | public static RangeFloat operator -(RangeFloat r1, float val) { 230 | r1.AddToValue(-val); 231 | return r1; 232 | } 233 | 234 | public static RangeFloat operator -(RangeFloat r1, int val) { 235 | r1.AddToValue(-val); 236 | return r1; 237 | } 238 | 239 | public static RangeFloat operator -(RangeFloat r1, RangeFloat r2) { 240 | r1.AddToValue(-r2.Value); 241 | return r1; 242 | } 243 | 244 | public static RangeFloat operator *(RangeFloat r1, float val) { 245 | r1.Value = r1.Value * val; 246 | return r1; 247 | } 248 | 249 | public static RangeFloat operator *(RangeFloat r1, int val) { 250 | r1.Value = r1.Value * val; 251 | return r1; 252 | } 253 | 254 | public static RangeFloat operator *(RangeFloat r1, RangeFloat r2) { 255 | r1.Value = r1.Value * r2.Value; 256 | return r1; 257 | } 258 | 259 | public static RangeFloat operator /(RangeFloat r1, float val) { 260 | r1.Value = r1.Value / val; 261 | return r1; 262 | } 263 | 264 | public static RangeFloat operator /(RangeFloat r1, int val) { 265 | r1.Value = r1.Value / val; 266 | return r1; 267 | } 268 | 269 | public static RangeFloat operator /(RangeFloat r1, RangeFloat r2) { 270 | r1.Value = r1.Value / r2.Value; 271 | return r1; 272 | } 273 | 274 | public override string ToString() => $"{min} -> {max} @ {value} ({(int)(GetPercentOfValue()*100)}%)"; 275 | } -------------------------------------------------------------------------------- /RndVal.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | public class RndVal { 4 | 5 | private int[] list; 6 | private int offset; 7 | private int shuffleCount; 8 | private int totalCount; 9 | 10 | private int lastIndex; 11 | public int LastValue { 12 | get => lastIndex + offset; 13 | } 14 | 15 | public RndVal(int min, int max, int shuffleCount) { 16 | 17 | if (min > max) 18 | (min, max) = (max, min); 19 | else if (min == max) 20 | throw new System.ArgumentOutOfRangeException(); 21 | 22 | this.shuffleCount = shuffleCount; 23 | 24 | offset = min; 25 | list = new int[Mathf.Abs(max - min)]; 26 | 27 | Next(); 28 | } 29 | 30 | public int Next() { 31 | if (totalCount == shuffleCount * list.Length) 32 | ResetList(); 33 | 34 | int index; 35 | do { 36 | index = Mathf.Clamp(Mathf.RoundToInt(Random.value * list.Length - 1), 0, list.Length - 1); 37 | } while (list[index] == shuffleCount || index == lastIndex); 38 | totalCount++; 39 | list[index]++; 40 | lastIndex = index; 41 | 42 | return index + offset; 43 | } 44 | 45 | private void ResetList() { 46 | totalCount = 0; 47 | for (int i = 0; i < list.Length; i++) 48 | list[i] = 0; 49 | } 50 | 51 | public static implicit operator int(RndVal rnd) => rnd.LastValue; 52 | } -------------------------------------------------------------------------------- /SORefer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEngine; 3 | 4 | public class SORefer : MonoBehaviour { 5 | 6 | private static SORefer control; 7 | 8 | [SerializeField] 9 | private ScriptableObject[] allObjects; 10 | 11 | private Dictionary objects; 12 | 13 | private void Awake() { 14 | control = this; 15 | 16 | #if UNITY_EDITOR 17 | UpdateList(); 18 | #endif 19 | 20 | BuildDictionary(); 21 | } 22 | 23 | #if UNITY_EDITOR 24 | [ContextMenu("Update List")] 25 | private void UpdateList() { 26 | allObjects = null; 27 | string[] guids = UnityEditor.AssetDatabase.FindAssets($"t:ScriptableObject", new string[] { "Assets/Scriptable Objects" }); 28 | allObjects = new ScriptableObject[guids.Length]; 29 | for (int i = 0; i < guids.Length; i++) 30 | allObjects[i] = UnityEditor.AssetDatabase.LoadAssetAtPath(UnityEditor.AssetDatabase.GUIDToAssetPath(guids[i])); 31 | } 32 | 33 | [UnityEditor.MenuItem("NOÉ'S FUCK SHIT/Update SORefer")] 34 | private static void UpdateSORefer() => GameObject.FindObjectOfType().UpdateList(); 35 | 36 | #endif 37 | 38 | public static int GetCode(T scriptableObject)where T : ScriptableObject { 39 | return HashFromNameAndType(scriptableObject.name, typeof(T)); 40 | } 41 | 42 | public static int[] GetCodes(T[] scriptableObjects)where T : ScriptableObject { 43 | if (scriptableObjects == null) 44 | return null; 45 | int[] results = new int[scriptableObjects.Length]; 46 | for (int i = 0; i < results.Length; i++) 47 | results[i] = GetCode(scriptableObjects[i]); 48 | return results; 49 | } 50 | 51 | public static T GetObject(int code)where T : ScriptableObject { 52 | return (T)control.objects[code]; 53 | } 54 | 55 | public static T[] GetObjects(int[] codes)where T : ScriptableObject { 56 | if (codes == null) 57 | return null; 58 | T[] results = new T[codes.Length]; 59 | for (int i = 0; i < results.Length; i++) 60 | results[i] = GetObject(codes[i]); 61 | return results; 62 | } 63 | 64 | private void BuildDictionary() { 65 | objects = new Dictionary(); 66 | foreach (ScriptableObject so in allObjects) 67 | objects.Add(HashFromNameAndType(so.name, so.GetType()), so); 68 | } 69 | 70 | private static int HashFromNameAndType(string name, System.Type type) { 71 | return (name + type.ToString()).GetHashCode(); 72 | } 73 | } -------------------------------------------------------------------------------- /SaveSystem.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics; 3 | using System.IO; 4 | using UnityEngine; 5 | 6 | public static class SaveSystem { 7 | 8 | /* 9 | 10 | HEY FUTURE NOÉ READ THIS BEFORE U TRY TO ADD MORE SHIT TO THE SAVE FILE 11 | idk just some things to remember before u stress urself out trying to find why smth's not working 12 | 13 | - WorldControl calls Load on Start() n Save() on OnDestroy() 14 | - shit w Save/Load methods do the SaveSystem.savables.Add(this); BEFORE WorldControl calls Start() 15 | - the ICanBeSaved interface is for reading from n writing to SaveSystem.Data!!! 16 | - load is only called when there's a loadable save file so remember to set defaults!! 17 | - i love u 18 | 19 | adding new shit?? here r ur steps: 20 | 1. add the data to b saved in the SaveData class 21 | 2. add n implement the ICanBeSaved interface on the class u want to do the saving 22 | 3. in the Load() of the ICanBeSaved, pull the data from SaveSystem.Data, sort it out 23 | 4. in the Save() of the ICanBeSaved, compile the data into the structure u wanna save in then set it in SaveSystem.Data 24 | 5. remember to account for when load DOESNT get called (when there is no save data) 25 | 6. smile cause u fucked this up a lot but not this time :) 26 | 27 | */ 28 | 29 | public static int currentSaveSlot = 1; 30 | private static string path; 31 | private static string saveFileName = $"Save {currentSaveSlot}"; 32 | 33 | private static SaveData data; 34 | public static SaveData Data { 35 | get { 36 | if (data == null) 37 | Load(); 38 | return data; 39 | } 40 | } 41 | 42 | public static List savables = new List(); 43 | 44 | private static void SetPath() { 45 | path = Path.Combine(Application.persistentDataPath, "Saves"); 46 | if (!Directory.Exists(path)) 47 | Directory.CreateDirectory(path); 48 | } 49 | 50 | #if UNITY_EDITOR 51 | [UnityEditor.MenuItem("Save Shit/Open Save Location")] 52 | private static void OpenSavePathInFinder() { 53 | SetPath(); 54 | OpenInFileBrowser.Open(path); 55 | } 56 | #endif 57 | 58 | /// 59 | /// Checks if the save exists. 60 | /// 61 | /// The file name, INCLUDING the extension, even for txts. 62 | /// Include '#' to be replaced with the current save slot. 63 | public static bool SaveExists(string fileName) { 64 | fileName = fileName.Replace("#", currentSaveSlot.ToString()); 65 | SetPath(); 66 | return File.Exists(Path.Combine(path, fileName)); 67 | } 68 | 69 | /// 70 | /// Saves to text file in the resources folder. 71 | /// 72 | /// File name, not including the extension. 73 | /// Include '#' to be replaced with the current save slot. 74 | /// JSON data string. 75 | public static void SaveTxt(string fileName, object data) { 76 | fileName = fileName.Replace("#", currentSaveSlot.ToString()); 77 | SetPath(); 78 | string stringData = JsonUtility.ToJson(data, true); 79 | StreamWriter writer = new StreamWriter(Path.Combine(path, fileName + ".txt")); 80 | writer.Write(stringData); 81 | writer.Close(); 82 | } 83 | 84 | /// 85 | /// Loads a text from text file. 86 | /// 87 | /// The from text file. 88 | /// File name excluding the extention. 89 | /// Include '#' to be replaced with the current save slot. 90 | public static T LoadTxt(string fileName) { 91 | fileName = fileName.Replace("#", currentSaveSlot.ToString()); 92 | SetPath(); 93 | StreamReader reader = new StreamReader(Path.Combine(path, fileName + ".txt")); 94 | string stringData = reader.ReadToEnd(); 95 | reader.Close(); 96 | return JsonUtility.FromJson(stringData); 97 | } 98 | 99 | #if UNITY_EDITOR 100 | [UnityEditor.MenuItem("Save Shit/Open/Current Save Game")] 101 | private static void OpenSave() => OpenFile("Save #.txt"); 102 | 103 | public static void OpenFile(string fileName) { 104 | fileName = fileName.Replace("#", currentSaveSlot.ToString()); 105 | SetPath(); 106 | string fullPath = Path.Combine(path, fileName); 107 | if (File.Exists(fullPath)) 108 | Process.Start(fullPath); 109 | else 110 | throw new System.Exception($"wtf {fullPath} isnt a thing???"); 111 | } 112 | #endif 113 | 114 | #if UNITY_EDITOR 115 | [UnityEditor.MenuItem("Save Shit/Load/Game Save")] 116 | #endif 117 | public static void Load() { 118 | bool validSaveFound = false; 119 | 120 | if (SaveExists(saveFileName + ".txt")) { 121 | data = LoadTxt(saveFileName); 122 | if (data == null) 123 | data = new SaveData(); 124 | else 125 | validSaveFound = true; 126 | } else 127 | data = new SaveData(); 128 | 129 | foreach (ICanBeSaved s in savables) { 130 | if (validSaveFound) 131 | s.Load(); 132 | else 133 | s.Initialize(); 134 | } 135 | } 136 | 137 | #if UNITY_EDITOR 138 | [UnityEditor.MenuItem("Save Shit/Save/Game Save")] 139 | #endif 140 | public static void Save() { 141 | foreach (ICanBeSaved s in savables) 142 | s.Save(); 143 | SaveTxt(saveFileName, data); 144 | } 145 | 146 | #if UNITY_EDITOR 147 | [UnityEditor.MenuItem("Save Shit/DANGER/DELETE ALL SAVE FILES")] 148 | private static void DeleteAllSaveFiles() { 149 | SetPath(); 150 | foreach (string s in Directory.GetFiles(path)) { 151 | File.Delete(s); 152 | } 153 | } 154 | #endif 155 | 156 | /// 157 | /// Gets the names of the files saved in the save folder. 158 | /// 159 | public static string[] GetSaveNames() { 160 | SetPath(); 161 | string[] files = Directory.GetFiles(path); 162 | for (int i = 0; i < files.Length; i++) 163 | files[i] = Path.GetFileNameWithoutExtension(files[i]); 164 | return files; 165 | } 166 | 167 | [System.Serializable] 168 | public class SaveData { 169 | 170 | } 171 | } 172 | 173 | public interface ICanBeSaved { 174 | void Save(); 175 | void Load(); 176 | void Initialize(); 177 | } -------------------------------------------------------------------------------- /SceneSwitcher.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEngine.SceneManagement; 3 | 4 | public class SceneSwitcher : MonoBehaviour { 5 | 6 | [Header("only need to fill in 1 of these!")] 7 | public string sceneName; 8 | public int sceneBuildIndex; 9 | 10 | public void SwitchScene() { 11 | if (sceneName != "") 12 | SwitchScene(sceneName); 13 | else 14 | SwitchScene(sceneBuildIndex); 15 | } 16 | 17 | public void SwitchScene(string name) { 18 | SceneManager.LoadScene(name); 19 | } 20 | 21 | public void SwitchScene(int sceneNum) { 22 | SceneManager.LoadScene(sceneNum); 23 | } 24 | } -------------------------------------------------------------------------------- /ScreenResolutionSetter.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEngine.Events; 3 | 4 | public class ScreenResolutionSetter : MonoBehaviour { 5 | 6 | public bool setPrefOnLoad; 7 | public int resolutionsFromMax; 8 | public Vector2Int targetWindowAspect; 9 | public Vector2Int targetResolution; 10 | public bool fToToggleFullscreen = true; 11 | public UnityEvent OnToggleFullscreen; 12 | 13 | private bool fullscreen; 14 | 15 | private void Awake() { 16 | 17 | fullscreen = PlayerPrefs.GetInt("Screenmanager Fullscreen Mode") == 1; 18 | 19 | if (setPrefOnLoad) 20 | SetResolution(); 21 | } 22 | 23 | private void Update() { 24 | if (fToToggleFullscreen && Input.GetKeyDown(KeyCode.F)) { 25 | fullscreen = !fullscreen; 26 | SetResolution(); 27 | OnToggleFullscreen.Invoke(); 28 | } 29 | } 30 | 31 | public void SetResolution() { 32 | if (resolutionsFromMax == Screen.resolutions.Length - 1) 33 | return; 34 | 35 | Resolution targetRes = Screen.resolutions[Screen.resolutions.Length - (fullscreen?0 : resolutionsFromMax) - 1]; 36 | Vector2Int size = new Vector2Int(targetRes.width, targetRes.height); 37 | if (!fullscreen) { 38 | float aspect; 39 | if (targetResolution == Vector2Int.zero && targetWindowAspect != Vector2Int.zero) 40 | aspect = (float)targetWindowAspect.y / targetWindowAspect.x; 41 | else 42 | aspect = (float)targetResolution.y / targetResolution.x; 43 | size.x = Mathf.CeilToInt((float)size.y / aspect); 44 | 45 | if (size.y + 5 > Screen.height) { 46 | resolutionsFromMax++; 47 | SetResolution(); 48 | return; 49 | } 50 | } 51 | Screen.SetResolution(size.x, size.y, fullscreen?FullScreenMode.FullScreenWindow : FullScreenMode.Windowed); 52 | 53 | PlayerPrefs.SetInt("Screenmanager Resolution Width", size.x); 54 | PlayerPrefs.SetInt("Screenmanager Resolution Height", size.y); 55 | PlayerPrefs.SetInt("Screenmanager Fullscreen Mode", fullscreen?1 : 0); 56 | PlayerPrefs.Save(); 57 | } 58 | } -------------------------------------------------------------------------------- /ScreenShake.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using UnityEngine; 3 | 4 | public class ScreenShake : MonoBehaviour { 5 | 6 | public static ScreenShake staticRef; 7 | 8 | [HideInInspector] public bool enable = true; // makes the shake n push methods do nothin 9 | [HideInInspector] public bool isPaused = false; // pause the shit 10 | 11 | public Vector3 Output { get { return output + offset; } } 12 | 13 | [Header("//SETTING SHIT")] 14 | [Tooltip("should this b the instance u can reference statically?")] 15 | [SerializeField] private bool isStaticReference = false; 16 | [Tooltip("recentering behaviour")] 17 | [SerializeField] private SmoothType smoothType = SmoothType.Smooth; 18 | [Tooltip("what to shake n push")] 19 | [SerializeField] private OutputType outputType = OutputType.Self; 20 | [Space] 21 | [Tooltip("its the time... between shakes")] 22 | [SerializeField] private float timeBetweenShakes = .1f; 23 | [Tooltip("transform to set if Output Type is set to another transform")] 24 | [SerializeField] private Transform transformTarget; 25 | [Space] 26 | [Tooltip("n start should it set the offset to the targets position?")] 27 | [SerializeField] private bool setOffsetToStartPos = true; 28 | [Tooltip("how much to offset when reading the output")] 29 | [SerializeField] private Vector3 offset; 30 | [Header("//SMOOTH SHIT")] 31 | [Tooltip("how soon the shake recenters")] 32 | [SerializeField] private float recenterTime = .1f; 33 | [Header("//SPRING SHIT")] 34 | [Tooltip("how much to multiply spring force in")] 35 | [SerializeField] private float forceMultiplier = 69; 36 | [Tooltip("how loosy goosy the spring is")] 37 | [SerializeField] private float spring = 16; 38 | [Range(0, 1)] 39 | [Tooltip("how quickly the spring chills")] 40 | [SerializeField] private float damper = .35f; 41 | 42 | [HideInInspector] 43 | public Vector3 output; 44 | 45 | private Vector3 velRef; 46 | private Vector3 springVel; 47 | 48 | private void Awake() { 49 | if (isStaticReference) { 50 | if (staticRef != null) { 51 | Debug.LogError($"hey jsyk {name} is set to b the static reference when {staticRef.name} already is so im not gonna set it"); 52 | isStaticReference = false; 53 | } else 54 | staticRef = this; 55 | } 56 | } 57 | 58 | private void Start() { 59 | if (setOffsetToStartPos && outputType != OutputType.None) { 60 | if (outputType == OutputType.Self) 61 | offset = transform.localPosition; 62 | else if (outputType == OutputType.OtherTransformLocal) 63 | offset = transformTarget.localPosition; 64 | else if (outputType == OutputType.OtherTransformWorld) 65 | offset = transformTarget.position; 66 | } 67 | } 68 | 69 | private void Update() { 70 | 71 | if (isPaused) 72 | return; 73 | 74 | // smooth shit 75 | if (smoothType == SmoothType.Smooth) { 76 | output = Vector3.SmoothDamp(output, Vector3.zero, ref velRef, recenterTime); 77 | 78 | } else if (smoothType == SmoothType.Spring) { 79 | springVel += (-output) * spring - (springVel * damper); 80 | output += (Vector3)springVel * Time.deltaTime; 81 | } 82 | 83 | // clamping shit 84 | float minValue = .01f; 85 | if (output.magnitude < minValue) { 86 | if (smoothType == SmoothType.Smooth && velRef.magnitude < minValue) 87 | output = velRef = Vector3.zero; 88 | else if (smoothType == SmoothType.Spring && springVel.magnitude < minValue) 89 | output = springVel = Vector3.zero; 90 | } 91 | 92 | // output shit 93 | if (outputType == OutputType.None) 94 | return; 95 | 96 | if (outputType == OutputType.Self) 97 | transform.localPosition = Output; 98 | else if (outputType == OutputType.OtherTransformLocal) 99 | transformTarget.localPosition = Output; 100 | else if (outputType == OutputType.OtherTransformLocal) 101 | transformTarget.position = Output; 102 | } 103 | 104 | /// 105 | /// Pushes the target in a direction. 106 | /// 107 | /// The direction of the push. 108 | /// The magnitude of the push 109 | public void Push(Vector2 direction, float amount) => Push(direction * amount); 110 | 111 | /// 112 | /// Pushes the target in a direction. 113 | /// 114 | /// The vector force of the push direction. 115 | public void Push(Vector2 force) { 116 | if (!enable) 117 | return; 118 | if (smoothType == SmoothType.Spring) 119 | springVel += (Vector3)force * forceMultiplier; 120 | else 121 | output += (Vector3)force; 122 | } 123 | 124 | /// 125 | /// Shakes the target some amount of times. 126 | /// 127 | /// How intense the shake is. For a SmoothType of Smooth this is how far off center it's pushed. 128 | /// The number of times to shake. 129 | /// Should this ignore Time.timeScale? 130 | public void Shake(float intensity, int numShakes, bool realtime = false) { 131 | StartCoroutine(DoShake(intensity, numShakes, realtime)); 132 | } 133 | 134 | private IEnumerator DoShake(float intensity, int numShakes, bool realtime = false) { 135 | if (!enable) 136 | yield break; 137 | for (int i = 0; i < numShakes; i++) { 138 | Vector2 dir = new Vector2(Random.Range(-1f, 1f), Random.Range(-1f, 1f)).normalized; 139 | Push(dir * intensity); 140 | 141 | yield return StartCoroutine(PauseableWaitForSeconds(timeBetweenShakes, realtime)); 142 | } 143 | } 144 | 145 | private IEnumerator PauseableWaitForSeconds(float duration, bool realtime = false) { 146 | for (float elapsed = 0; elapsed < duration; elapsed += (realtime?Time.unscaledDeltaTime : Time.deltaTime)) { 147 | while (isPaused) 148 | yield return null; 149 | yield return null; 150 | } 151 | } 152 | 153 | public enum SmoothType { 154 | Smooth, 155 | Spring 156 | } 157 | 158 | public enum OutputType { 159 | None, 160 | Self, 161 | OtherTransformLocal, 162 | OtherTransformWorld 163 | } 164 | } -------------------------------------------------------------------------------- /Scriptable State System/Editor/state_callback_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/celechii/Unity-Tools/7b3b37322aabdd230a3539360a658d8e155622cf/Scriptable State System/Editor/state_callback_icon.png -------------------------------------------------------------------------------- /Scriptable State System/Editor/state_nav_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/celechii/Unity-Tools/7b3b37322aabdd230a3539360a658d8e155622cf/Scriptable State System/Editor/state_nav_icon.png -------------------------------------------------------------------------------- /Scriptable State System/Editor/state_object_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/celechii/Unity-Tools/7b3b37322aabdd230a3539360a658d8e155622cf/Scriptable State System/Editor/state_object_icon.png -------------------------------------------------------------------------------- /Scriptable State System/State.cs: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | Copyright (c) 2022 Noé Charron 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 15 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 16 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 17 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 18 | SOFTWARE. 19 | */ 20 | 21 | using System; 22 | using System.Collections.Generic; 23 | using UnityEngine; 24 | #if UNITY_EDITOR 25 | using UnityEditor; 26 | #endif 27 | 28 | [CreateAssetMenu(menuName = "States/New State")] 29 | public class State : ScriptableObject { 30 | 31 | [Tooltip("States that this state can transition to.")] 32 | public List childStates = new List(); 33 | 34 | public event Action Entered; 35 | public event Action Left; 36 | public event Action LeftToParent; 37 | public event Action LeftToSibling; 38 | public event Action LeftToChild; 39 | 40 | public void RaiseEnterEvent() { 41 | OnEnterEventRaised(); 42 | Entered?.Invoke(); 43 | } 44 | 45 | public void RaiseLeftToParentEvent() { 46 | OnLeftToParentEventRaised(); 47 | LeftToParent?.Invoke(); 48 | 49 | OnLeftEventRaised(); 50 | Left?.Invoke(); 51 | } 52 | 53 | public void RaiseLeftToSiblingEvent() { 54 | OnLeftToSiblingEventRaised(); 55 | LeftToSibling?.Invoke(); 56 | 57 | OnLeftEventRaised(); 58 | Left?.Invoke(); 59 | } 60 | 61 | public void RaiseLeftToChildEvent() { 62 | OnLeftToChildEventRaised(); 63 | LeftToChild?.Invoke(); 64 | 65 | OnLeftEventRaised(); 66 | Left?.Invoke(); 67 | } 68 | 69 | protected virtual void OnEnterEventRaised() {} 70 | protected virtual void OnLeftEventRaised() {} 71 | protected virtual void OnLeftToParentEventRaised() {} 72 | protected virtual void OnLeftToSiblingEventRaised() {} 73 | protected virtual void OnLeftToChildEventRaised() {} 74 | 75 | public void PrintEventSubscribers() { 76 | System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder(); 77 | stringBuilder.Append($"{name} State Events:\n"); 78 | Delegate[] subscribers = Entered.GetInvocationList(); 79 | AddSubscribers("Enter state", Entered); 80 | AddSubscribers("Exit state", Left); 81 | 82 | Debug.Log(stringBuilder.ToString()); 83 | 84 | void AddSubscribers(string title, Action action) { 85 | stringBuilder.Append($"{title}: ["); 86 | for (int iSub = 0; iSub < subscribers.Length; ++iSub) { 87 | stringBuilder.Append(subscribers[iSub] + (iSub == subscribers.Length - 1 ? "]\n" : ", ")); 88 | } 89 | } 90 | } 91 | 92 | #if UNITY_EDITOR 93 | 94 | [ContextMenu("Create New Child State")] 95 | private void CreateNewChildState() { 96 | State newState = ScriptableObject.CreateInstance(); 97 | string path = AssetDatabase.GetAssetPath(GetInstanceID()); 98 | path = AssetDatabase.GenerateUniqueAssetPath(path.Substring(0, path.LastIndexOf('/') + 1) + "New Game State.asset"); 99 | AssetDatabase.CreateAsset(newState, path); 100 | AssetDatabase.SaveAssets(); 101 | 102 | EditorUtility.FocusProjectWindow(); 103 | Selection.activeObject = newState; 104 | 105 | for (int i = 0; i < childStates.Count; i++) { 106 | if (childStates[i] == null) { 107 | childStates[i] = newState; 108 | return; 109 | } 110 | } 111 | childStates.Add(newState); 112 | } 113 | 114 | #endif 115 | } -------------------------------------------------------------------------------- /Scriptable State System/StateChangeCallbacks.cs: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | Copyright (c) 2022 Noé Charron 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 15 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 16 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 17 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 18 | SOFTWARE. 19 | */ 20 | 21 | using System.Text; 22 | using UnityEngine; 23 | using UnityEngine.Events; 24 | 25 | public class StateChangeCallbacks : MonoBehaviour { 26 | 27 | [SerializeField] 28 | private StateCallback[] callbacks; 29 | 30 | private void Awake() { 31 | for (int i = 0; i < callbacks.Length; i++) 32 | callbacks[i].RegisterEvents(); 33 | } 34 | 35 | private void OnDestroy() { 36 | for (int i = 0; i < callbacks.Length; i++) 37 | callbacks[i].UnregisterEvents(); 38 | } 39 | 40 | [System.Serializable] 41 | private struct StateCallback : ISerializationCallbackReceiver { 42 | 43 | [HideInInspector] 44 | [SerializeField] 45 | private string name; 46 | 47 | public State state; 48 | 49 | public UnityEvent onEntered; 50 | 51 | public UnityEvent onLeft; 52 | 53 | public void RegisterEvents() { 54 | state.Entered += InvokeEntered; 55 | state.Left += InvokeLeft; 56 | } 57 | 58 | public void UnregisterEvents() { 59 | state.Entered -= InvokeEntered; 60 | state.Left -= InvokeLeft; 61 | } 62 | 63 | private void InvokeEntered() => onEntered.Invoke(); 64 | 65 | private void InvokeLeft() => onLeft.Invoke(); 66 | 67 | public void OnBeforeSerialize() { 68 | string name = state == null ? "Nothing" : state.name; 69 | int enterCount = onEntered.GetPersistentEventCount(); 70 | int exitCount = onLeft.GetPersistentEventCount(); 71 | 72 | if (enterCount + exitCount > 0) { 73 | StringBuilder sb = new StringBuilder("On "); 74 | 75 | if (enterCount > 0 && exitCount > 0) 76 | sb.Append("Enter/Exit "); 77 | else if (enterCount > 0) 78 | sb.Append("Enter "); 79 | else if (exitCount > 0) 80 | sb.Append("Exit "); 81 | sb.Append(state.name); 82 | 83 | this.name = sb.ToString(); 84 | } 85 | 86 | this.name = name; 87 | } 88 | 89 | public void OnAfterDeserialize() { 90 | // do nothing 91 | } 92 | } 93 | 94 | } -------------------------------------------------------------------------------- /Scriptable State System/StateNav.cs: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | Copyright (c) 2022 Noé Charron 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 15 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 16 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 17 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 18 | SOFTWARE. 19 | */ 20 | 21 | using System; 22 | using System.Collections.Generic; 23 | using System.Text; 24 | using UnityEngine; 25 | 26 | public class StateNav : MonoBehaviour { 27 | 28 | private enum BaseStateEnterInvocation { DontInvoke, OnAwake, OnStart } 29 | 30 | private enum LogDetail { DontLog, OnlyStateChanges, All } 31 | 32 | /// 33 | /// The current state the nav is in. 34 | /// 35 | public State CurrentState => path.Count > 0 ? path[path.Count - 1] : null; 36 | public State PreviousState { get; private set; } 37 | public bool IsInBaseState => CurrentState == baseState; 38 | 39 | /// 40 | /// Event called every time the nav switches to a new state. 41 | /// Provides the previous state, then the new state. 42 | /// 43 | public event Action SwitchedStates = delegate {}; 44 | 45 | [SerializeField] 46 | private State baseState; 47 | public State BaseState => baseState; 48 | 49 | [Tooltip("States that can be transitioned to from any state.")] 50 | [SerializeField] 51 | private List fromAnyState = new List(); 52 | 53 | /// 54 | /// Should this invoke the base state's enter on start? 55 | /// 56 | [Space] 57 | [Tooltip("Should this invoke the base state's enter on start?")] 58 | [SerializeField] 59 | private BaseStateEnterInvocation baseStateEnterInvocation = BaseStateEnterInvocation.OnStart; 60 | 61 | /// 62 | /// Set this to true if you want the path to clear upon entering the base state. 63 | /// This allows you to have loops in your state flow while preventing you from 64 | /// leaving the base state back to its parent state. 65 | /// 66 | /// You can do this on a case by case basis by calling after 67 | /// transitioning to the base state. 68 | /// 69 | public bool clearPathOnEnterBaseState; 70 | [Space] 71 | [SerializeField] 72 | private LogDetail logDetail = LogDetail.OnlyStateChanges; 73 | [SerializeField] 74 | private bool drawPath; 75 | 76 | private List path = new List(); 77 | 78 | private void Awake() { 79 | path.Clear(); 80 | 81 | if (baseState != null) { 82 | path.Add(baseState); 83 | 84 | if (baseStateEnterInvocation == BaseStateEnterInvocation.OnAwake) { 85 | baseState.RaiseEnterEvent(); 86 | DetailedLog("Entered base state on Awake"); 87 | } 88 | } 89 | } 90 | 91 | private void Start() { 92 | if (baseState != null && baseStateEnterInvocation == BaseStateEnterInvocation.OnStart) { 93 | baseState.RaiseEnterEvent(); 94 | DetailedLog("Entered base state on Start"); 95 | } 96 | } 97 | 98 | /// 99 | /// Moves to a new state if the previous state has it as a child state. 100 | /// 101 | /// The state to move to. 102 | /// Returns true if move was successful. 103 | public bool MoveTo(State state) { 104 | if (CanMoveTo(state, true)) { 105 | PreviousState = CurrentState; 106 | path.Add(state); 107 | 108 | TryClearPathIfBase(state); 109 | 110 | PreviousState.RaiseLeftToChildEvent(); 111 | CurrentState.RaiseEnterEvent(); 112 | 113 | SwitchedStates.Invoke(PreviousState, CurrentState); 114 | 115 | Log($"Moved from {PreviousState} to {CurrentState}"); 116 | return true; 117 | } 118 | 119 | DetailedLog($"Could not move to {state} from {CurrentState}"); 120 | return false; 121 | } 122 | 123 | /// 124 | /// Leaves the state if it's the nav's current state and returns to the previous state. 125 | /// 126 | /// The state to leave. 127 | /// Returns true if leaving was successful. 128 | public bool Leave(State state) { 129 | if (CanLeave(state, true)) { 130 | PreviousState = CurrentState; 131 | 132 | path.RemoveAt(path.Count - 1); 133 | 134 | TryClearPathIfBase(state); 135 | 136 | PreviousState.RaiseLeftToParentEvent(); 137 | CurrentState.RaiseEnterEvent(); 138 | 139 | SwitchedStates.Invoke(PreviousState, CurrentState); 140 | 141 | Log($"Left {PreviousState} to fall back to {CurrentState}"); 142 | return true; 143 | } 144 | 145 | DetailedLog($"Could not leave {state} from {CurrentState}"); 146 | return false; 147 | } 148 | 149 | /// 150 | /// Switches to a new state if it is also the child of the current state's parent. 151 | /// 152 | /// The state to switch to. 153 | /// Returns true if switching was successful. 154 | public bool SwitchTo(State state) { 155 | if (CanSwitchTo(state, true)) { 156 | PreviousState = CurrentState; 157 | path[path.Count - 1] = state; 158 | 159 | TryClearPathIfBase(state); 160 | 161 | PreviousState.RaiseLeftToSiblingEvent(); 162 | CurrentState.RaiseEnterEvent(); 163 | 164 | SwitchedStates.Invoke(PreviousState, CurrentState); 165 | 166 | Log($"Switched from {PreviousState} to {CurrentState}"); 167 | return true; 168 | } 169 | 170 | DetailedLog($"Could not switch to {state} from {CurrentState}"); 171 | return false; 172 | } 173 | 174 | public bool CanMoveTo(State state) => CanMoveTo(state, false); 175 | 176 | public bool CanLeave(State state) => CanLeave(state, false); 177 | 178 | public bool CanSwitchTo(State state) => CanSwitchTo(state, false); 179 | 180 | private bool CanMoveTo(State state, bool throwExceptions) { 181 | if (CurrentState == null) { 182 | if (throwExceptions) 183 | throw new NullReferenceException("Can't move to a new state from a null state!"); 184 | return false; 185 | } 186 | 187 | if (throwExceptions && state == null) 188 | throw new System.NullReferenceException("Can't move to a null state dingus!! >:0"); 189 | return CurrentState.childStates.Contains(state) || fromAnyState.Contains(state); 190 | } 191 | 192 | private bool CanLeave(State state, bool throwExceptions) { 193 | if (throwExceptions) { 194 | if (path[path.Count - 2] == null) 195 | throw new NullReferenceException("Can't leave to a null parent state!"); 196 | if (state == null) 197 | throw new NullReferenceException("Can't leave a null state dingus!! >:0"); 198 | if (path.Count == 1 && state == CurrentState) 199 | throw new Exception("You can't leave the base state!"); 200 | } 201 | return state == CurrentState && path.Count != 1; 202 | } 203 | 204 | private bool CanSwitchTo(State state, bool throwExceptions) { 205 | State parentState = path[path.Count - 2]; 206 | 207 | if (throwExceptions) { 208 | if (parentState == null) 209 | throw new NullReferenceException("Can't switch from a null parent state!"); 210 | if (state == null) 211 | throw new NullReferenceException("Can't switch from a null state dingus!! >:0"); 212 | if (path.Count == 1) 213 | throw new Exception("You can't switch out of the base state! Try just regular ol' MoveTo()"); 214 | } 215 | return parentState.childStates.Contains(state); 216 | } 217 | 218 | public bool InState(State state) => CurrentState == state; 219 | 220 | /// 221 | /// Returns true if the parent state is in the path. 222 | /// 223 | /// The state to check in the path. 224 | /// Does the current state count? 225 | public bool IsInChildStateOf(State parentState, bool includeCurrentState = false) { 226 | for (int i = path.Count - (includeCurrentState ? 1 : 2); i >= 0; i--) { 227 | if (path[i] == parentState) 228 | return true; 229 | } 230 | return false; 231 | } 232 | 233 | /// 234 | /// Clears the path if currently in the base state. 235 | /// 236 | /// Returns true if cleared. 237 | public bool ClearPath() { 238 | if (CurrentState == baseState) { 239 | path.Clear(); 240 | path.Add(baseState); 241 | 242 | DetailedLog("Path cleared"); 243 | return true; 244 | } 245 | DetailedLog($"Path not cleared as current state ({CurrentState}) is not the base state ({BaseState})"); 246 | return false; 247 | } 248 | 249 | private void TryClearPathIfBase(State newState) { 250 | if (clearPathOnEnterBaseState && newState == baseState) 251 | ClearPath(); 252 | } 253 | 254 | /// 255 | /// Returns the current path from the base state to the current state. 256 | /// 257 | public string GetCurrentPath() { 258 | StringBuilder sb = new StringBuilder(); 259 | for (int i = 0; i < path.Count; i++) 260 | sb.Append($" > {path[i].name}"); 261 | return sb.ToString().Trim(); 262 | } 263 | 264 | /// 265 | /// Gets a copy of the path using ToArray(). 266 | /// 267 | public State[] GetCopyOfPath() { 268 | return path.ToArray(); 269 | } 270 | 271 | /// 272 | /// Initializes the nav with a path of n > 0 states. 273 | /// The state at index 0 will become the base state, and the state at index n - 1 will become the current state. 274 | /// 275 | /// The path to initialize the nav with. 276 | /// Should the current state's Entered event be raised after initialization? 277 | public void InitializeWithPath(State[] path, bool raiseCurrentStateEnter) { 278 | if (path == null) 279 | throw new ArgumentNullException("Provided path was null"); 280 | if (path.Length == 0) 281 | throw new System.Exception("Path must have at least 1 state for the base state!"); 282 | 283 | baseState = path[0]; 284 | this.path = new List(path); 285 | 286 | DetailedLog($"Initialized nav with new path of {path.Length} state{(path.Length == 1 ? "" : "s")}"); 287 | 288 | if (raiseCurrentStateEnter) 289 | CurrentState.RaiseEnterEvent(); 290 | } 291 | 292 | /// 293 | /// Initializes the nav with a base state. 294 | /// Note that if the nav is already initialized, this will replace the base state and not throw any left events on the current state. 295 | /// 296 | /// The state to use as the base state for the nav. 297 | /// Should the base state's Entered event be raised after initialization? 298 | public void InitializeWithState(State baseState, bool raiseCurrentStateEnter) { 299 | if (path == null) 300 | throw new ArgumentNullException("Base state cannot be initialized with null"); 301 | 302 | this.baseState = baseState; 303 | 304 | path.Clear(); 305 | path.Add(baseState); 306 | 307 | DetailedLog($"Initialized nav with base state of {baseState.name}"); 308 | 309 | if (raiseCurrentStateEnter) 310 | CurrentState.RaiseEnterEvent(); 311 | } 312 | 313 | private void Log(string message) { 314 | if (logDetail != LogDetail.DontLog) 315 | Debug.Log($"{message}\nState Path: {GetCurrentPath()}"); 316 | } 317 | 318 | private void DetailedLog(string message) { 319 | if (logDetail == LogDetail.All) 320 | Debug.Log($"{message}\nState path: {GetCurrentPath()}"); 321 | } 322 | 323 | public static implicit operator State(StateNav stateTree) => stateTree.CurrentState; 324 | 325 | #if UNITY_EDITOR 326 | private Texture2D backgroundTexture; 327 | 328 | private void OnGUI() { 329 | if (!drawPath) 330 | return; 331 | 332 | if (backgroundTexture == null) { 333 | backgroundTexture = new Texture2D(1, 1); 334 | backgroundTexture.SetPixel(0, 0, Color.black); 335 | backgroundTexture.Apply(); 336 | } 337 | 338 | GUIStyle style = new GUIStyle(); 339 | style.normal.background = backgroundTexture; 340 | style.normal.textColor = new Color(.8f, .7f, 0); 341 | style.padding = new RectOffset(10, 10, 10, 10); 342 | style.fontSize = 35; 343 | 344 | GUILayout.Box(GetCurrentPath(), style); 345 | } 346 | #endif 347 | } 348 | -------------------------------------------------------------------------------- /SetAspectRatio.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | public class SetAspectRatio : MonoBehaviour { 5 | 6 | public Vector2 aspectRatio = new Vector3(16, 9); 7 | 8 | void Start() { 9 | float targetAspect = aspectRatio.x / aspectRatio.y; 10 | 11 | float windowAspect = (float) Screen.width / (float) Screen.height; 12 | 13 | float scaleHeight = windowAspect / targetAspect; 14 | 15 | Camera camera = GetComponent(); 16 | 17 | if (scaleHeight < 1.0f) { 18 | 19 | Rect rect = camera.rect; 20 | 21 | rect.width = 1.0f; 22 | rect.height = scaleHeight; 23 | rect.x = 0; 24 | rect.y = (1.0f - scaleHeight) / 2.0f; 25 | 26 | camera.rect = rect; 27 | 28 | } else { 29 | float scaleWidth = 1.0f / scaleHeight; 30 | 31 | Rect rect = camera.rect; 32 | 33 | rect.width = scaleWidth; 34 | rect.height = 1.0f; 35 | rect.x = (1.0f - scaleWidth) / 2.0f; 36 | rect.y = 0; 37 | 38 | camera.rect = rect; 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /Simple Animator/SimpleAnimator.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using UnityEngine; 3 | using UnityEngine.UI; 4 | 5 | public abstract class SimpleAnimator : MonoBehaviour { 6 | 7 | [SerializeField] 8 | private bool playOnStart = true; 9 | [SerializeField] 10 | private PlayType playType; 11 | [SerializeField] 12 | private int frameRate; 13 | [SerializeField] 14 | private int frameRateVariation; 15 | [SerializeField] 16 | private int initialOffset; 17 | [Space] 18 | [SerializeField] 19 | private Frame[] frames; 20 | 21 | private int index; 22 | 23 | private void Start() { 24 | if (playOnStart) 25 | StartCoroutine(Run()); 26 | } 27 | 28 | private IEnumerator Run() { 29 | 30 | float actualFrameRate = frameRate + Random.Range(-frameRateVariation, frameRateVariation); 31 | 32 | if (playType == PlayType.Loop) { 33 | while (true) { 34 | Frame current = frames[(index + initialOffset) % frames.Length]; 35 | SetImage(current.frame); 36 | 37 | yield return new WaitForSeconds((float)current.duration / actualFrameRate); 38 | index = (index + 1) % frames.Length; 39 | } 40 | 41 | } else if (playType == PlayType.Once) { 42 | 43 | for (int i = initialOffset; i < frames.Length; i++) { 44 | SetImage(frames[i].frame); 45 | yield return new WaitForSeconds((float)frames[i].duration / actualFrameRate); 46 | } 47 | } 48 | } 49 | 50 | protected abstract void SetImage(Sprite sprite); 51 | 52 | public void Play() { 53 | StartCoroutine(Run()); 54 | } 55 | 56 | private enum PlayType { 57 | Loop, 58 | Once 59 | } 60 | 61 | [System.Serializable] 62 | private class Frame { 63 | public Sprite frame; 64 | public int duration = 1; 65 | } 66 | } -------------------------------------------------------------------------------- /Simple Animator/SimpleSpriteAnimator.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | [RequireComponent(typeof(SpriteRenderer))] 4 | public class SimpleSpriteAnimator : SimpleAnimator { 5 | 6 | private SpriteRenderer spriteRenderer; 7 | 8 | private void Awake() { 9 | spriteRenderer = GetComponent(); 10 | } 11 | 12 | protected override void SetImage(Sprite sprite) { 13 | spriteRenderer.sprite = sprite; 14 | } 15 | } -------------------------------------------------------------------------------- /Simple Animator/SimpleStateAnimator.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using UnityEngine; 3 | 4 | public class SimpleStateAnimator : MonoBehaviour { 5 | 6 | public bool startOnEnable; 7 | public Animation[] anims; 8 | 9 | private SpriteRenderer spriteRenderer; 10 | private int animIndex; 11 | private Coroutine currentAnimPlaying; 12 | 13 | private void Awake() { 14 | spriteRenderer = GetComponent(); 15 | } 16 | 17 | private void OnEnable() { 18 | if (startOnEnable) 19 | PlayAnim(0); 20 | } 21 | 22 | public void PlayAnim(string name) { 23 | for (int i = 0; i < anims.Length; i++) { 24 | if (anims[i].name == name) { 25 | PlayAnim(i); 26 | return; 27 | } 28 | } 29 | throw new System.Exception($"hmmmm, don't see {name} anywhere in here??"); 30 | } 31 | 32 | public void PlayAnim(int index) { 33 | animIndex = index; 34 | if (currentAnimPlaying != null) 35 | StopCoroutine(currentAnimPlaying); 36 | currentAnimPlaying = StartCoroutine(Play(anims[index])); 37 | 38 | } 39 | 40 | public void PlayNext() { 41 | PlayAnim((animIndex + 1) % anims.Length); 42 | } 43 | 44 | public void PlayPrev() { 45 | if (animIndex == 0) 46 | animIndex = anims.Length - 1; 47 | else 48 | animIndex--; 49 | PlayAnim(animIndex); 50 | } 51 | 52 | private IEnumerator Play(Animation anim) { 53 | int index = 0; 54 | while (true) { 55 | while (WorldControl.isPaused) 56 | yield return null; 57 | 58 | spriteRenderer.sprite = anim.frames[index]; 59 | if (index == anim.frames.Length - 1 && !anim.lööp) 60 | break; 61 | yield return new WaitForSeconds(1f / anim.framerate); 62 | index = (index + 1) % anim.frames.Length; 63 | } 64 | 65 | currentAnimPlaying = null; 66 | } 67 | 68 | [System.Serializable] 69 | public class Animation { 70 | public string name; 71 | public int framerate = 25; 72 | public Sprite[] frames; 73 | public bool lööp = true; 74 | } 75 | } -------------------------------------------------------------------------------- /Simple Animator/SimpleUIAnimator.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEngine.UI; 3 | 4 | [RequireComponent(typeof(Image))] 5 | public class SimpleUIAnimator : SimpleAnimator { 6 | 7 | private Image image; 8 | 9 | private void Awake() { 10 | image = GetComponent(); 11 | } 12 | 13 | protected override void SetImage(Sprite sprite) { 14 | image.sprite = sprite; 15 | } 16 | } -------------------------------------------------------------------------------- /SpeedrunClock.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | public class SpeedrunClock : MonoBehaviour { 4 | 5 | public double time; 6 | 7 | private bool timeAdvance = true; 8 | 9 | private void Update() { 10 | if (timeAdvance) 11 | time += (double)Time.unscaledDeltaTime; 12 | } 13 | 14 | public void StopTimer() { 15 | timeAdvance = false; 16 | } 17 | 18 | public void ResumeTimer() { 19 | timeAdvance = true; 20 | } 21 | 22 | [ContextMenu("Print Time")] 23 | public string GetStringTime() { 24 | string ret = System.TimeSpan.FromSeconds(time).ToString("hh\\:mm\\:ss\\.fff"); 25 | 26 | print(ret); 27 | return ret; 28 | } 29 | } -------------------------------------------------------------------------------- /Spring.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using UnityEngine; 3 | 4 | public class Spring : MonoBehaviour { 5 | 6 | public float spring = 50; 7 | [Range(0, 1)] 8 | public float damper = .35f; 9 | public Vector2 targetPos; 10 | 11 | private Vector2 vel; 12 | 13 | private void Update() { 14 | // credit this bit from wilhelm nylund @wilnyl 15 | // https://twitter.com/wilnyl/status/1201516498445058048?s=20 16 | vel += (targetPos - (Vector2)transform.localPosition) * spring - (vel * damper); 17 | transform.Translate(vel * Time.deltaTime); 18 | } 19 | 20 | /// 21 | /// Push the GameObject in a direction. 22 | /// 23 | /// The direction of the force applied. 24 | /// The magnitude of the force applied. 25 | public void Push(Vector2 dir, float amount) { 26 | Push(dir * amount); 27 | } 28 | 29 | /// 30 | /// Push the GameObject in a direction. 31 | /// 32 | /// The force to apply to the GameObject. 33 | public void Push(Vector2 force) { 34 | vel += force; 35 | } 36 | 37 | /// 38 | /// Shake the GameObject. 39 | /// 40 | /// The intensity of the force applied. 41 | /// The duration of the shake. 42 | /// How frequently the GameObject should be pushed in a different direction. 43 | /// Is the frequency in real time or time as per the scaled time? 44 | public void Shake(float intensity, float duration, float frequency = .1f, bool realTime = false) { 45 | StartCoroutine(DoShake(intensity, duration, frequency, realTime)); 46 | } 47 | 48 | private IEnumerator DoShake(float intensity, float duration, float frequency, bool realTime) { 49 | for (float elapsed = 0; elapsed < duration; elapsed += (realTime?Time.unscaledDeltaTime : Time.deltaTime)) { 50 | 51 | Vector2 dir = ((new Vector2(Random.value, Random.value) - Vector2.one / 2f) * 2f).normalized; 52 | Push(dir * intensity); 53 | 54 | if (realTime) 55 | yield return new WaitForSecondsRealtime(frequency); 56 | else 57 | yield return new WaitForSeconds(frequency); 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /Tag.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | public class Tag : MonoBehaviour { 4 | 5 | [MultiEnum] 6 | public Tags tags; 7 | 8 | public bool Has(Tags tag) => tags.HasFlag(tag); 9 | 10 | public void AddTag(Tags tag) { 11 | tags |= tag; 12 | } 13 | 14 | public void RemoveTag(Tags tag) { 15 | tags &= ~tag; 16 | } 17 | 18 | public void Toggle(Tags tag) { 19 | tags ^= tag; 20 | } 21 | 22 | } 23 | 24 | [System.Flags] 25 | public enum Tags { 26 | Nothing = 1, 27 | WillMove = 2, 28 | IsEnvironment = 4 29 | } -------------------------------------------------------------------------------- /UniqueID.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Security.Cryptography; 3 | using System.Text; 4 | using NaughtyAttributes; 5 | using UnityEngine; 6 | 7 | #if UNITY_ENGINE 8 | using UnityEditor.SceneManagement; 9 | using UnityEngine.SceneManagement; 10 | #endif 11 | 12 | [ExecuteInEditMode] 13 | public class UniqueID : MonoBehaviour { 14 | 15 | public string group; 16 | [ReadOnly] 17 | public int ID; 18 | 19 | [ContextMenu("Regenerate")] 20 | private void Reset() { 21 | MD5 md5 = MD5.Create(); 22 | 23 | ID = BitConverter.ToInt32(md5.ComputeHash(Encoding.UTF8.GetBytes(group + GetInstanceID())), 0); 24 | md5.Clear(); 25 | 26 | #if UNITY_ENGINE 27 | EditorSceneManager.MarkSceneDirty(SceneManager.GetActiveScene()); 28 | #endif 29 | } 30 | } -------------------------------------------------------------------------------- /Utils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Runtime.Serialization.Formatters.Binary; 6 | using UnityEngine; 7 | 8 | public static class Utils { 9 | 10 | #region UTILITIES 11 | 12 | #region Number Utils 13 | 14 | public static float pixelsPerUnit = 16; 15 | 16 | public static float PixelsToUnits(float value) => PixelsToUnits(value, pixelsPerUnit); 17 | public static float PixelsToUnits(float value, float ppu) => value / ppu; 18 | 19 | public static float UnitsToPixels(float value) => UnitsToPixels(value, pixelsPerUnit); 20 | public static float UnitsToPixels(float value, float ppu) => value * ppu; 21 | 22 | public static float ClampWithMax(float value, float max) => value > max ? max : value; 23 | public static float ClampWithMin(float value, float min) => value < min ? min : value; 24 | 25 | public static float FindTime(float speed, float distance) => distance / speed; 26 | public static float FindDist(float speed, float time) => speed * time; 27 | public static float FindSpeed(float distance, float time) => distance / time; 28 | 29 | public static int IndexOfSmallest(float[] values) { 30 | if (values == null || values.Length == 0) 31 | throw new Exception("hey what the fuck, give me some values"); 32 | float smallest = values[0]; 33 | int index = 0; 34 | for (int i = 1; i < values.Length; i++) { 35 | if (values[i] < smallest) { 36 | smallest = values[i]; 37 | index = i; 38 | } 39 | } 40 | return index; 41 | } 42 | 43 | public static int IndexOfLargest(float[] values) { 44 | if (values == null || values.Length == 0) 45 | throw new Exception("hey what the fuck, give me some values"); 46 | float largest = values[0]; 47 | int index = 0; 48 | for (int i = 1; i < values.Length; i++) { 49 | if (values[i] > largest) { 50 | largest = values[i]; 51 | index = i; 52 | } 53 | } 54 | return index; 55 | } 56 | 57 | public static float ExpLerp01(float t, float exp = 10f) => ExpLerp(0, 1, Mathf.Clamp01(t), exp); 58 | 59 | public static float ExpLerp(float a, float b, float t, float exp = 10f) { 60 | return a + Mathf.Pow(Mathf.InverseLerp(a, b, t), exp) * (b - a); 61 | } 62 | 63 | public static float AbsMax(float a, float b) { 64 | if (Mathf.Abs(a) >= Mathf.Abs(b)) 65 | return a; 66 | return b; 67 | } 68 | 69 | public static float AbsMin(float a, float b) { 70 | if (Mathf.Abs(a) <= Mathf.Abs(b)) 71 | return a; 72 | return b; 73 | } 74 | 75 | public static float RoundToNearest(float value, float[] snaps) { 76 | if (snaps == null) 77 | throw new Exception("um wtf the snaps array u gave me is null :/"); 78 | if (snaps.Length == 0) 79 | throw new Exception("lmao fucker u gotta put things in the snaps array to snap to"); 80 | 81 | float smallestDist = Mathf.Abs(value - snaps[0]); 82 | int index = 0; 83 | for (int i = 1; i < snaps.Length; i++) { 84 | float dist = Mathf.Abs(value - snaps[i]); 85 | if (dist < smallestDist) { 86 | smallestDist = dist; 87 | index = i; 88 | } 89 | } 90 | return snaps[index]; 91 | } 92 | 93 | #endregion 94 | 95 | #region Vector Utils 96 | 97 | public static Vector2 InverseLerp(Vector2 a, Vector2 b, Vector2 t) { 98 | return new Vector2(Mathf.InverseLerp(a.x, b.x, t.x), Mathf.InverseLerp(a.y, b.y, t.y)); 99 | } 100 | 101 | public static Vector2 Lerp(Vector2 a, Vector2 b, Vector2 t) { 102 | return new Vector2(Mathf.Lerp(a.x, b.x, t.x), Mathf.Lerp(a.y, b.y, t.y)); 103 | } 104 | 105 | #endregion 106 | 107 | #region Colour Utils 108 | 109 | // based on how unity did it: 110 | // https://stackoverflow.com/questions/61372498/how-does-mathf-smoothdamp-work-what-is-it-algorithm 111 | public static Color ColourSmoothDamp(Color current, Color target, ref Vector4 currentVelocity, float smoothTime) => 112 | ColourSmoothDamp(current, target, ref currentVelocity, smoothTime, Mathf.Infinity, Time.deltaTime); 113 | public static Color ColourSmoothDamp(Color current, Color target, ref Vector4 currentVelocity, float smoothTime, float maxSpeed) => 114 | ColourSmoothDamp(current, target, ref currentVelocity, smoothTime, maxSpeed, Time.deltaTime); 115 | 116 | public static Color ColourSmoothDamp(Color current, Color target, ref Vector4 currentVelocity, float smoothTime, float maxSpeed, float deltaTime) { 117 | // Based on Game Programming Gems 4 Chapter 1.10 118 | smoothTime = Mathf.Max(0.0001f, smoothTime); 119 | float omega = 2f / smoothTime; 120 | 121 | Vector4 currentColour = new Vector4(current.r, current.g, current.b, current.a); 122 | Vector4 targetColour = new Vector4(target.r, target.g, target.b, target.a); 123 | 124 | float x = omega * deltaTime; 125 | float exp = 1f / (1f + x + 0.48f * x * x + 0.235f * x * x * x); 126 | Vector4 change = currentColour - targetColour; 127 | Vector4 originalTo = targetColour; 128 | 129 | // Clamp maximum speed 130 | Vector4 maxChange = maxSpeed * smoothTime * Vector4.one; 131 | change.x = Mathf.Clamp(change.x, -maxChange.x, maxChange.x); 132 | change.y = Mathf.Clamp(change.y, -maxChange.y, maxChange.y); 133 | change.z = Mathf.Clamp(change.z, -maxChange.z, maxChange.z); 134 | change.w = Mathf.Clamp(change.w, -maxChange.w, maxChange.w); 135 | 136 | targetColour = currentColour - change; 137 | 138 | Vector4 temp = (currentVelocity + (omega * change)) * deltaTime; 139 | currentVelocity = (currentVelocity - omega * temp) * exp; 140 | Vector4 output = targetColour + (change + temp) * exp; 141 | 142 | // Prevent overshooting 143 | if (originalTo.x - currentColour.x > 0.0f == output.x > originalTo.x) { 144 | output.x = originalTo.x; 145 | currentVelocity.x = (output.x - originalTo.x) / deltaTime; 146 | } 147 | if (originalTo.y - currentColour.y > 0.0f == output.y > originalTo.y) { 148 | output.y = originalTo.y; 149 | currentVelocity.y = (output.y - originalTo.y) / deltaTime; 150 | } 151 | if (originalTo.z - currentColour.z > 0.0f == output.z > originalTo.z) { 152 | output.z = originalTo.z; 153 | currentVelocity.z = (output.z - originalTo.z) / deltaTime; 154 | } 155 | if (originalTo.w - currentColour.w > 0.0f == output.w > originalTo.w) { 156 | output.w = originalTo.w; 157 | currentVelocity.w = (output.w - originalTo.w) / deltaTime; 158 | } 159 | 160 | return new Color(output.x, output.y, output.z, output.w); 161 | } 162 | 163 | #endregion 164 | 165 | #region Coroutine Utils 166 | 167 | /// Duration in seconds. 168 | /// Should use real time or scaled time? 169 | /// 170 | public static IEnumerator PauseableWait(float duration, Func predicate, bool realtime = false) { 171 | for (float elapsed = 0; elapsed < duration; elapsed += (realtime?Time.unscaledDeltaTime : Time.deltaTime)) { 172 | while (predicate.Invoke()) 173 | yield return null; 174 | yield return null; 175 | } 176 | } 177 | 178 | /// 179 | /// Call a function every frame for a duration. 180 | /// 181 | /// How long call the function for. 182 | /// Function to be invoked and passed the elapsed time. 183 | /// 184 | public static IEnumerator DoOverTime(float duration, Action action, bool callAgainOnComplete = true, Func pausePredicate = null) { 185 | for (float elapsed = 0; elapsed < duration; elapsed += Time.deltaTime) { 186 | if (pausePredicate != null && pausePredicate.Invoke()) 187 | yield return null; 188 | 189 | action.Invoke(elapsed); 190 | yield return null; 191 | } 192 | if (callAgainOnComplete) 193 | action.Invoke(duration); 194 | } 195 | #endregion 196 | 197 | #region Generic Utils 198 | 199 | public static T DeepCopy(T other) { 200 | using(MemoryStream ms = new MemoryStream()) { 201 | BinaryFormatter formatter = new BinaryFormatter(); 202 | formatter.Serialize(ms, other); 203 | ms.Position = 0; 204 | return (T)formatter.Deserialize(ms); 205 | } 206 | } 207 | 208 | #endregion 209 | 210 | #endregion 211 | 212 | #region EXTENSION METHODS 213 | 214 | #region Value Extensions 215 | 216 | /// 217 | /// The fractional component of the value. 218 | /// 219 | public static float Frac(this float f) => f - (int)f; 220 | 221 | /// 222 | /// The absolute fractional component of the value. 223 | /// 224 | public static float AbsFrac(this float f) => Math.Abs(f - (int)f); 225 | 226 | /// 227 | /// Is the character an uppercase letter? 228 | /// 229 | public static bool IsUpper(this char c) => c >= 'A' && c <= 'Z'; 230 | 231 | /// 232 | /// Is the character a lowercase letter? 233 | /// 234 | public static bool IsLower(this char c) => c >= 'a' && c <= 'z'; 235 | 236 | /// 237 | /// Is the character a letter? 238 | /// 239 | public static bool IsLetter(this char c) => c.IsUpper() || c.IsLower(); 240 | 241 | /// 242 | /// Trim every string in an array. 243 | /// 244 | public static string[] Trim(this string[] s) { 245 | for (int i = 0; i < s.Length; i++) 246 | s[i] = s[i].Trim(); 247 | return s; 248 | } 249 | 250 | /// 251 | /// Puts richtext colour tags around the string. 252 | /// 253 | public static string Colour(this string s, Color colour) { 254 | string cHex = colour.ToHexRGBA(); 255 | return "" + s + ""; 256 | } 257 | 258 | public static void ClampWithMax(this ref float value, float max) { 259 | if (value > max) 260 | value = max; 261 | } 262 | 263 | public static void ClampWithMin(this ref float value, float min) { 264 | if (value < min) 265 | value = min; 266 | } 267 | 268 | /// 269 | /// Is the int even? 270 | /// 271 | public static bool IsEven(this int i) => i % 2 == 0; 272 | 273 | /// 274 | /// Is the int odd? 275 | /// 276 | public static bool IsOdd(this int i) => i % 2 != 0; 277 | 278 | /// 279 | /// gets u either -1, 0, 1 280 | /// 281 | public static float Direction(this float value) { 282 | if (value == 0) 283 | return 0; 284 | return value / Math.Abs(value); 285 | } 286 | 287 | /// 288 | /// Converts HashSet into an array. 289 | /// 290 | public static T[] ToArray(this HashSet set) { 291 | T[] array = new T[set.Count]; 292 | int count = 0; 293 | foreach (T t in set) { 294 | array[count] = t; 295 | count++; 296 | } 297 | return array; 298 | } 299 | 300 | #endregion 301 | 302 | #region Enum Extensions 303 | 304 | /// 305 | /// Returns how many entries there are in the enum. 306 | /// 307 | public static int LengthOfEnum(this TEnum t)where TEnum : struct => System.Enum.GetNames(typeof(TEnum)).Length; 308 | 309 | /// 310 | /// Inserts spaces in between words of an enum. 311 | /// 312 | public static string MakeEnumReadable(this TEnum t)where TEnum : struct, IConvertible { 313 | string entry = t.ToString(); 314 | for (int i = 1; i < entry.Length; i++) { 315 | if (entry[i].IsUpper()) { 316 | entry = entry.Insert(i, " "); 317 | i++; 318 | } 319 | } 320 | return entry; 321 | } 322 | 323 | #endregion 324 | 325 | #region Vector Extensions 326 | 327 | public static Vector2 Add(this ref Vector2 v, float x, float y) => v + new Vector2(x, y); 328 | 329 | public static Vector3 ToV3(this Vector2 v, float z) => new Vector3(v.x, v.y, z); 330 | 331 | public static Vector2 Abs(this Vector2 v) => new Vector2(Mathf.Abs(v.x), Mathf.Abs(v.y)); 332 | public static Vector2Int Abs(this Vector2Int v) => new Vector2Int(Mathf.Abs(v.x), Mathf.Abs(v.y)); 333 | public static Vector3 Abs(this Vector3 v) => new Vector3(Mathf.Abs(v.x), Mathf.Abs(v.y), Mathf.Abs(v.z)); 334 | public static Vector3Int Abs(this Vector3Int v) => new Vector3Int(Mathf.Abs(v.x), Mathf.Abs(v.y), Mathf.Abs(v.z)); 335 | 336 | public static Vector2 Round(this ref Vector2 v) => v = v.Rounded(); 337 | public static Vector3 Round(this ref Vector3 v) => v = v.Rounded(); 338 | public static Vector2 Rounded(this Vector2 v) => new Vector2(Mathf.RoundToInt(v.x), Mathf.RoundToInt(v.y)); 339 | public static Vector3 Rounded(this Vector3 v) => new Vector3(Mathf.RoundToInt(v.x), Mathf.RoundToInt(v.y), Mathf.RoundToInt(v.z)); 340 | public static Vector2Int RoundedToInt(this Vector2 v) => new Vector2Int(Mathf.RoundToInt(v.x), Mathf.RoundToInt(v.y)); 341 | public static Vector3Int RoundedToInt(this Vector3 v) => new Vector3Int(Mathf.RoundToInt(v.x), Mathf.RoundToInt(v.y), Mathf.RoundToInt(v.z)); 342 | 343 | public static Vector2 Floor(this ref Vector2 v) => v = v.Floored(); 344 | public static Vector3 Floor(this ref Vector3 v) => v = v.Floored(); 345 | public static Vector2 Floored(this Vector2 v) => new Vector2(Mathf.Floor(v.x), Mathf.Floor(v.y)); 346 | public static Vector3 Floored(this Vector3 v) => new Vector3(Mathf.Floor(v.x), Mathf.Floor(v.y), Mathf.Floor(v.z)); 347 | 348 | public static Vector2 Ceil(this ref Vector2 v) => v = v.Ceiled(); 349 | public static Vector3 Ceil(this ref Vector3 v) => v = v.Ceiled(); 350 | public static Vector2 Ceiled(this Vector2 v) => new Vector2(Mathf.Ceil(v.x), Mathf.Ceil(v.y)); 351 | public static Vector3 Ceiled(this Vector3 v) => new Vector3(Mathf.Ceil(v.x), Mathf.Ceil(v.y), Mathf.Ceil(v.z)); 352 | 353 | public static Vector2 Clamp(this ref Vector2 v, Vector2 a, Vector2 b) { 354 | v.x = Mathf.Clamp(v.x, Mathf.Min(a.x, b.x), Mathf.Max(a.x, b.x)); 355 | v.y = Mathf.Clamp(v.y, Mathf.Min(a.y, b.y), Mathf.Max(a.y, b.y)); 356 | return v; 357 | } 358 | 359 | #endregion 360 | 361 | #region Colour Extensions 362 | 363 | public static Color WithAlpha(this Color c, float alpha) { 364 | c.a = alpha; 365 | return c; 366 | } 367 | 368 | public static string ToHexRGBA(this Color c) { 369 | string r = ((int)Mathf.Lerp(0, 255, c.r)).ToString("X"); 370 | string g = ((int)Mathf.Lerp(0, 255, c.g)).ToString("X"); 371 | string b = ((int)Mathf.Lerp(0, 255, c.b)).ToString("X"); 372 | string a = ((int)Mathf.Lerp(0, 255, c.a)).ToString("X"); 373 | return (r.Length == 2 ? r : "0" + r) + (g.Length == 2 ? g : "0" + g) + (b.Length == 2 ? b : "0" + b) + (a.Length == 2 ? a : "0" + a); 374 | } 375 | 376 | public static Gradient AsGradient(this Color c) { 377 | Gradient grad = new Gradient(); 378 | grad.colorKeys = new GradientColorKey[] { 379 | new GradientColorKey(c, 0), 380 | new GradientColorKey(c, 1), 381 | }; 382 | grad.alphaKeys = new GradientAlphaKey[] { 383 | new GradientAlphaKey(c.a, 0), 384 | new GradientAlphaKey(c.a, 1), 385 | }; 386 | return grad; 387 | } 388 | 389 | #endregion 390 | 391 | #region Bounds Extensions 392 | 393 | public static bool IsPointInside(this Bounds bounds, Vector2 point) => 394 | point.x <= bounds.max.x && point.x >= bounds.min.x && point.y <= bounds.max.y && point.y >= bounds.min.y; 395 | 396 | #endregion 397 | 398 | #region MonoBehaviour Extensions 399 | 400 | public static Transform GetGrandChild(this Transform trans) { 401 | if (trans.childCount == 0) 402 | return trans; 403 | return trans.GetChild(0).GetGrandChild(); 404 | } 405 | 406 | #endregion 407 | 408 | #endregion 409 | 410 | } --------------------------------------------------------------------------------