Last active
December 5, 2023 17:42
-
-
Save Denchyaknow/138158b68dbc82619dbfcbd6ece65184 to your computer and use it in GitHub Desktop.
Magical_Extensions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections; | |
using System.Collections.Generic; | |
using UnityEngine; | |
//Made by Dencho, Just some useful extensions for Unity types, wrote them over the years for my own use, enjoi thx! | |
namespace Yakno | |
{ | |
#region Debugging | |
public static class DebugExt | |
{ | |
public static void DrawBounds(Transform _transform, Bounds _bounds, Color _color, float _duration = 0.04f) | |
{ | |
Vector3 v3Center = _transform.position + _transform.TransformDirection(_bounds.center); | |
Vector3 v3Extents = _bounds.extents;//_transform.TransformDirection(_bounds.extents); | |
Vector3 v3FrontTopLeft = new Vector3(v3Center.x - v3Extents.x, v3Center.y + v3Extents.y, v3Center.z - v3Extents.z); | |
Vector3 v3FrontTopRight = new Vector3(v3Center.x + v3Extents.x, v3Center.y + v3Extents.y, v3Center.z - v3Extents.z); | |
Vector3 v3FrontBottomLeft = new Vector3(v3Center.x - v3Extents.x, v3Center.y - v3Extents.y, v3Center.z - v3Extents.z); | |
Vector3 v3FrontBottomRight = new Vector3(v3Center.x + v3Extents.x, v3Center.y - v3Extents.y, v3Center.z - v3Extents.z); | |
Vector3 v3BackTopLeft = new Vector3(v3Center.x - v3Extents.x, v3Center.y + v3Extents.y, v3Center.z + v3Extents.z); | |
Vector3 v3BackTopRight = new Vector3(v3Center.x + v3Extents.x, v3Center.y + v3Extents.y, v3Center.z + v3Extents.z); | |
Vector3 v3BackBottomLeft = new Vector3(v3Center.x - v3Extents.x, v3Center.y - v3Extents.y, v3Center.z + v3Extents.z); | |
Vector3 v3BackBottomRight = new Vector3(v3Center.x + v3Extents.x, v3Center.y - v3Extents.y, v3Center.z + v3Extents.z); | |
Debug.DrawLine(v3FrontTopLeft, v3FrontTopRight, _color, _duration); | |
Debug.DrawLine(v3FrontTopRight, v3FrontBottomRight, _color, _duration); | |
Debug.DrawLine(v3FrontBottomRight, v3FrontBottomLeft, _color, _duration); | |
Debug.DrawLine(v3FrontBottomLeft, v3FrontTopLeft, _color, _duration); | |
Debug.DrawLine(v3BackTopLeft, v3BackTopRight, _color, _duration); | |
Debug.DrawLine(v3BackTopRight, v3BackBottomRight, _color, _duration); | |
Debug.DrawLine(v3BackBottomRight, v3BackBottomLeft, _color, _duration); | |
Debug.DrawLine(v3BackBottomLeft, v3BackTopLeft, _color, _duration); | |
Debug.DrawLine(v3FrontTopLeft, v3BackTopLeft, _color, _duration); | |
Debug.DrawLine(v3FrontTopRight, v3BackTopRight, _color, _duration); | |
Debug.DrawLine(v3FrontBottomRight, v3BackBottomRight, _color, _duration); | |
Debug.DrawLine(v3FrontBottomLeft, v3BackBottomLeft, _color, _duration); | |
} | |
} | |
/// <summary> | |
/// Useful Gizmoes extensions made by Dencho | |
/// </summary> | |
public static class GizmosExt | |
{ | |
public static void DrawAngleRange(Vector3 position_, float range_, Vector3 direction_, Vector3 aroundAxis_, float upAngle_, Vector3 upAxis_, float deg_ = 180f) | |
{ | |
float fDeg = deg_ * range_; | |
Quaternion a = Quaternion.identity; | |
Quaternion b = Quaternion.identity; | |
Quaternion aa = Quaternion.identity; | |
Quaternion bb = Quaternion.identity; | |
float aP = Mathf.Lerp(0f, fDeg, 1); | |
float bP = Mathf.Lerp(0f, -fDeg, 1); | |
var up = Quaternion.AngleAxis(upAngle_, upAxis_); | |
a = Quaternion.AngleAxis(aP, aroundAxis_); | |
b = Quaternion.AngleAxis(bP, aroundAxis_); | |
aa = a; | |
bb = b; | |
Gizmos.DrawLine(position_, position_ + up * a * direction_); | |
Gizmos.DrawLine(position_, position_ + up * b * direction_); | |
aP = Mathf.Lerp(0f, fDeg, 0); | |
bP = Mathf.Lerp(0f, -fDeg, 0); | |
aa = Quaternion.AngleAxis(aP, aroundAxis_); | |
bb = Quaternion.AngleAxis(bP, aroundAxis_); | |
for (float t = 0f; t <= 1.05f; t += 0.05f) | |
{ | |
aP = Mathf.Lerp(0f, fDeg, t); | |
bP = Mathf.Lerp(0f, -fDeg, t); | |
a = Quaternion.AngleAxis(aP, aroundAxis_); | |
b = Quaternion.AngleAxis(bP, aroundAxis_); | |
Gizmos.DrawLine(position_ + up * a * direction_, position_ + up * aa * direction_); | |
Gizmos.DrawLine(position_ + up * b * direction_, position_ + up * bb * direction_); | |
aa = a; | |
bb = b; | |
} | |
Gizmos.DrawLine(position_ + a * direction_, position_ + aa * direction_); | |
Gizmos.DrawLine(position_ + b * direction_, position_ + bb * direction_); | |
} | |
/// <summary> | |
/// If you have trouble with this, ask Dencho as he wrote this when he was haf | |
/// </summary> | |
public static void DrawAngleRange(Vector3 position_, float range_, Vector3 direction_, Vector3 aroundAxis_) | |
{ | |
float fDeg = 2 * Mathf.PI * Mathf.Rad2Deg * range_ * 0.5f; | |
Quaternion a = Quaternion.identity; | |
Quaternion b = Quaternion.identity; | |
Quaternion aa = Quaternion.identity; | |
Quaternion bb = Quaternion.identity; | |
var up = Quaternion.AngleAxis(0f, aroundAxis_); | |
float aP = Mathf.Lerp(0f, fDeg, 1); | |
float bP = Mathf.Lerp(0f, -fDeg, 1); | |
a = Quaternion.AngleAxis(aP, aroundAxis_); | |
b = Quaternion.AngleAxis(bP, aroundAxis_); | |
aa = a; | |
bb = b; | |
Gizmos.DrawLine(position_, position_ + a * direction_); | |
Gizmos.DrawLine(position_, position_ + b * direction_); | |
aP = Mathf.Lerp(0f, fDeg, 0); | |
bP = Mathf.Lerp(0f, -fDeg, 0); | |
aa = Quaternion.AngleAxis(aP, aroundAxis_); | |
bb = Quaternion.AngleAxis(bP, aroundAxis_); | |
for (float t = 0f; t <= 1.05f; t += 0.05f) | |
{ | |
aP = Mathf.Lerp(0f, fDeg, t); | |
bP = Mathf.Lerp(0f, -fDeg, t); | |
a = Quaternion.AngleAxis(aP, aroundAxis_); | |
b = Quaternion.AngleAxis(bP, aroundAxis_); | |
Gizmos.DrawLine(position_ + up * a * direction_, position_ + up * aa * direction_); | |
Gizmos.DrawLine(position_ + up * b * direction_, position_ + up * bb * direction_); | |
aa = a; | |
bb = b; | |
} | |
Gizmos.DrawLine(position_ + a * direction_, position_ + aa * direction_); | |
Gizmos.DrawLine(position_ + b * direction_, position_ + bb * direction_); | |
} | |
/// <summary> | |
/// Draw a modular Pipe, If you wanna know how to use it just read the code or ask Dencho | |
/// </summary> | |
/// <param name="origin">center lastVertexPoint</param> | |
/// <param name="aroundAxis">Axis to rotate around</param> | |
/// <param name="forwardAxis">If this forward changes then sow ill the lines</param> | |
/// <param name="innerRadius">Inner Radius of the wire circle or pipe</param> | |
/// <param name="outterRadius">Default = 0 = doesnt show</param> | |
/// <param name="height">Default = 0 = no walls</param> | |
/// <param name="resolution"></param> | |
/// <param name="drawOriginLines"></param> | |
public static void DrawWirePipe(Vector3 origin, Vector3 aroundAxis, Vector3 forwardAxis, Vector3 rightAxis, float innerRadius, float outterRadius = 0f, float height = 0f, int resolution = 5, bool drawOriginLines = false) | |
{ | |
float d = 0.001f * Mathf.PI * Mathf.PI * (Mathf.InverseLerp(0f, 100f, 100 - resolution)); | |
Vector3 a = origin + Quaternion.AngleAxis(360f, aroundAxis.normalized) * forwardAxis.normalized * outterRadius; | |
Vector3 aa = origin + Quaternion.AngleAxis(360f, aroundAxis.normalized) * forwardAxis.normalized * innerRadius; | |
Vector3 b = a; | |
Vector3 bb = aa; | |
Vector3 up = aroundAxis * height; | |
Vector3 right = rightAxis * innerRadius; | |
bool outter = outterRadius != 0f && outterRadius > innerRadius; | |
for (int i = 0; i <= 360 * 3; i += resolution) | |
{ | |
a = origin + Quaternion.AngleAxis(i, aroundAxis.normalized) * rightAxis.normalized * innerRadius; | |
Gizmos.DrawLine(a, b); | |
if (height != 0f) | |
{ | |
Gizmos.DrawLine(a + up, b + up); | |
Gizmos.DrawLine(a + up, a); | |
} | |
b = a; | |
if (outter) | |
{ | |
aa = origin + Quaternion.AngleAxis(i, aroundAxis.normalized) * forwardAxis.normalized * outterRadius; | |
Gizmos.DrawLine(aa, bb); | |
if (height != 0f) | |
{ | |
Gizmos.DrawLine(aa + up, bb + up); | |
Gizmos.DrawLine(aa + up, aa); | |
} | |
bb = aa; | |
if (drawOriginLines) | |
Gizmos.DrawLine(aa, a); | |
} | |
else | |
{ | |
} | |
} | |
a = origin + Quaternion.AngleAxis(360f, aroundAxis.normalized) * forwardAxis.normalized * outterRadius; | |
Gizmos.DrawLine(a, b); | |
if (outter) | |
{ | |
aa = origin + Quaternion.AngleAxis(360f, aroundAxis.normalized) * forwardAxis.normalized * innerRadius; | |
Gizmos.DrawLine(aa, bb); | |
} | |
} | |
/// <summary> | |
/// A function to draw a wire circle, If you wanna know how to use it just read the code or ask Dencho | |
/// </summary> | |
/// <param name="origin"></param> | |
/// <param name="radius"></param> | |
/// <param name="aroundAxis">The axis to rotate the circle around</param> | |
/// <param name="resolution">Will be clamped between 0 - 100</param> | |
public static void DrawWireCircle(Vector3 origin, float radius, Vector3 aroundAxis, Vector3 forwardAxis, int resolution = 10, bool drawOriginLines = false) | |
{ | |
int count = 0; | |
if (resolution >= 360) | |
resolution = 359; | |
float d = ((Mathf.Deg2Rad * Mathf.PI * resolution) / 360f) * Mathf.InverseLerp(0f, 360, 360 - resolution); | |
Vector3 a = origin + Quaternion.AngleAxis(360f, aroundAxis.normalized) * forwardAxis.normalized * radius; | |
Vector3 b = a; | |
for (float i = 0f; i <= 1f; i += d) | |
{ | |
a = origin + Quaternion.AngleAxis(i * 360f, aroundAxis.normalized) * forwardAxis.normalized * radius; | |
if (drawOriginLines) | |
Gizmos.DrawLine(origin, a); | |
Gizmos.DrawLine(a, b); | |
b = a; | |
count++; | |
} | |
a = origin + Quaternion.AngleAxis(360f, aroundAxis.normalized) * forwardAxis.normalized * radius; | |
Gizmos.DrawLine(a, b); | |
} | |
} | |
#endregion | |
#region Cameras and Renderers | |
/// <summary> | |
/// Useful Camera externions made by Dencho | |
/// </summary> | |
public static class CameraExt | |
{ | |
public static void FocusOnGameObject(this Camera camera, Bounds targetBounds, float height = 0.5f, float distancePadding = 0f, float angle = 0f) | |
{ | |
//var max = Mathf.Max(targetBounds.size.x, targetBounds.size.y, targetBounds.size.z); | |
//max / 2f; | |
var objectRadius = targetBounds.extents.magnitude; | |
var cameraView = 2.0f * Mathf.Tan(0.5f * Mathf.Deg2Rad * camera.fieldOfView); | |
var distance = 1f * objectRadius / cameraView; | |
distance += objectRadius; | |
Debug.Log(string.Format("radius: {0}, view: {1}, distance: {2}", objectRadius, cameraView, distance)); | |
var pos = (distance + (distance * distancePadding)) * (Quaternion.AngleAxis(angle, Vector3.up) * Vector3.forward); | |
var dir = (targetBounds.center - pos); | |
float dis = dir.magnitude; | |
dir += (Vector3.up * (height + (height * distance))); | |
var newPos = targetBounds.center - (dir.normalized * dis); | |
//(distance + (distance * distancePadding))); | |
//pos = targetBounds.center - (dir.normalized * (distance + (distance * distancePadding))); | |
camera.transform.position = newPos; | |
camera.transform.rotation = Quaternion.LookRotation(targetBounds.center - camera.transform.position); | |
} | |
public static void FocusOnGameObject(this Camera camera, GameObject targetGameObject, float height = 0.5f) | |
{ | |
var bounds = targetGameObject.GetTotalBounds(); | |
var objectRadius = bounds.extents.magnitude; | |
var cameraView = 2.0f * Mathf.Tan(0.5f * Mathf.Deg2Rad * camera.fieldOfView); | |
var distance = 1f * objectRadius / cameraView; | |
distance += objectRadius; | |
camera.transform.position = (bounds.center - (distance * Vector3.forward)) + (Vector3.up * height); | |
camera.transform.rotation = Quaternion.LookRotation(bounds.center - camera.transform.position); | |
} | |
} | |
/// <summary> | |
/// Useful Renderer extensions made by Dencho | |
/// </summary> | |
public static class RendererExt | |
{ | |
/// <summary> | |
/// Get the Max _bounds of all types of Renderers, Colliders, ect in the given GO, used for auto focusing a Camera. | |
/// </summary> | |
/// <param name="gameObject">the Go that will we AutoFocused on</param> | |
/// <returns>The _bounds to use in the AutoFocus Calculations</returns> | |
public static Bounds GetTotalBounds(this GameObject gameObject) | |
{ | |
var meshRenderers = gameObject.GetComponentsInChildren<MeshRenderer>(); | |
var skinnedMeshRenderers = gameObject.GetComponentsInChildren<SkinnedMeshRenderer>(); | |
var colliders = gameObject.GetComponentsInChildren<Collider>(); | |
var bounds = new Bounds(); | |
foreach (var meshRenderer in meshRenderers) | |
{ | |
bounds.Encapsulate(meshRenderer.bounds); | |
} | |
foreach (var skinnedMeshRenderer in skinnedMeshRenderers) | |
{ | |
bounds.Encapsulate(skinnedMeshRenderer.bounds); | |
} | |
foreach (var collider in colliders) | |
{ | |
bounds.Encapsulate(collider.bounds); | |
} | |
return bounds; | |
} | |
public static bool HasMat(this MeshRenderer renderer, Material mat) | |
{ | |
if (mat == null) | |
return false; | |
if (renderer.sharedMaterials.Length > 0) | |
{ | |
for (int i = 0; i < renderer.sharedMaterials.Length; i++) | |
{ | |
if (renderer.sharedMaterials[i].shader.GetInstanceID() == mat.shader.GetInstanceID()) | |
return true; | |
} | |
} | |
return false; | |
} | |
public static int GetMatIndex(this MeshRenderer renderer, Material mat) | |
{ | |
if (mat == null) | |
return -1; | |
if (renderer.sharedMaterials.Length > 0) | |
{ | |
for (int i = 0; i < renderer.sharedMaterials.Length; i++) | |
{ | |
if (renderer.sharedMaterials[i].shader.GetInstanceID() == mat.shader.GetInstanceID()) | |
return i; | |
} | |
} | |
return -1; | |
} | |
} | |
#endregion | |
#region Math Types | |
/// <summary> | |
/// Useful Float extensions made by Dencho | |
/// </summary> | |
public static class FloatExt | |
{ | |
/// <summary> | |
/// Get the Angle of the forward direction | |
/// </summary> | |
/// <param name="planarDir"></param> | |
/// <returns></returns> | |
public static float ToAngle(this Vector3 planarDir) | |
{ | |
return (Mathf.Atan2(planarDir.x, planarDir.z) * Mathf.Rad2Deg); | |
} | |
/// <summary> | |
/// Get the delta between 2 forward directions | |
/// </summary> | |
/// <param name="from"></param> | |
/// <param name="to"></param> | |
/// <returns></returns> | |
public static float GetDeltaForward(Vector3 from, Vector3 to) | |
{ | |
return ( | |
Mathf.DeltaAngle(Mathf.Atan2(from.z, from.x) * Mathf.Rad2Deg, Mathf.Atan2(to.z, to.x) * Mathf.Rad2Deg) | |
); | |
} | |
/// <summary> | |
/// Get the delta between 2 forward directions | |
/// </summary> | |
/// <param name="from"></param> | |
/// <param name="to"></param> | |
/// <returns></returns> | |
public static float GetDeltaForward(Vector2 from, Vector2 to) | |
{ | |
return ( | |
Mathf.DeltaAngle(Mathf.Atan2(from.x, from.y) * Mathf.Rad2Deg, Mathf.Atan2(to.x, to.y) * Mathf.Rad2Deg) | |
); | |
} | |
/// <summary>Original Float: 0.123, Converted to Short: 0.123 * 1000 = 123 </summary> | |
/// <param name="value">This float will lose some precision</param> <returns>A short ranging from -32,768 to 32,768</returns> | |
public static short ToShort(this float value, float scalingFactor = 1000f) | |
{ | |
float scaled = value * scalingFactor; | |
return (short)Mathf.Clamp(scaled, short.MinValue, short.MaxValue); | |
} | |
/// <summary>Original Float: 0.123, Back to Float: 123 / 1000 = 0.123</summary> | |
/// <param name="value">This short should not be 0</param> <returns>A <see cref="float"/> of big number</returns> | |
public static float ToFloat(this short value, float scalingFactor = 1000f) | |
{ | |
return value / scalingFactor; | |
} | |
/// <summary>Original Float: 1.23, Converted to sbyte: 1.23 * 10 = 12.3 (rounded to 12)</summary> | |
/// <param name="value">This float will lose some precision</param><returns>An <see cref="sbyte"/> ranging -128 to 127</returns> | |
public static sbyte ToSByte(this float value, float scalingFactor = 120f) | |
{ | |
float scaled = value * scalingFactor; | |
return (sbyte)Mathf.Clamp(scaled, sbyte.MinValue, sbyte.MaxValue); | |
} | |
/// <summary> Original Float: 1.23, Back to Float: 12 / 10 = 1.2 </summary> | |
/// <param name="value">This short should not be 0</param> <returns>A <see cref="float"/> of big number</returns> | |
public static float ToFloat(this sbyte value, float scalingFactor = 120f) | |
{ | |
return value / scalingFactor; | |
} | |
} | |
/// <summary> | |
/// Useful Vector3 extensions made by Dencho | |
/// </summary> | |
public static class Vector3Ext | |
{ | |
public static Vector3[] LocalPath(this Transform[] points_, ref Vector3[] outPut_) | |
{ | |
if (outPut_.Length != points_.Length) | |
outPut_ = new Vector3[points_.Length]; | |
for (int i = 0; i < points_.Length; i++) | |
{ | |
outPut_[i] = points_[i].localPosition; | |
} | |
return outPut_; | |
} | |
public static Vector3[] Path(this Transform[] points_, ref Vector3[] outPut_) | |
{ | |
if (outPut_.Length != points_.Length) | |
outPut_ = new Vector3[points_.Length]; | |
for (int i = 0; i < points_.Length; i++) | |
{ | |
outPut_[i] = points_[i].position; | |
} | |
return outPut_; | |
} | |
public static Vector3 TransformRelative(this Vector3 value_, Transform target_) | |
{ | |
return new Vector3(Vector3.Dot(target_.right, value_), Vector3.Dot(target_.right, value_), Vector3.Dot(target_.right, value_)); | |
} | |
public static void ToTransformRelative(this Vector3 output_, Transform target_) | |
{ | |
Vector3 temp = output_; | |
output_.x = Vector3.Dot(target_.right, temp); | |
output_.y = Vector3.Dot(target_.up, temp); | |
output_.z = Vector3.Dot(target_.forward, temp); | |
} | |
/// <summary> | |
/// Get the average via lerping of a Vector3 Aray relative to bias | |
/// No in the form of a Vector3! | |
/// </summary> | |
/// <param name="points">Data set of vector to iterate through</param> | |
/// <param name="avg">the resulting vector</param> | |
/// <param name="bias">Value 0 - 1 set how much to blend the vector by each iteration</param> | |
public static Vector3 LerpAverage(this List<Vector3> points, float bias, int normalCount = 0) | |
{ | |
var avg = points[0]; | |
for (int i = 1; i < (normalCount > 0 ? normalCount : points.Count); i++) | |
{ | |
avg = Vector3.Lerp(avg, points[i], bias); | |
} | |
return avg; | |
} | |
/// <summary> | |
/// Get the average via lerping of a Vector3 Aray relative to bias | |
/// </summary> | |
/// <param name="points">Data set of vector to iterate through</param> | |
/// <param name="avg">the resulting vector</param> | |
/// <param name="bias">Value 0 - 1 set how much to blend the vector by each iteration</param> | |
public static void LerpAverage(this List<Vector3> points, out Vector3 avg, float bias, int normalCount = 0) | |
{ | |
avg = points[0]; | |
for (int i = 1; i < (normalCount > 0 ? normalCount : points.Count); i++) | |
{ | |
avg = Vector3.Lerp(avg, points[i], bias); | |
} | |
} | |
/// <summary> | |
/// Get the average via Slerping of a Vector3 Aray relative to bias | |
/// </summary> | |
/// <param name="points">Data set of vector to iterate through</param> | |
/// <param name="avg">the resulting vector</param> | |
/// <param name="bias">Value 0 - 1 set how much to blend the vector by each iteration</param> | |
public static void SlerpAverage(this List<Vector3> points, out Vector3 avg, float bias) | |
{ | |
avg = points[0]; | |
for (int i = 1; i < points.Count; i++) | |
{ | |
avg = Vector3.Slerp(avg, points[i], bias); | |
} | |
} | |
/// <summary> | |
/// Flattens the y axis from one vector to 0 | |
/// </summary> | |
/// <param name="vectorToSet"></param> | |
/// <returns></returns> | |
public static Vector3 FlatY(this Vector3 vectorToSet) | |
{ | |
vectorToSet.y = 0f; | |
return vectorToSet; | |
} | |
/// <summary> | |
/// Invert the X | |
/// </summary> | |
/// <param name="vectorToSet"></param> | |
/// <returns></returns> | |
public static Vector3 FlipX(this Vector3 vectorToSet) | |
{ | |
vectorToSet.x = -vectorToSet.x; | |
return vectorToSet; | |
} | |
/// <summary> | |
/// Copys the y axis from one vector to another | |
/// </summary> | |
/// <param name="vectorToSet"></param> | |
/// <param name="vectorToCopy"></param> | |
/// <returns></returns> | |
public static Vector3 CopyY(this Vector3 vectorToSet, Vector3 vectorToCopy) | |
{ | |
vectorToSet.y = vectorToCopy.y; | |
return vectorToSet; | |
} | |
/// <summary> | |
/// Copys the y axis from one vector to another | |
/// </summary> | |
/// <param name="vectorToSet"></param> | |
/// <param name="vectorToCopy"></param> | |
/// <returns></returns> | |
public static Vector3 CopyX(this Vector3 vectorToSet, Vector3 vectorToCopy) | |
{ | |
vectorToSet.x = vectorToCopy.x; | |
return vectorToSet; | |
} | |
/// <summary> | |
/// Copies a vectors x and z axis | |
/// </summary> | |
/// <param name="vectorToSet"></param> | |
/// <param name="vectorToCopy"></param> | |
/// <returns></returns> | |
public static Vector3 CopyXZ(this Vector3 vectorToSet, Vector3 vectorToCopy) | |
{ | |
vectorToSet.x = vectorToCopy.x; | |
vectorToSet.z = vectorToCopy.z; | |
return vectorToSet; | |
} | |
/// <summary> | |
/// Just a helper method fo easy distance debugging | |
/// </summary> | |
/// <param name="fromPos"></param> | |
/// <param name="toPos"></param> | |
/// <param name="active"></param> | |
/// <returns></returns> | |
public static void DrawLine(this Vector3 fromPos, Vector3 toPos, bool active) | |
{ | |
if (active && Time.frameCount % 60 == 0) | |
{ | |
Debug.DrawLine(fromPos, toPos, Color.cyan, 0.75f, false); | |
} | |
} | |
public static void DrawLine(this Vector3 fromPos, Vector3 toPos, bool active, Color color) | |
{ | |
if (active && Time.frameCount % 60 == 0) | |
{ | |
Debug.DrawLine(fromPos, toPos, color, 0.75f, false); | |
} | |
} | |
} | |
/// <summary> | |
/// Just a class of usefull functions for Quaternions and thier evil ways | |
/// </summary> | |
public static class QuaternionExt | |
{ | |
/// <summary> | |
/// Used in <see cref="TransformOffset"/> | |
/// Slerps a current rotation of a _transform with to angle | |
/// </summary> | |
/// <param name="curForward">the current rotation of the _transform you want to rotate</param> | |
/// <param name="deltaAngle">+ or - a delta angle to add to the rotation</param> | |
/// <param name="deltaTime">Value between 0 to 1</param> | |
/// <param name="up">Default = <see cref="Vector3.up"/></param> | |
/// <returns></returns> | |
public static Quaternion SlerpToAngle(this Quaternion curForward, float deltaAngle, float deltaTime, Vector3 up) | |
{ | |
return Quaternion.Slerp(curForward, curForward * Quaternion.AngleAxis(-deltaAngle, up), deltaTime); | |
} | |
/// <summary> | |
/// Optimized Quaternion.Lerp | |
/// </summary> | |
public static Quaternion Lerp(Quaternion fromRotation, Quaternion toRotation, float weight) | |
{ | |
if (weight <= 0f) return fromRotation; | |
if (weight >= 1f) return toRotation; | |
return Quaternion.Lerp(fromRotation, toRotation, weight); | |
} | |
/// <summary> | |
/// Optimized Quaternion.Slerp | |
/// </summary> | |
public static Quaternion Slerp(Quaternion fromRotation, Quaternion toRotation, float weight) | |
{ | |
if (weight <= 0f) return fromRotation; | |
if (weight >= 1f) return toRotation; | |
return Quaternion.Slerp(fromRotation, toRotation, weight); | |
} | |
/// <summary> | |
/// Returns the rotation from identity Quaternion to "q", interpolated linearily by "weight". | |
/// </summary> | |
public static Quaternion LinearBlend(Quaternion q, float weight) | |
{ | |
if (weight <= 0f) return Quaternion.identity; | |
if (weight >= 1f) return q; | |
return Quaternion.Lerp(Quaternion.identity, q, weight); | |
} | |
/// <summary> | |
/// Returns the rotation from identity Quaternion to "q", interpolated spherically by "weight". | |
/// </summary> | |
public static Quaternion SphericalBlend(Quaternion q, float weight) | |
{ | |
if (weight <= 0f) return Quaternion.identity; | |
if (weight >= 1f) return q; | |
return Quaternion.Slerp(Quaternion.identity, q, weight); | |
} | |
/// <summary> | |
/// Creates a FromToRotation, but makes sure it's axis remains fixed near to the Quaternion singularity lastVertexPoint. | |
/// </summary> | |
/// <returns> | |
/// The from to rotation around an axis. | |
/// </returns> | |
/// <param name='fromDirection'> | |
/// From direction. | |
/// </param> | |
/// <param name='toDirection'> | |
/// To direction. | |
/// </param> | |
/// <param name='axis'> | |
/// Axis. Should be normalized before passing into this method. | |
/// </param> | |
public static Quaternion FromToAroundAxis(Vector3 fromDirection, Vector3 toDirection, Vector3 axis) | |
{ | |
Quaternion fromTo = Quaternion.FromToRotation(fromDirection, toDirection); | |
float angle = 0; | |
Vector3 freeAxis = Vector3.zero; | |
fromTo.ToAngleAxis(out angle, out freeAxis); | |
float dot = Vector3.Dot(freeAxis, axis); | |
if (dot < 0) angle = -angle; | |
return Quaternion.AngleAxis(angle, axis); | |
} | |
/// <summary> | |
/// Gets the rotation that can be used to convert a rotation from one axis space to another. | |
/// </summary> | |
public static Quaternion RotationToLocalSpace(Quaternion space, Quaternion rotation) | |
{ | |
return Quaternion.Inverse(Quaternion.Inverse(space) * rotation); | |
} | |
/// <summary> | |
/// Gets the Quaternion from rotation "from" to rotation "to". | |
/// </summary> | |
public static Quaternion FromToRotation(Quaternion from, Quaternion to) | |
{ | |
if (to == from) return Quaternion.identity; | |
return to * Quaternion.Inverse(from); | |
} | |
/// <summary> | |
/// Gets the closest direction axis to a vector. Input vector must be normalized! | |
/// </summary> | |
public static Vector3 GetAxis(Vector3 v) | |
{ | |
Vector3 closest = Vector3.right; | |
bool neg = false; | |
float x = Vector3.Dot(v, Vector3.right); | |
float maxAbsDot = Mathf.Abs(x); | |
if (x < 0f) neg = true; | |
float y = Vector3.Dot(v, Vector3.up); | |
float absDot = Mathf.Abs(y); | |
if (absDot > maxAbsDot) | |
{ | |
maxAbsDot = absDot; | |
closest = Vector3.up; | |
neg = y < 0f; | |
} | |
float z = Vector3.Dot(v, Vector3.forward); | |
absDot = Mathf.Abs(z); | |
if (absDot > maxAbsDot) | |
{ | |
closest = Vector3.forward; | |
neg = z < 0f; | |
} | |
if (neg) closest = -closest; | |
return closest; | |
} | |
/// <summary> | |
/// Clamps the rotation similar to V3Tools.ClampDirection. | |
/// </summary> | |
public static Quaternion ClampRotation(Quaternion rotation, float clampWeight, int clampSmoothing) | |
{ | |
if (clampWeight >= 1f) return Quaternion.identity; | |
if (clampWeight <= 0f) return rotation; | |
float angle = Quaternion.Angle(Quaternion.identity, rotation); | |
float dot = 1f - (angle / 180f); | |
float targetClampMlp = Mathf.Clamp(1f - ((clampWeight - dot) / (1f - dot)), 0f, 1f); | |
float clampMlp = Mathf.Clamp(dot / clampWeight, 0f, 1f); | |
// Sine smoothing iterations | |
for (int i = 0; i < clampSmoothing; i++) | |
{ | |
float sinF = clampMlp * Mathf.PI * 0.5f; | |
clampMlp = Mathf.Sin(sinF); | |
} | |
return Quaternion.Slerp(Quaternion.identity, rotation, clampMlp * targetClampMlp); | |
} | |
/// <summary> | |
/// Clamps an angular value. | |
/// </summary> | |
public static float ClampAngle(float angle, float clampWeight, int clampSmoothing) | |
{ | |
if (clampWeight >= 1f) return 0f; | |
if (clampWeight <= 0f) return angle; | |
float dot = 1f - (Mathf.Abs(angle) / 180f); | |
float targetClampMlp = Mathf.Clamp(1f - ((clampWeight - dot) / (1f - dot)), 0f, 1f); | |
float clampMlp = Mathf.Clamp(dot / clampWeight, 0f, 1f); | |
// Sine smoothing iterations | |
for (int i = 0; i < clampSmoothing; i++) | |
{ | |
float sinF = clampMlp * Mathf.PI * 0.5f; | |
clampMlp = Mathf.Sin(sinF); | |
} | |
return Mathf.Lerp(0f, angle, clampMlp * targetClampMlp); | |
} | |
/// <summary> | |
/// Used for matching the rotations of objects that have different orientations. | |
/// </summary> | |
public static Quaternion MatchRotation(Quaternion targetRotation, Vector3 targetforwardAxis, Vector3 targetUpAxis, Vector3 forwardAxis, Vector3 upAxis) | |
{ | |
Quaternion f = Quaternion.LookRotation(forwardAxis, upAxis); | |
Quaternion fTarget = Quaternion.LookRotation(targetforwardAxis, targetUpAxis); | |
Quaternion d = targetRotation * fTarget; | |
return d * Quaternion.Inverse(f); | |
} | |
/// <summary> | |
/// Converts an Euler rotation from 0 to 360 representation to -180 to 180. | |
/// </summary> | |
public static Vector3 ToBiPolar(Vector3 euler) | |
{ | |
return new Vector3(ToBiPolar(euler.x), ToBiPolar(euler.y), ToBiPolar(euler.z)); | |
} | |
/// <summary> | |
/// Converts an angular value from 0 to 360 representation to -180 to 180. | |
/// </summary> | |
public static float ToBiPolar(float angle) | |
{ | |
angle = angle % 360f; | |
if (angle >= 180f) return angle - 360f; | |
if (angle <= -180f) return angle + 360f; | |
return angle; | |
} | |
} | |
#endregion | |
#region Physics | |
/// <summary> | |
/// Useful Configurable Joint extensions made by Dencho | |
/// </summary> | |
public static class JointExt | |
{ | |
/// <summary> | |
/// Sets a joint's targetRotation to match a given local rotation. | |
/// The joint _transform's local rotation must be cached on Start and passed into this method. | |
/// </summary> | |
public static void SetTargetRotationLocal(this ConfigurableJoint joint, Quaternion targetLocalRotation, Quaternion startLocalRotation) | |
{ | |
if (joint.configuredInWorldSpace) | |
{ | |
Debug.LogError("SetTargetRotationLocal should not be used with joints that are configured in world space. For world space joints, use SetTargetRotation.", joint); | |
} | |
SetTargetRotationInternal(joint, targetLocalRotation, startLocalRotation, Space.Self); | |
} | |
/// <summary> | |
/// Sets a joint's targetRotation to match a given world rotation. | |
/// The joint _transform's world rotation must be cached on Start and passed into this method. | |
/// </summary> | |
public static void SetTargetRotation(this ConfigurableJoint joint, Quaternion targetWorldRotation, Quaternion startWorldRotation) | |
{ | |
if (!joint.configuredInWorldSpace) | |
{ | |
Debug.LogError("SetTargetRotation must be used with joints that are configured in world space. For local space joints, use SetTargetRotationLocal.", joint); | |
} | |
SetTargetRotationInternal(joint, targetWorldRotation, startWorldRotation, Space.World); | |
} | |
public static void SetTargetRotation(this ConfigurableJoint cj, Quaternion startRot, Quaternion target, Space space) | |
{ | |
Vector3 right = cj.axis; | |
Vector3 forward = Vector3.Cross(cj.axis, cj.secondaryAxis).normalized; | |
Vector3 up = Vector3.Cross(forward, right).normalized; | |
Quaternion localToJointSpace = Quaternion.LookRotation(forward, up); | |
if (space == Space.World) | |
{ | |
Quaternion worldToLocal = Quaternion.Inverse(cj.transform.parent.rotation); | |
target = worldToLocal * target; | |
} | |
cj.targetRotation = Quaternion.Inverse(localToJointSpace) * Quaternion.Inverse(target) * startRot * localToJointSpace; | |
} | |
static void SetTargetRotationInternal(ConfigurableJoint joint, Quaternion targetRotation, Quaternion startRotation, Space space) | |
{ | |
// Calculate the rotation expressed by the joint's axis and secondary axis | |
var right = joint.axis; | |
var forward = Vector3.Cross(joint.axis, joint.secondaryAxis).normalized; | |
var up = Vector3.Cross(forward, right).normalized; | |
Quaternion worldToJointSpace = Quaternion.LookRotation(forward, up); | |
// Transform into world space | |
Quaternion resultRotation = Quaternion.Inverse(worldToJointSpace); | |
// Counter-rotate and apply the new local rotation. | |
// Joint space is the inverse of world space, so we need to invert our value | |
if (space == Space.World) | |
{ | |
resultRotation *= startRotation * Quaternion.Inverse(targetRotation); | |
} | |
else | |
{ | |
resultRotation *= Quaternion.Inverse(targetRotation) * startRotation; | |
} | |
// Transform back into joint space | |
resultRotation *= worldToJointSpace; | |
// SetSize target rotation to our newly calculated rotation | |
joint.targetRotation = resultRotation; | |
} | |
/// <summary> | |
/// Adjust ConfigurableJoint settings to closely match CharacterJoint behaviour | |
/// </summary> | |
//public static void SetupAsCharacterJoint(this ConfigurableJoint joint) | |
//{ | |
// joint.xMotion = ConfigurableJointMotion.Locked; | |
// joint.yMotion = ConfigurableJointMotion.Locked; | |
// joint.zMotion = ConfigurableJointMotion.Locked; | |
// joint.angularXMotion = ConfigurableJointMotion.Limited; | |
// joint.angularYMotion = ConfigurableJointMotion.Limited; | |
// joint.angularZMotion = ConfigurableJointMotion.Limited; | |
// joint.breakForce = Mathf.Infinity; | |
// joint.breakTorque = Mathf.Infinity; | |
// joint.rotationDriveMode = RotationDriveMode.Slerp; | |
// var slerpDrive = joint.slerpDrive; | |
// slerpDrive.mode = JointDriveMode.Position; | |
// slerpDrive.maximumForce = Mathf.Infinity; | |
// joint.slerpDrive = slerpDrive; | |
//} | |
} | |
/// <summary> | |
/// Useful Extensions made by Dencho | |
/// </summary> | |
public static class PhysicsExt | |
{ | |
/// <summary> | |
/// Does a bit check to determin if the gameobject is in a certain layer | |
/// </summary> | |
public static bool IsInLayer(this GameObject other_, int layer_) | |
{ | |
return (layer_ == (layer_ | (1 << other_.layer))); | |
} | |
/// <summary> | |
/// Calculate the initial velocity of a jump based off gravity and desired maximum height attained | |
/// </summary> | |
public static float CalculateJumpSpeed(float jumpHeight, float gravity) | |
{ | |
return Mathf.Sqrt(2 * jumpHeight * gravity); | |
} | |
/// <summary> | |
/// Blends <see cref="Rigidbody.velocity"/> towards a given direction relative to set weight | |
/// </summary> | |
/// <param name="body"></param> | |
/// <param name="direction"></param> | |
/// <param name="target"></param> | |
/// <param name="weight"></param> | |
public static void LerpRigidbodyVelocityTo(this Rigidbody body, Vector3 direction, out Vector3 target, float weight) | |
{ | |
target = body.velocity; | |
target = Vector3.Lerp(target, direction, weight); | |
body.velocity = target; | |
} | |
public static void SlerpRigidbodyVelocityTo(this Rigidbody body, Vector3 direction, out Vector3 target, float weight) | |
{ | |
target = body.velocity; | |
target = Vector3.Slerp(target, direction, weight); | |
body.velocity = target; | |
} | |
public static void LerpRigidbodyAngularVelocityTo(this Rigidbody body, Vector3 direction, out Vector3 target, float weight) | |
{ | |
target = body.angularVelocity; | |
target = Vector3.Lerp(target, direction, weight); | |
body.angularVelocity = target; | |
} | |
public static void SlerpRigidbodyAngularVelocityTo(this Rigidbody body, Vector3 direction, out Vector3 target, float weight) | |
{ | |
target = body.angularVelocity; | |
target = Vector3.Slerp(target, direction, weight); | |
body.angularVelocity = target; | |
} | |
} | |
#endregion | |
#region Unity Types | |
/// <summary> | |
/// Useful Transform extensions made by Dencho | |
/// </summary> | |
public static class TransformExt | |
{ | |
/// <summary> | |
/// Say you use this like pelvisIKBone.GetRelativePositionFromRoot(rootTransform, out targetDirection); | |
/// Example of Calcs going on: pelvisToRoot = Quaternion.Inverse(ikDummyBone.rotation) * (root.position - ikDummyBone.position); | |
/// pelvisToRoot = The direction vector from root towards the pelvisIKBone | |
/// ikDummyBone = ik.references.pelvis, set to IKDumy not the ragDoll; | |
/// root = ik.references.root is always the root referenced in the ik solver; | |
/// </summary> | |
/// <param name="dumyBone">this is the bone being rotated, like the refference bone of an IK solver</param> | |
/// <param name="ikRoot">The root of the IKDummyRig(like [IKRig]) </param> | |
/// <param name="vector">The vector to set</param> | |
public static void GetRelativePositionFromRoot(this Transform dumyBone, Transform ikRoot, out Vector3 vector) | |
{ | |
vector = Quaternion.Inverse(dumyBone.rotation) * (ikRoot.position - dumyBone.position); | |
} | |
/// <summary> | |
/// Say you use this like pelvisIKBone.GetRelativePositionFromRoot(rootTransform, out targetDirection); | |
/// Example: vector = Quaternion.Inverse(dumyBone.rotation) * (dumyBone.root.position - dumyBone.position); | |
/// pelvisToRoot = The direction vector from root towards the pelvisIKBone | |
/// ikDummyBone = ik.references.pelvis, set to IKDumy not the ragDoll; | |
/// root = ik.references.root is always the root referenced in the ik solver; | |
/// </summary> | |
/// <param name="dumyBone">this is the bone being rotated, like the refference bone of an IK solver</param> | |
/// <param name="vector">The vector to set</param> | |
public static void GetRelativePositionFromRoot(this Transform dumyBone, out Vector3 vector) | |
{ | |
vector = Quaternion.Inverse(dumyBone.rotation) * (dumyBone.root.position - dumyBone.position); | |
} | |
/// <summary> | |
/// Example: quater = Quaternion.Inverse(dollBone.rotation) * ikRoot.rotation; | |
/// relativeRotationToTarget = the (inversed) dollBones rotation with an added (not inversed)ikRoot rotation | |
/// target = usually the _transform the IK is on, than the parent of the ik | |
/// </summary> | |
/// <param name="dollBone">The rag dollBoned</param> | |
/// <param name="ikRoot">The _transform that hold the IKSolver</param> | |
/// <returns></returns> | |
public static void GetRelativeRotationFromRoot(this Transform dollBone, Transform ikRoot, out Quaternion quater) | |
{ | |
quater = Quaternion.Inverse(dollBone.rotation) * ikRoot.rotation; | |
} | |
/// <summary> | |
/// Example: quater = Quaternion.Slerp(dollBone.rotation, Quaternion.Inverse(dollBone.rotation), 0.5f); | |
/// relativeRotationToTarget = the (inversed) dollBones rotation with an added (not inversed)ikRoot rotation | |
/// target = usually the _transform the IK is on, than the parent of the ik | |
/// </summary> | |
/// <param name="dollBone">The rag dollBoned</param> | |
/// <returns></returns> | |
public static void GetRelativeRotationFromRoot(this Transform dollBone, out Quaternion quater) | |
{ | |
quater = Quaternion.Slerp(dollBone.rotation, Quaternion.Inverse(dollBone.rotation), 0.5f); | |
} | |
/// <summary> | |
/// Helper Method but for RectTransforms | |
/// </summary> | |
/// <param name="transform"></param> | |
/// <param name="parent"></param> | |
public static void Reset(this RectTransform transform, Transform parent = null) | |
{ | |
if (parent != null) | |
{ | |
transform.SetParent(parent); | |
transform.anchoredPosition = Vector2.zero; | |
transform.localRotation = Quaternion.identity; | |
transform.localScale = Vector3.one; | |
} | |
else | |
{ | |
transform.localPosition = Vector3.zero; | |
transform.localRotation = Quaternion.identity; | |
transform.localScale = Vector3.one; | |
} | |
} | |
/// <summary> | |
/// Helper method to Reset Transforms to thier parent, if a parent is provided it will reparent the _transform to it before zeroing out its _transform values | |
/// </summary> | |
/// <param name="transform"></param> | |
/// <param name="parent"></param> | |
public static void Reset(this Transform transform, Transform parent = null) | |
{ | |
if (parent != null) | |
{ | |
transform.SetParent(parent); | |
transform.localPosition = Vector3.zero; | |
transform.localRotation = Quaternion.identity; | |
transform.localScale = Vector3.one; | |
} | |
else | |
{ | |
transform.localPosition = Vector3.zero; | |
transform.localRotation = Quaternion.identity; | |
transform.localScale = Vector3.one; | |
} | |
} | |
public static bool IsInLayer(this Transform transform, LayerMask layerMMask) | |
{ | |
return (layerMMask == (layerMMask | (1 << transform.gameObject.layer))); | |
} | |
} | |
/// <summary> | |
/// Useful GameObject extensions made by Dencho | |
/// </summary> | |
public static class GameObjectExt | |
{ | |
/// <summary> | |
/// Uses InstanceIDs to check if an object is the same or not | |
/// </summary> | |
/// <param name="gameObject"></param> | |
/// <param name="other"></param> | |
/// <returns></returns> | |
public static bool IsSameInstance(this GameObject gameObject, GameObject other) | |
{ | |
return gameObject.GetInstanceID() == other.GetInstanceID(); | |
} | |
/// <summary> | |
/// Helper method to check if a gameobject is included under the given <see cref="LayerMask"/> | |
/// </summary> | |
/// <param name="gameObject"></param> | |
/// <param name="layerMMask"></param> | |
/// <returns>True if gameobjects layer is in the given mask</returns> | |
public static bool IsInLayer(this GameObject gameObject, LayerMask layerMMask) | |
{ | |
return (layerMMask == (layerMMask | (1 << gameObject.layer))); | |
} | |
} | |
#endregion | |
#region Structs | |
/// <summary> | |
/// Maps a <see cref="Vector3"/> using <see cref="Mathf.InverseLerp(float, float, float)"/> | |
/// </summary> | |
[System.Serializable] | |
public struct Vector3Curve | |
{ | |
public AnimationCurve X; | |
public AnimationCurve Y; | |
public AnimationCurve Z; | |
public Vector3Curve(float min = 0f, float max = 1f) | |
{ | |
X = AnimationCurve.Linear(0f, min, 1f, max); | |
Y = AnimationCurve.Linear(0f, min, 1f, max); | |
Z = AnimationCurve.Linear(0f, min, 1f, max); | |
} | |
public Vector3Curve LinearCurve() | |
{ | |
return new Vector3Curve(0f, 1f); | |
} | |
public void Evaluate(out Vector3 output, float x = 0f, float y = 0f, float z = 0f) | |
{ | |
output.x = X.Evaluate(x); | |
output.y = Y.Evaluate(y); | |
output.z = Z.Evaluate(z); | |
} | |
public void Evaluate(Vector3 input, out Vector3 output) | |
{ | |
output.x = X.Evaluate(input.x); | |
output.y = Y.Evaluate(input.y); | |
output.z = Z.Evaluate(input.z); | |
} | |
} | |
/// <summary> | |
/// Maps a <see cref="Vector3"/> using <see cref="Mathf.InverseLerp(float, float, float)"/> | |
/// </summary> | |
[System.Serializable] | |
public struct Vector3Map | |
{ | |
public bool zero; | |
[Tooltip("The min magnitude an axis can be before its returned as 0")] | |
public Vector3 axisMin; | |
[Tooltip("The max magnitude an axis can be before its returned as 1")] | |
public Vector3 axisMax; | |
[Range(0f, 6f)] | |
[Tooltip("The weight of this whole map, 0 = all axis will be 0, remember these are multipliers, these all are")] | |
public float multiplier; | |
public Vector3 Format(Vector3 input, out Vector3 output) | |
{ | |
output.x = Mathf.InverseLerp(axisMin.x, axisMax.x, input.x); | |
output.y = Mathf.InverseLerp(axisMin.y, axisMax.y, input.y); | |
output.z = Mathf.InverseLerp(axisMin.z, axisMax.z, input.z); | |
return (output * multiplier); | |
} | |
} | |
/// <summary> | |
/// Maps a float using <see cref="Mathf.InverseLerp(float, float, float)"/> | |
/// </summary> | |
[System.Serializable] | |
public struct Vector1Map | |
{ | |
public bool zero; | |
[Tooltip("The min magnitude an axis can be before its returned as 0")] | |
public float axisMin; | |
[Tooltip("The max magnitude an axis can be before its returned as 1")] | |
public float axisMax; | |
[Range(0f, 6f)] | |
[Tooltip("The weight of this whole map, 0 = all axis will be 0, remember these are multipliers, these all are")] | |
public float multiplier; | |
public float Format(float input, out float output) | |
{ | |
output = Mathf.InverseLerp(axisMax, axisMax, input); | |
return (output * multiplier); | |
} | |
} | |
#endregion | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment