using UnityEngine; using UnityEngine.UI; using System.Collections; using System; namespace MoreMountains.Tools { /// /// Math helpers /// public static class MMMaths { /// /// Internal method used to compute the spring velocity /// /// /// /// /// /// /// /// /// private static float SpringVelocity(float currentValue, float targetValue, float velocity, float damping, float frequency, float speed, float deltaTime) { float maxDeltaTime = Mathf.Min(1.0f / (frequency * 10.0f), deltaTime); frequency = frequency * 2f * Mathf.PI; deltaTime = Mathf.Min(deltaTime, maxDeltaTime); return velocity + (deltaTime * frequency * frequency * (targetValue - currentValue)) + (-2.0f * deltaTime * frequency * damping * velocity); } /// /// Springs a float towards a target value /// /// the current value to spring, passed as a ref /// the target value we're aiming for /// a velocity value, passed as ref, used to compute the current speed of the springed value /// the damping, between 0.01f and 1f, the higher the daming, the less springy it'll be /// the frequency, in Hz, so the amount of periods the spring should go over in 1 second /// the speed (between 0 and 1) at which the spring should operate /// the delta time (usually Time.deltaTime or Time.unscaledDeltaTime) public static void Spring(ref float currentValue, float targetValue, ref float velocity, float damping, float frequency, float speed, float deltaTime) { float initialVelocity = velocity; velocity = SpringVelocity(currentValue, targetValue, velocity, damping, frequency, speed, deltaTime); velocity = MMMaths.Lerp(initialVelocity, velocity, speed, Time.deltaTime); currentValue += deltaTime * velocity; } /// /// Springs a Vector2 towards a target value /// /// the current value to spring, passed as a ref /// the target value we're aiming for /// a velocity value, passed as ref, used to compute the current speed of the springed value /// the damping, between 0.01f and 1f, the higher the daming, the less springy it'll be /// the frequency, in Hz, so the amount of periods the spring should go over in 1 second /// the speed (between 0 and 1) at which the spring should operate /// the delta time (usually Time.deltaTime or Time.unscaledDeltaTime) public static void Spring(ref Vector2 currentValue, Vector2 targetValue, ref Vector2 velocity, float damping, float frequency, float speed, float deltaTime) { Vector2 initialVelocity = velocity; velocity.x = SpringVelocity(currentValue.x, targetValue.x, velocity.x, damping, frequency, speed, deltaTime); velocity.y = SpringVelocity(currentValue.y, targetValue.y, velocity.y, damping, frequency, speed, deltaTime); velocity.x = MMMaths.Lerp(initialVelocity.x, velocity.x, speed, Time.deltaTime); velocity.y = MMMaths.Lerp(initialVelocity.y, velocity.y, speed, Time.deltaTime); currentValue += deltaTime * velocity; } /// /// Springs a Vector3 towards a target value /// /// the current value to spring, passed as a ref /// the target value we're aiming for /// a velocity value, passed as ref, used to compute the current speed of the springed value /// the damping, between 0.01f and 1f, the higher the daming, the less springy it'll be /// the frequency, in Hz, so the amount of periods the spring should go over in 1 second /// the speed (between 0 and 1) at which the spring should operate /// the delta time (usually Time.deltaTime or Time.unscaledDeltaTime) public static void Spring(ref Vector3 currentValue, Vector3 targetValue, ref Vector3 velocity, float damping, float frequency, float speed, float deltaTime) { Vector3 initialVelocity = velocity; velocity.x = SpringVelocity(currentValue.x, targetValue.x, velocity.x, damping, frequency, speed, deltaTime); velocity.y = SpringVelocity(currentValue.y, targetValue.y, velocity.y, damping, frequency, speed, deltaTime); velocity.z = SpringVelocity(currentValue.z, targetValue.z, velocity.z, damping, frequency, speed, deltaTime); velocity.x = MMMaths.Lerp(initialVelocity.x, velocity.x, speed, Time.deltaTime); velocity.y = MMMaths.Lerp(initialVelocity.y, velocity.y, speed, Time.deltaTime); velocity.z = MMMaths.Lerp(initialVelocity.z, velocity.z, speed, Time.deltaTime); currentValue += deltaTime * velocity; } /// /// Springs a Vector4 towards a target value /// /// the current value to spring, passed as a ref /// the target value we're aiming for /// a velocity value, passed as ref, used to compute the current speed of the springed value /// the damping, between 0.01f and 1f, the higher the daming, the less springy it'll be /// the frequency, in Hz, so the amount of periods the spring should go over in 1 second /// the speed (between 0 and 1) at which the spring should operate /// the delta time (usually Time.deltaTime or Time.unscaledDeltaTime) public static void Spring(ref Vector4 currentValue, Vector4 targetValue, ref Vector4 velocity, float damping, float frequency, float speed, float deltaTime) { Vector4 initialVelocity = velocity; velocity.x = SpringVelocity(currentValue.x, targetValue.x, velocity.x, damping, frequency, speed, deltaTime); velocity.y = SpringVelocity(currentValue.y, targetValue.y, velocity.y, damping, frequency, speed, deltaTime); velocity.z = SpringVelocity(currentValue.z, targetValue.z, velocity.z, damping, frequency, speed, deltaTime); velocity.w = SpringVelocity(currentValue.w, targetValue.w, velocity.w, damping, frequency, speed, deltaTime); velocity.x = MMMaths.Lerp(initialVelocity.x, velocity.x, speed, Time.deltaTime); velocity.y = MMMaths.Lerp(initialVelocity.y, velocity.y, speed, Time.deltaTime); velocity.z = MMMaths.Lerp(initialVelocity.z, velocity.z, speed, Time.deltaTime); velocity.w = MMMaths.Lerp(initialVelocity.w, velocity.w, speed, Time.deltaTime); currentValue += deltaTime * velocity; } /// /// internal method used to determine the lerp rate /// /// /// private static float LerpRate(float rate, float deltaTime) { rate = Mathf.Clamp01(rate); float invRate = - Mathf.Log(1.0f - rate, 2.0f) * 60f; return Mathf.Pow(2.0f, -invRate * deltaTime); } /// /// Lerps a float towards a target at the specified rate /// /// /// /// /// public static float Lerp(float value, float target, float rate, float deltaTime) { if (deltaTime == 0f) { return value; } return Mathf.Lerp(target, value, LerpRate(rate, deltaTime)); } /// /// Lerps a Vector2 towards a target at the specified rate /// /// /// /// /// public static Vector2 Lerp(Vector2 value, Vector2 target, float rate, float deltaTime) { if (deltaTime == 0f) { return value; } return Vector2.Lerp(target, value, LerpRate(rate, deltaTime)); } /// /// Lerps a Vector3 towards a target at the specified rate /// /// /// /// /// public static Vector3 Lerp(Vector3 value, Vector3 target, float rate, float deltaTime) { if (deltaTime == 0f) { return value; } return Vector3.Lerp(target, value, LerpRate(rate, deltaTime)); } /// /// Lerps a Vector4 towards a target at the specified rate /// /// /// /// /// public static Vector4 Lerp(Vector4 value, Vector4 target, float rate, float deltaTime) { if (deltaTime == 0f) { return value; } return Vector4.Lerp(target, value, LerpRate(rate, deltaTime)); } /// /// Lerps a Quaternion towards a target at the specified rate /// /// /// /// /// public static Quaternion Lerp(Quaternion value, Quaternion target, float rate, float deltaTime) { if (deltaTime == 0f) { return value; } return Quaternion.Lerp(target, value, LerpRate(rate, deltaTime)); } /// /// Lerps a Color towards a target at the specified rate /// /// /// /// /// public static Color Lerp(Color value, Color target, float rate, float deltaTime) { if (deltaTime == 0f) { return value; } return Color.Lerp(target, value, LerpRate(rate, deltaTime)); } /// /// Lerps a Color32 towards a target at the specified rate /// /// /// /// /// public static Color32 Lerp(Color32 value, Color32 target, float rate, float deltaTime) { if (deltaTime == 0f) { return value; } return Color32.Lerp(target, value, LerpRate(rate, deltaTime)); } /// /// Clamps a float between min and max, both bounds being optional and driven by clampMin and clampMax respectively /// /// /// /// /// /// /// public static float Clamp(float value, float min, float max, bool clampMin, bool clampMax) { float returnValue = value; if (clampMin && (returnValue < min)) { returnValue = min; } if (clampMax && (returnValue > max)) { returnValue = max; } return returnValue; } /// /// Rounds a float to the nearest half value : 1, 1.5, 2, 2.5 etc /// /// /// public static float RoundToNearestHalf(float a) { return a = a - (a % 0.5f); } /// /// /// /// /// public static Quaternion LookAt2D(Vector2 direction) { var angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg; return Quaternion.AngleAxis(angle, Vector3.forward); } /// /// Takes a Vector3 and turns it into a Vector2 /// /// The vector2. /// The Vector3 to turn into a Vector2. public static Vector2 Vector3ToVector2 (Vector3 target) { return new Vector2(target.x, target.y); } /// /// Takes a Vector2 and turns it into a Vector3 with a null z value /// /// The vector3. /// The Vector2 to turn into a Vector3. public static Vector3 Vector2ToVector3 (Vector2 target) { return new Vector3(target.x, target.y, 0); } /// /// Takes a Vector2 and turns it into a Vector3 with the specified z value /// /// The vector3. /// The Vector2 to turn into a Vector3. /// New Z value. public static Vector3 Vector2ToVector3 (Vector2 target, float newZValue) { return new Vector3(target.x, target.y, newZValue); } /// /// Rounds all components of a Vector3. /// /// The vector3. /// Vector. public static Vector3 RoundVector3 (Vector3 vector) { return new Vector3 (Mathf.Round (vector.x), Mathf.Round (vector.y), Mathf.Round (vector.z)); } /// /// Returns a random Vector2 from 2 defined Vector2. /// /// The random Vector2. /// Minimum. /// Maximum. public static Vector2 RandomVector2(Vector2 minimum, Vector2 maximum) { return new Vector2(UnityEngine.Random.Range(minimum.x, maximum.x), UnityEngine.Random.Range(minimum.y, maximum.y)); } /// /// Returns a random Vector3 from 2 defined Vector3. /// /// The random Vector3. /// Minimum. /// Maximum. public static Vector3 RandomVector3(Vector3 minimum, Vector3 maximum) { return new Vector3(UnityEngine.Random.Range(minimum.x, maximum.x), UnityEngine.Random.Range(minimum.y, maximum.y), UnityEngine.Random.Range(minimum.z, maximum.z)); } /// /// Returns a random point on the circle of the specified radius /// /// /// public static Vector2 RandomPointOnCircle(float circleRadius) { return UnityEngine.Random.insideUnitCircle.normalized * circleRadius; } /// /// Returns a random point on the sphere of the specified radius /// /// /// public static Vector3 RandomPointOnSphere(float sphereRadius) { return UnityEngine.Random.onUnitSphere * sphereRadius; } /// /// Rotates a point around the given pivot. /// /// The new point position. /// The point to rotate. /// The pivot's position. /// The angle we want to rotate our point. public static Vector3 RotatePointAroundPivot(Vector3 point, Vector3 pivot, float angle) { angle = angle*(Mathf.PI/180f); float rotatedX = Mathf.Cos(angle) * (point.x - pivot.x) - Mathf.Sin(angle) * (point.y-pivot.y) + pivot.x; float rotatedY = Mathf.Sin(angle) * (point.x - pivot.x) + Mathf.Cos(angle) * (point.y - pivot.y) + pivot.y; return new Vector3(rotatedX,rotatedY,0); } /// /// Rotates a point around the given pivot. /// /// The new point position. /// The point to rotate. /// The pivot's position. /// The angle as a Vector3. public static Vector3 RotatePointAroundPivot(Vector3 point, Vector3 pivot, Vector3 angle) { // we get point direction from the point to the pivot Vector3 direction = point - pivot; // we rotate the direction direction = Quaternion.Euler(angle) * direction; // we determine the rotated point's position point = direction + pivot; return point; } /// /// Rotates a point around the given pivot. /// /// The new point position. /// The point to rotate. /// The pivot's position. /// The angle as a Vector3. public static Vector3 RotatePointAroundPivot(Vector3 point, Vector3 pivot, Quaternion quaternion) { // we get point direction from the point to the pivot Vector3 direction = point - pivot; // we rotate the direction direction = quaternion * direction; // we determine the rotated point's position point = direction + pivot; return point; } /// /// Rotates a vector2 by the angle (in degrees) specified and returns it /// /// The rotated Vector2. /// The vector to rotate. /// Degrees. public static Vector2 RotateVector2(Vector2 vector, float angle) { if (angle == 0) { return vector; } float sinus = Mathf.Sin(angle * Mathf.Deg2Rad); float cosinus = Mathf.Cos(angle * Mathf.Deg2Rad); float oldX = vector.x; float oldY = vector.y; vector.x = (cosinus * oldX) - (sinus * oldY); vector.y = (sinus * oldX) + (cosinus * oldY); return vector; } /// /// Computes and returns the angle between two vectors, on a 360° scale /// /// The . /// Vector a. /// Vector b. public static float AngleBetween(Vector2 vectorA, Vector2 vectorB) { float angle = Vector2.Angle(vectorA, vectorB); Vector3 cross = Vector3.Cross(vectorA, vectorB); if (cross.z > 0) { angle = 360 - angle; } return angle; } /// /// Computes and returns the direction between two vector3, used to check if a vector is pointing left or right of another one /// /// The . /// Vector a. /// Vector b. public static float AngleDirection(Vector3 vectorA, Vector3 vectorB, Vector3 up) { Vector3 cross = Vector3.Cross(vectorA, vectorB); float direction = Vector3.Dot(cross, up); return direction; } /// /// Returns the distance between a point and a line. /// /// The between point and line. /// Point. /// Line start. /// Line end. public static float DistanceBetweenPointAndLine(Vector3 point, Vector3 lineStart, Vector3 lineEnd) { return Vector3.Magnitude(ProjectPointOnLine(point, lineStart, lineEnd) - point); } /// /// Projects a point on a line (perpendicularly) and returns the projected point. /// /// The point on line. /// Point. /// Line start. /// Line end. public static Vector3 ProjectPointOnLine(Vector3 point, Vector3 lineStart, Vector3 lineEnd) { Vector3 rhs = point - lineStart; Vector3 vector2 = lineEnd - lineStart; float magnitude = vector2.magnitude; Vector3 lhs = vector2; if (magnitude > 1E-06f) { lhs = (Vector3)(lhs / magnitude); } float num2 = Mathf.Clamp(Vector3.Dot(lhs, rhs), 0f, magnitude); return (lineStart + ((Vector3)(lhs * num2))); } /// /// Returns the sum of all the int passed in parameters /// /// Things to add. public static int Sum(params int[] thingsToAdd) { int result=0; for (int i = 0; i < thingsToAdd.Length; i++) { result += thingsToAdd[i]; } return result; } /// /// Returns the result of rolling a dice of the specified number of sides /// /// The result of the dice roll. /// Number of sides of the dice. public static int RollADice(int numberOfSides) { return (UnityEngine.Random.Range(1,numberOfSides+1)); } /// /// Returns a random success based on X% of chance. /// Example : I have 20% of chance to do X, Chance(20) > true, yay! /// /// Percent of chance. public static bool Chance(int percent) { return (UnityEngine.Random.Range(0,100) <= percent); } /// /// Moves from "from" to "to" by the specified amount and returns the corresponding value /// /// From. /// To. /// Amount. public static float Approach(float from, float to, float amount) { if (from < to) { from += amount; if (from > to) { return to; } } else { from -= amount; if (from < to) { return to; } } return from; } /// /// Remaps a value x in interval [A,B], to the proportional value in interval [C,D] /// /// The value to remap. /// the minimum bound of interval [A,B] that contains the x value /// the maximum bound of interval [A,B] that contains the x value /// the minimum bound of target interval [C,D] /// the maximum bound of target interval [C,D] public static float Remap(float x, float A, float B, float C, float D) { float remappedValue = C + (x-A)/(B-A) * (D - C); return remappedValue; } /// /// Clamps the angle in parameters between a minimum and maximum angle (all angles expressed in degrees) /// /// /// /// /// public static float ClampAngle(float angle, float minimumAngle, float maximumAngle) { if (angle < -360) { angle += 360; } if (angle > 360) { angle -= 360; } return Mathf.Clamp(angle, minimumAngle, maximumAngle); } public static float RoundToDecimal(float value, int numberOfDecimals) { if (numberOfDecimals <= 0) { return Mathf.Round(value); } else { return Mathf.Round(value * 10f * numberOfDecimals) / (10f * numberOfDecimals); } } /// /// Rounds the value passed in parameters to the closest value in the parameter array /// /// /// /// public static float RoundToClosest(float value, float[] possibleValues, bool pickSmallestDistance = false) { if (possibleValues.Length == 0) { return 0f; } float closestValue = possibleValues[0]; foreach (float possibleValue in possibleValues) { float closestDistance = Mathf.Abs(closestValue - value); float possibleDistance = Mathf.Abs(possibleValue - value); if (closestDistance > possibleDistance) { closestValue = possibleValue; } else if (closestDistance == possibleDistance) { if ((pickSmallestDistance && closestValue > possibleValue) || (!pickSmallestDistance && closestValue < possibleValue)) { closestValue = (value < 0) ? closestValue : possibleValue; } } } return closestValue; } /// /// Returns a vector3 based on the angle in parameters /// /// /// public static Vector3 DirectionFromAngle(float angle, float additionalAngle) { angle += additionalAngle; Vector3 direction = Vector3.zero; direction.x = Mathf.Sin(angle * Mathf.Deg2Rad); direction.y = 0f; direction.z = Mathf.Cos(angle * Mathf.Deg2Rad); return direction; } /// /// Returns a vector3 based on the angle in parameters /// /// /// public static Vector3 DirectionFromAngle2D(float angle, float additionalAngle) { angle += additionalAngle; Vector3 direction = Vector3.zero; direction.x = Mathf.Cos(angle * Mathf.Deg2Rad); direction.y = Mathf.Sin(angle * Mathf.Deg2Rad); direction.z = 0f; return direction; } } }