SanAndreasUnity/Assets/Scripts/Utilities/F.cs
2021-01-04 01:28:58 +01:00

864 lines
No EOL
25 KiB
C#

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;
using Object = UnityEngine.Object;
namespace SanAndreasUnity.Utilities
{
//Static class with extra functions
public static class F
{
private static Vector3[] m_fourCornersArray = new Vector3[4];
//Returns the number with the greatest absolute value
public static float MaxAbs(params float[] nums)
{
float result = 0;
for (int i = 0; i < nums.Length; i++)
{
if (Mathf.Abs(nums[i]) > Mathf.Abs(result))
{
result = nums[i];
}
}
return result;
}
//Returns the topmost parent with a certain component
public static Component GetTopmostParentComponent<T>(Transform tr) where T : Component
{
Component getting = null;
while (tr.parent != null)
{
if (tr.parent.GetComponent<T>() != null)
{
getting = tr.parent.GetComponent<T>();
}
tr = tr.parent;
}
return getting;
}
public static Mesh GetSharedMesh(this Collider col)
{
if (col is MeshCollider)
{
return ((MeshCollider)col).sharedMesh;
}
else
{
// WIP: Depending on the collider generate a diferent shape
MeshFilter f = col.gameObject.GetComponent<MeshFilter>();
return f != null ? f.sharedMesh : null;
}
}
public static Vector3 ClosestPointOrBoundsCenter(this Collider collider, Vector3 position)
{
if (collider is MeshCollider && !((MeshCollider)collider).convex)
{
// ClosestPoint() function is not supported on non-convex mesh colliders
return collider.bounds.center;
}
return collider.ClosestPoint(position);
}
public static bool BetweenInclusive(this float v, float min, float max)
{
return v >= min && v <= max;
}
public static bool BetweenExclusive(this float v, float min, float max)
{
return v > min && v < max;
}
public static int RoundToInt(this float f)
{
return Mathf.RoundToInt (f);
}
public static float SqrtOrZero(this float f)
{
if (float.IsNaN(f))
return 0f;
if (f <= 0f)
return 0f;
return Mathf.Sqrt(f);
}
public static double DateTimeToUnixTimestamp(this DateTime dateTime)
{
return (TimeZoneInfo.ConvertTimeToUtc(dateTime) -
new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds;
}
public static string CurrentDateForLogging
{
get
{
try {
return System.DateTime.Now.ToString("dd MMM HH:mm:ss");
} catch {
return "";
}
}
}
public static string Nl2Br(this string str)
{
return str.Replace(Environment.NewLine, "<br>");
}
public static string OptimizeHTML(this string str)
{
return str.Replace("<", "&lt;").Replace(">", "&gt;").Nl2Br().Replace("<br>", "[br]");
}
public static string CleanElement(this string html, string element)
{
string orEl = string.Format(@"\[{0}\]", element);
return Regex.Replace(html, orEl, (m) => { return Callback(m, element, html); });
}
private static string Callback(Match match, string element, string html)
{
int oc = FindOccurrences(html.Substring(0, match.Index + 1), match.Index);
string befChar = html.Substring(match.Index - (element.Length + 3), 1);
string sep = new string(Convert.ToChar(9), oc);
return string.Format("{3}<{0}>{1}{2}", element, Environment.NewLine, sep, befChar.Replace(Environment.NewLine, " ").IsNullOrWhiteSpace() ? sep : "");
}
private static int FindOccurrences(string str, int maxIndex)
{
int lio = str.LastIndexOf("</");
//Debug.LogFormat("LastIndexOf: {0}\nMatchIndex: {1}\nLength: {2}", lio, maxIndex, str.Length);
return Regex.Matches(str.Substring(lio, maxIndex - lio), @"<\w+").Count;
}
public static bool IsNullOrWhiteSpace(this string value)
{
if (value == null) return true;
return string.IsNullOrEmpty(value.Trim());
}
public static void Clear(this StringBuilder value)
{
value.Length = 0;
value.Capacity = 0;
}
public static String NameOf<T>(this T obj)
{
return typeof(T).ToString();
}
public static T GetComponentWithName<T>(this Component root, string name) where T : Component
{
return root.GetComponentsInChildren<T>().FirstOrDefault(x => x.name == name);
}
public static T GetOrAddComponent<T> (this GameObject go) where T : Component
{
T comp = go.GetComponent<T> ();
if (null == comp)
comp = go.AddComponent<T> ();
return comp;
}
public static T GetComponentOrThrow<T> (this GameObject go) where T : Component
{
T comp = go.GetComponent<T> ();
if (null == comp)
throw new MissingComponentException (string.Format ("Failed to get component of type: {0}, on game object: {1}", typeof(T), go.name));
return comp;
}
public static T GetComponentOrThrow<T> (this Component comp) where T : Component
{
return comp.gameObject.GetComponentOrThrow<T> ();
}
public static T GetComponentOrLogError<T> (this GameObject go) where T : Component
{
T comp = go.GetComponent<T> ();
if (null == comp)
Debug.LogErrorFormat ("Failed to get component of type: {0}, on game object: {1}", typeof(T), go.name);
return comp;
}
public static T GetComponentOrLogError<T> (this Component comp) where T : Component
{
return comp.gameObject.GetComponentOrLogError<T> ();
}
public static void DestroyComponent<T> (this GameObject go) where T : Component
{
T comp = go.GetComponent<T> ();
if (comp != null)
UnityEngine.Object.Destroy(comp);
}
public static void MakeChild(this Transform parent, GameObject[] children)
{
MakeChild(parent, children, null);
}
//Make the game objects children of the parent.
public static void MakeChild(this Transform parent, GameObject[] children, Action<Transform, GameObject> actionPerLoop)
{
foreach (GameObject child in children)
{
child.transform.parent = parent;
if (actionPerLoop != null) actionPerLoop(parent, child);
}
}
public static IEnumerable<Transform> GetAllParents(this Transform tr)
{
Transform currentParent = tr.parent;
while (currentParent != null)
{
yield return currentParent;
currentParent = currentParent.parent;
}
}
public static bool IsParentOf(this Transform tr, Transform child)
{
return child.GetAllParents().Any(p => p == tr);
}
public static IEnumerable<Transform> GetFirstLevelChildren(this Transform tr)
{
for (int i = 0; i < tr.childCount; i++)
{
yield return tr.GetChild(i);
}
}
public static IEnumerable<T> GetFirstLevelChildrenComponents<T>(this GameObject go) where T : Component
{
return go.transform.GetFirstLevelChildren().SelectMany(c => c.GetComponents<T>());
}
public static void SetY(this Transform t, float yPos) {
Vector3 pos = t.position;
pos.y = yPos;
t.position = pos;
}
public static float Distance(this Transform t, Vector3 pos)
{
return Vector3.Distance (t.position, pos);
}
public static Quaternion CreateRotationAroundAxes (Vector3 degrees)
{
Quaternion rotation = Quaternion.identity;
if (degrees.x != 0)
rotation *= Quaternion.AngleAxis (degrees.x, Vector3.right);
if (degrees.y != 0)
rotation *= Quaternion.AngleAxis (degrees.y, Vector3.up);
if (degrees.z != 0)
rotation *= Quaternion.AngleAxis (degrees.z, Vector3.forward);
return rotation;
}
public static Vector3 TransformDirection (this Quaternion rot, Vector3 dir)
{
return rot * dir;
}
/// <summary>
/// Transforms the rotation from local space to world space.
/// </summary>
public static Quaternion TransformRotation (this Transform tr, Quaternion rot)
{
Vector3 localForward = rot * Vector3.forward;
Vector3 localUp = rot * Vector3.up;
return Quaternion.LookRotation (tr.TransformDirection (localForward), tr.TransformDirection (localUp));
}
public static void SetGlobalScale (this Transform tr, Vector3 globalScale)
{
Vector3 parentGlobalScale = tr.parent != null ? tr.parent.lossyScale : Vector3.one;
tr.localScale = Vector3.Scale (globalScale, parentGlobalScale.Inverted () );
}
public static Vector3 ClampDirection (Vector3 dir, Vector3 referenceVec, float maxAngle)
{
float angle = Vector3.Angle (dir, referenceVec);
if (angle > maxAngle) {
// needs to be clamped
return Vector3.RotateTowards( dir, referenceVec, (angle - maxAngle) * Mathf.Deg2Rad, 0f );
// Vector3.Lerp( dir, referenceVec, );
}
return dir;
}
public static void EnableRigidBody(Rigidbody rb)
{
rb.isKinematic = false;
rb.detectCollisions = true;
}
public static void DisableRigidBody(Rigidbody rb)
{
rb.isKinematic = true;
rb.detectCollisions = false;
}
public static object FromHex(this string hexString, Type type, CultureInfo info)
{
var argTypes = new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider) };
var convert = type.GetMethod("Parse",
BindingFlags.Static | BindingFlags.Public,
null, argTypes, null);
return convert.Invoke(null, new object[] { hexString, NumberStyles.HexNumber, info });
}
public static void SafeDestroy<T>(this T obj) where T : Object
{
if (Application.isEditor)
Object.DestroyImmediate(obj);
else
Object.Destroy(obj);
}
public static void SafeDestroyGameObject<T>(this T component) where T : Component
{
if (component != null)
SafeDestroy(component.gameObject);
}
public static string FirstCharToUpper(this string input)
{
switch (input)
{
case null: throw new ArgumentNullException(nameof(input));
case "": throw new ArgumentException($"{nameof(input)} cannot be empty", nameof(input));
default: return input.First().ToString().ToUpper() + input.Substring(1);
}
}
public static string GetGameObjectPath(this GameObject obj)
{
string path = obj.name;
while (obj.transform.parent != null)
{
obj = obj.transform.parent.gameObject;
path = "/" + obj.name + path;
}
return path;
}
public static float GetTimePerc (this AnimationState state)
{
return state.time / state.length;
}
public static void SetTimePerc (this AnimationState state, float perc)
{
state.time = state.length * perc;
}
public static void RunExceptionSafe (System.Action function)
{
try {
function();
} catch(System.Exception ex) {
try {
Debug.LogException (ex);
} catch {}
}
}
public static void Invoke( this System.Object obj, string methodName, params object[] args ) {
var method = obj.GetType().GetMethod( methodName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public );
if(method != null) {
method.Invoke( obj, args );
}
}
public static void InvokeExceptionSafe( this System.Object obj, string methodName, params object[] args ) {
try {
obj.Invoke( methodName, args );
} catch (System.Exception ex) {
Debug.LogException (ex);
}
}
/// <summary>
/// Invokes all subscribed delegates, and makes sure any exception is caught and logged, so that all
/// subscribers will get notified.
/// </summary>
public static void InvokeEventExceptionSafe( MulticastDelegate eventDelegate, params object[] parameters )
{
RunExceptionSafe( () => {
var delegates = eventDelegate.GetInvocationList ();
foreach (var del in delegates) {
if (del.Method != null) {
try {
del.Method.Invoke (del.Target, parameters);
} catch(System.Exception ex) {
UnityEngine.Debug.LogException (ex);
}
}
}
});
}
public static void SendMessageToObjectsOfType<T> (string msg, params object[] args) where T : UnityEngine.Object
{
var objects = UnityEngine.Object.FindObjectsOfType<T> ();
foreach (var obj in objects) {
obj.InvokeExceptionSafe (msg, args);
}
}
public static bool IsCasteable<T>(this object input)
{
try
{
Convert.ChangeType(input, typeof(T));
return true;
}
catch
{
return false;
}
}
public static T[] AddValue<T>(this T[] arr, T value)
{
return (new List<T>(arr.ToList()) { value }).ToArray();
}
public static T[] Add<T>(this T[] target, T item)
{
if (target == null)
throw new ArgumentNullException();
T[] result = new T[target.Length + 1];
target.CopyTo(result, 0);
result[target.Length] = item;
return result;
}
public static TNew[] ConvertArray<TNew, TOld>(TOld[] array, Func<TOld, TNew> selector)
{
int length = array.Length;
TNew[] newArray = new TNew[length];
for (int i = 0; i < length; i++)
{
newArray[i] = selector(array[i]);
}
return newArray;
}
public static IEnumerable<T> DistinctBy<T,T2>(this IEnumerable<T> enumerable, System.Func<T,T2> selector)
{
List<KeyValuePair<T,T2>> list = new List<KeyValuePair<T, T2>>();
foreach (var elem in enumerable) {
var value = selector (elem);
if (!list.Exists (item => item.Value.Equals (value)))
list.Add (new KeyValuePair<T, T2> (elem, value));
}
return list.Select (item => item.Key);
}
public static int FindIndex<T> (this IEnumerable<T> enumerable, System.Predicate<T> predicate)
{
int i = 0;
foreach (var elem in enumerable) {
if (predicate (elem))
return i;
i++;
}
return -1;
}
public static int IndexOf<T> (this IEnumerable<T> enumerable, T value)
{
return enumerable.FindIndex (elem => elem.Equals (value));
}
public static bool AddIfNotPresent<T> (this List<T> list, T item)
{
if (!list.Contains (item)) {
list.Add (item);
return true;
}
return false;
}
public static T RandomElement<T> (this IList<T> list)
{
if (list.Count < 1)
throw new System.InvalidOperationException("List has no elements");
return list [UnityEngine.Random.Range(0, list.Count)];
}
public static int RemoveDeadObjects<T> (this List<T> list) where T : UnityEngine.Object
{
return list.RemoveAll(item => null == item);
}
private static Dictionary<string, Texture2D> Texturemap = new Dictionary<string, Texture2D>();
private static Texture2D Font;
public static Color[] WriteLetterToTexture(this char chipName, int fontWidth = 12, int fontHeight = 18)
{
if (Font == null)
{
Debug.Log("Loaded chipfont!");
Font = Resources.Load<Texture2D>("Textures/chipfont");
}
// Copy each letter to the texture
int cur_id = (int)(chipName - '0');
return Font.GetPixels(cur_id * fontWidth, 0, fontWidth, fontHeight);
}
// WIP: Vector2? offset = null
public static Texture2D WriteTextToTexture(this string chipName, Texture2D texture, int fontWidth = 12, int fontHeight = 18)
{
if (Font == null)
Font = Resources.Load<Texture2D>("Textures/chipfont");
// If texture already exists, don't create it again
int offset = 5;
int offset_y = Font.height;
// Copy each letter to the texture
for (int i = 0; i < chipName.Length; i++)
{
int cur_id = (int)(chipName[i] - '0');
for (int y = 0; y < fontHeight; y++)
{
for (int x = 0; x < fontWidth; x++)
{
Color tempColor = Font.GetPixel(cur_id * fontWidth + x, offset_y - y);
texture.SetPixel(offset + x, offset_y - y + 10, tempColor);
}
}
offset += fontWidth;
}
texture.Apply();
return texture;
}
public static Texture2D TextToTexture(this string chipName, int fontWidth = 12, int fontHeight = 18)
{
if (Font == null)
Font = Resources.Load<Texture2D>("Textures/chipfont");
// If texture already exists, don't create it again
if (!Texturemap.ContainsKey(chipName))
{
int textureWidth = 100;
// Generate the texture
var texture = new Texture2D(textureWidth, 100, TextureFormat.ARGB32, false);
for (int y = 0; y < 100; y++)
{
for (int x = 0; x < textureWidth; x++)
{
texture.SetPixel(x, y, Color.clear);
}
}
int offset = 5;
int offset_y = Font.height;
// Copy each letter to the texture
for (int i = 0; i < chipName.Length; i++)
{
int cur_id = (int)(chipName[i] - '0');
for (int y = 0; y < fontHeight; y++)
{
for (int x = 0; x < fontWidth; x++)
{
Color tempColor = Font.GetPixel(cur_id * fontWidth + x, offset_y - y);
texture.SetPixel(offset + x, offset_y - y + 10, tempColor);
}
}
offset += fontWidth;
}
// Apply all SetPixel calls
texture.Apply();
Texturemap[chipName] = texture;
}
return Texturemap[chipName];
}
public static Vector2 GetSize (this Texture2D tex)
{
return new Vector2 (tex.width, tex.height);
}
// Slow method
public static int CountObjectsInLayer(int layer)
{
int i = 0;
foreach (Transform t in Object.FindObjectsOfType<Transform>())
if (t.gameObject.layer == layer)
++i;
return i;
}
public static bool IsGreaterOrEqual(this Vector2 local, Vector2 other)
{
if (local.x >= other.x && local.y >= other.y)
return true;
else
return false;
}
public static bool IsLesserOrEqual(this Vector2 local, Vector2 other)
{
if (local.x <= other.x && local.y <= other.y)
return true;
else
return false;
}
public static bool IsGreater(this Vector2 local, Vector2 other, bool orOperator = false)
{
if (orOperator)
{
if (local.x > other.x || local.y > other.y)
return true;
else
return false;
}
else
{
if (local.x > other.x && local.y > other.y)
return true;
else
return false;
}
}
public static bool IsLesser(this Vector2 local, Vector2 other, bool orOperator = false)
{
if (orOperator)
{
if (local.x < other.x || local.y < other.y)
return true;
else
return false;
}
else
{
if (local.x < other.x && local.y < other.y)
return true;
else
return false;
}
}
public static Vector2 ToVec2WithXAndZ( this Vector3 vec3 ) {
return new Vector2 (vec3.x, vec3.z);
}
public static Vector3 WithXAndZ( this Vector3 vec3 ) {
return new Vector3 (vec3.x, 0f, vec3.z);
}
public static Vector3 WithY( this Vector3 vec3, float yValue ) {
return new Vector3 (vec3.x, yValue, vec3.z);
}
public static Vector3 Inverted (this Vector3 vec3)
{
return new Vector3 (1.0f / vec3.x, 1.0f / vec3.y, 1.0f / vec3.z);
}
public static Color OrangeColor { get { return Color.Lerp (Color.yellow, Color.red, 0.5f); } }
/// <summary>
/// Clamps all coordinates between 0 and 1.
/// </summary>
public static Rect Clamp01(this Rect rect) {
float xMin = rect.xMin;
float xMax = rect.xMax;
float yMin = rect.yMin;
float yMax = rect.yMax;
xMin = Mathf.Clamp01 (xMin);
xMax = Mathf.Clamp01 (xMax);
yMin = Mathf.Clamp01 (yMin);
yMax = Mathf.Clamp01 (yMax);
return new Rect(xMin, yMin, xMax - xMin, yMax - yMin);
}
public static bool Contains(this Rect rect, Rect other) {
return rect.xMax >= other.xMax && rect.xMin <= other.xMin && rect.yMax >= other.yMax && rect.yMin <= other.yMin;
}
public static Rect Intersection(this Rect rect, Rect other) {
float xMax = Mathf.Min (rect.xMax, other.xMax);
float yMax = Mathf.Min (rect.yMax, other.yMax);
float xMin = Mathf.Max (rect.xMin, other.xMin);
float yMin = Mathf.Max (rect.yMin, other.yMin);
return new Rect(xMin, yMin, xMax - xMin, yMax - yMin);
}
public static Rect Normalized(this Rect rect, Rect outter) {
float xMin = (rect.xMin - outter.xMin) / outter.width;
float xMax = (rect.xMax - outter.xMin) / outter.width;
float yMin = (rect.yMin - outter.yMin) / outter.height;
float yMax = (rect.yMax - outter.yMin) / outter.height;
return new Rect(xMin, yMin, xMax - xMin, yMax - yMin);
}
public static Rect CreateRect(Vector2 center, Vector2 size) {
return new Rect (center - size / 2.0f, size);
}
public static Rect GetRect (this RectTransform rectTransform)
{
Vector3[] localCorners = m_fourCornersArray;
rectTransform.GetLocalCorners (localCorners);
float xMin = float.PositiveInfinity, yMin = float.PositiveInfinity;
float xMax = float.NegativeInfinity, yMax = float.NegativeInfinity;
for (int i = 0; i < localCorners.Length; i++) {
Vector3 corner = localCorners [i];
if (corner.x < xMin)
xMin = corner.x;
else if (corner.x > xMax)
xMax = corner.x;
if (corner.y < yMin)
yMin = corner.y;
else if (corner.y > yMax)
yMax = corner.y;
}
return new Rect (xMin, yMin, xMax - xMin, yMax - yMin);
}
public static Texture2D CreateTexture (int width, int height, Color color) {
Color[] pixels = new Color[width * height];
for (int i = 0; i < pixels.Length; i++)
pixels [i] = color;
Texture2D texture = new Texture2D (width, height);
texture.SetPixels (pixels);
texture.Apply ();
return texture;
}
public static Ray GetRayFromCenter (this Camera cam)
{
Vector3 viewportPos = new Vector3 (0.5f, 0.5f, 0f);
return cam.ViewportPointToRay (viewportPos);
}
public static Camera FindMainCameraEvenIfDisabled()
{
GameObject camObj = GameObject.FindGameObjectWithTag("MainCamera");
if (camObj != null)
return camObj.GetComponentInChildren<Camera>(true);
return null;
}
public static void GizmosDrawLineFromCamera ()
{
if (null == Camera.main)
return;
Ray ray = Camera.main.GetRayFromCenter ();
Gizmos.DrawLine (ray.origin, ray.origin + ray.direction * Camera.main.farClipPlane);
}
public static void HandlesDrawText(Vector3 pos, string text, Color color)
{
#if UNITY_EDITOR
UnityEditor.Handles.color = color;
UnityEditor.Handles.Label(pos, text);
#endif
}
public static bool ScreenHasHighDensity => Application.isMobilePlatform;
public static int GetAudioClipSizeInBytes(AudioClip clip)
{
return clip.samples * sizeof(float);
}
}
}