BITKit/Src/Unity/Scripts/Utility/Extensions.cs

794 lines
28 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using System.Linq;
using System.Reflection;
using System;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Newtonsoft.Json.Linq;
using System.Text;
using Unity.Mathematics;
using UnityEngine.UIElements;
using Object = UnityEngine.Object;
namespace BITKit
{
public static partial class MathQ
{
public static bool IsNaN(this Quaternion self)
{
return float.IsNaN(self.x) || float.IsNaN(self.y) || float.IsNaN(self.z) || float.IsNaN(self.w);
}
public static Quaternion AlignRotation(Quaternion rotation, float angleIncrement)
{
var eulerAngles = rotation.eulerAngles;
eulerAngles.x = Mathf.Round(eulerAngles.x / angleIncrement) * angleIncrement;
eulerAngles.y = Mathf.Round(eulerAngles.y / angleIncrement) * angleIncrement;
eulerAngles.z = Mathf.Round(eulerAngles.z / angleIncrement) * angleIncrement;
return Quaternion.Euler(eulerAngles);
}
}
public static partial class MathV
{
public static Matrix4x4 Rotate(Transform self, Vector3 center, Vector3 euler)
{
// 计算物体与中心点的相对位置
Vector3 relativePosition = self.position - center;
// 使用欧拉角来计算旋转矩阵
Quaternion rotation = Quaternion.Euler(euler);
// 计算旋转后的相对位置
Vector3 rotatedPosition = rotation * relativePosition;
var newPosition = center + rotatedPosition;
var newRotation = rotation * self.rotation;
return Matrix4x4.TRS(newPosition, newRotation, Vector3.one);
}
public static Vector3 CalculateTorque(Transform transform,Quaternion targetRotation)
{
var deltaRot = targetRotation * Quaternion.Inverse(transform.rotation);
deltaRot.ToAngleAxis(out var angle, out var axis);
var angleDelta = axis * (angle * Mathf.Deg2Rad);
angleDelta.x = MathV.TransientRotationAxis(angleDelta.x);
var torqueLocal = transform.InverseTransformDirection(angleDelta);
// torqueLocal = rigidbody.inertiaTensorRotation * torqueLocal;
// torqueLocal.Scale(rigidbody.inertiaTensor);
// torqueLocal = Quaternion.Inverse(rigidbody.inertiaTensorRotation) * torqueLocal;
var torque = transform.TransformDirection(torqueLocal);
var minTorque = MathV.TransientRotationAxis(torque);
return new Vector3()
{
x = Mathf.Min(torque.x, minTorque.x),
y = Mathf.Min(torque.y, minTorque.y),
z = Mathf.Min(torque.z, minTorque.z),
};
}
public static Vector3 Multiply(params Vector3[] vectors)
{
var result = Vector3.one;
foreach (var x in vectors)
{
result.x *= x.x;
result.y *= x.y;
result.z *= x.z;
}
return result;
}
public static bool InFovRange(Vector3 selfPosition,Vector3 selfForward, Vector3 target, float fov)
{
var direction = target - selfPosition;
var angle = Vector3.Angle(direction, selfForward);
return angle < fov;
}
public static bool IsForward(Vector3 selfPosition, Vector3 selfForward,Vector3 targetPos)
{
// Get the direction from referencePos to targetPos
Vector3 directionToTarget = (targetPos - selfPosition).normalized;
// Calculate the dot product
float dotProduct = Vector3.Dot(directionToTarget, selfForward);
// If dot product is greater than 0, targetPos is in front of referencePos
return dotProduct > 0;
}
// 对于 Vector3
public static Vector3 AlignRotation(Vector3 eulerAngles, float angleIncrement)
{
eulerAngles.x = Mathf.Round(eulerAngles.x / angleIncrement) * angleIncrement;
eulerAngles.y = Mathf.Round(eulerAngles.y / angleIncrement) * angleIncrement;
eulerAngles.z = Mathf.Round(eulerAngles.z / angleIncrement) * angleIncrement;
return eulerAngles;
}
public static Vector3 AlignToGrid(Vector3 position, float gridSize)
{
var x = Mathf.Round(position.x / gridSize) * gridSize;
var y = Mathf.Round(position.y / gridSize) * gridSize;
var z = Mathf.Round(position.z / gridSize) * gridSize;
return new Vector3(x, y, z);
}
private static float CalculateMaxSpeed(this Vector2 direction)
{
var angle = Vector2.Angle(Vector2.right, direction);
var normalizedAngle = angle / 45f; // 根据圆的角度计算速度倍数
var maxSpeed = Mathf.Lerp(1f, 0.77f, normalizedAngle);
return maxSpeed;
}
public static bool IsNaN(this Vector3 self)
{
return float.IsNaN(self.x) || float.IsNaN(self.y) || float.IsNaN(self.z);
}
public static Vector2 Fix(this Vector2 self, int length = 1024)
{
if (MathB.IsNormal(self.x, length) is false)
{
self.x = 0;
}
if (MathB.IsNormal(self.y, length) is false)
{
self.y = 0;
}
return self;
}
public static Vector3 Fix(this Vector3 self, int length = 1024)
{
if (MathB.IsNormal(self.x, length) is false)
{
self.x = 0;
}
if (MathB.IsNormal(self.y, length) is false)
{
self.y = 0;
}
if (MathB.IsNormal(self.z, length) is false)
{
self.z = 0;
}
return self;
}
public static bool Parallel(Vector2 vectorX, Vector2 vectorY)
{
return (vectorX, vectorY) switch
{
var (x, y) when (x.x > 0 && y.x > 0 && x.y > 0 && y.y > 0) => true,
var (x, y) when (x.x < 0 && y.x < 0 && x.y < 0 && y.y < 0) => true,
_ => false,
};
}
public static bool Approximately(Vector2 vectorX, Vector2 vectorY, float factor = 8)
{
/* var direction = vectorX - vectorY;
return direction.sqrMagnitude < factor; */
return Vector3.Angle(vectorX, vectorY) < 8;
}
public static Vector2 TransientRotationAxis(Vector2 transientAxis)
{
transientAxis.x = TransientRotationAxis(transientAxis.x);
transientAxis.y = TransientRotationAxis(transientAxis.y);
return transientAxis;
}
public static Vector3 TransientRotationAxis(Vector3 transientAxis)
{
transientAxis.x = TransientRotationAxis(transientAxis.x);
transientAxis.y = TransientRotationAxis(transientAxis.y);
transientAxis.z = TransientRotationAxis(transientAxis.z);
return transientAxis;
}
public static Vector3Int Align(this Vector3 self, int snapSize = 64)
{
Vector3Int result = new()
{
x = GetAlign(self.x),
y = GetAlign(self.y),
z = GetAlign(self.z),
};
return result * snapSize;
int GetAlign(float _self)
{
var size = (int)_self / snapSize;
var remainder = _self % snapSize;
return remainder > snapSize / 2 ? size + 1 : size;
}
}
public static float GetLength(this Vector2 self)
{
return math.max(math.abs(self.x), math.abs(self.y));
}
public static float GetLength(this Vector3 self)
{
return math.max(math.abs(self.x),math.max(math.abs(self.y), math.abs(self.z)));
}
public static bool InRange(this Vector2Int self, Vector2Int other)
{
return self is { x: >= 0, y: >= 0 }
&& other is { x: >= 0, y: >= 0 }
&& other.x <= self.x
&& other.y <= self.y
&& (self + other).x >= 0
&& (self + other).y >= 0;
}
public static float TransientRotationAxis(this float transientAxis)
{
return (transientAxis %= 360f) switch
{
> 180f => transientAxis - 360f,
< -180f => transientAxis + 360f,
_ => transientAxis,
};
}
public static float WrapAngle(float angle)
{
angle %= 360f;
if (angle > 180f)
return angle - 360f;
return angle;
}
public static Vector3 WrapAngle(Vector3 vector)
{
return new Vector3
{
x = WrapAngle(vector.x),
y = WrapAngle(vector.y),
z = WrapAngle(vector.z),
};
}
private static float UnwrapAngle(float angle)
{
if (angle >= 0)
return angle;
angle = -angle % 360;
return 360 - angle;
}
public static Vector2Int ToVector2Int(this Vector2 self)
{
return new Vector2Int
{
x = (int)self.x,
y = (int)self.y,
};
}
}
public static partial class Utility
{
public static bool Includes(this LayerMask mask, int layer)
{
return (mask.value & 1 << layer) > 0;
}
public static byte[] ToByte(this string self)
{
return System.Text.Encoding.UTF8.GetBytes(self);
}
public static string Padding(this string self, int length = 16, char c = ' ')
{
Encoding coding = Encoding.GetEncoding("gb2312");
int dcount = 0;
foreach (char ch in self.ToCharArray())
{
if (coding.GetByteCount(ch.ToString()) == 2)
dcount++;
}
string w = self.PadRight(length - dcount, c);
return w;
}
public static bool IsDefault<T>(this T self) where T : IEquatable<T>
{
return self.Equals(default);
}
public static float Random(this float self) => UnityEngine.Random.Range(-self, self);
public static int Random(this int self) => UnityEngine.Random.Range(-self, self);
public static T Random<T>(this IEnumerable<T> e)
{
return e.ElementAt(UnityEngine.Random.Range(0, e.Count()));
}
public static T Random<T>(this T[] self)
{
if (self is null || self.Length is 0) return default;
return self[UnityEngine.Random.Range(0, self.Length)];
}
public static bool IsNullOrEmpty(this string self)
{
return String.IsNullOrWhiteSpace(self);
}
public static bool IsValid(this string self)
{
return !self.IsNullOrEmpty();
}
public static UnityEvent AddListener(this UnityEvent unityEvent, UnityAction call)
{
unityEvent.AddListener(call);
return unityEvent;
}
public static string Combine(this string[] self)
{
if (self.IsNull())
{
return string.Empty;
}
StringBuilder stringBuilder = new();
self.ForEach(x =>
{
stringBuilder.Append(x);
});
return stringBuilder.ToString();
}
public static string Combine(this IEnumerable<string> self, bool split = false)
{
StringBuilder stringBuilder = new StringBuilder();
if (self.IsNull())
{
return string.Empty;
}
self.ForEach(x =>
{
stringBuilder.Append(x);
stringBuilder.Append(" ");
if (split)
{
stringBuilder.Append("\n");
}
});
return stringBuilder.ToString();
}
public static float ToFloat(this string self)
{
if (string.IsNullOrEmpty(self)) return default;
return float.Parse(self);
}
public static Vector3 MatchTarget(this ref Vector3 self, Vector3 pos, float normalizedTime, float start, float end)
{
if (normalizedTime >= start && normalizedTime <= end)
{
float process = normalizedTime / (end - start);
Vector3 direction = pos - self;
return Vector3.Lerp(default, direction, process);
}
return default;
}
public static Dictionary<string, string> ToDictionary(this string jsonSrting, params string[] removes)
{
removes.ForEach(x =>
{
jsonSrting = jsonSrting.Replace(x, string.Empty);
});
var jsonObject = JObject.Parse(jsonSrting);
var jTokens = jsonObject.Descendants().Where(p => !p.Any());
var tmpKeys = jTokens.Aggregate(new Dictionary<string, string>(),
(properties, jToken) =>
{
properties.Add(jToken.Path, jToken.ToString());
return properties;
});
return tmpKeys;
}
public static Dictionary<string, string> JsonToDictionary(this string jsonSrting, params string[] replace)
{
replace.ForEach(x =>
{
jsonSrting = jsonSrting.Replace(x, string.Empty);
});
Dictionary<string, string> dic = JsonConvert.DeserializeObject<Dictionary<string, string>>(jsonSrting);
return dic;
}
public static string DeleteLines(string text, int lineCount)
{
while (text.Split('\n').Length > lineCount)
text = text.Remove(0, text.Split('\n')[0].Length + 1);
return text;
}
public static float GetDifference(this IEnumerable<float> self)
{
return self.Max() - self.Min();
}
public static void Toggle(this Behaviour self)
{
self.enabled = !self.enabled;
}
public static void SetEnabled(this UnityEngine.Behaviour self, bool value)
{
if (self)
{
self.enabled = value;
}
}
public static T Or<T>(params T[] ts)
{
for (int i = 0; i < ts.Length; i++)
{
var t = ts[i];
if (t != null && t.Equals(default))
{
}
else
{
return t;
}
}
return default;
}
public static bool TryGetComponentAny<T>(this Component self, out T component)
{
component = self.GetComponentInChildren<T>(true);
if (component is not null)
{
return true;
}
component = self.GetComponentInParent<T>(true);
if (component is not null)
{
return true;
}
component = self.GetComponent<T>();
return component is not null;
}
public static bool TryGetFirstOrDefault<T>(this IEnumerable<T> self, out T value)
{
value = default;
if (self.IsValid())
{
value = self.ElementAt(0);
return true;
}
return default;
}
public static TValue GetOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> self, TKey key)
{
TValue value = default;
self.TryGetValue(key, out value);
return value; ;
}
public static void DestoryChilds(this GameObject self,bool immediately=false)
{
if (!self) return;
var list = self.GetComponentsInChildren<Transform>().ToList();
list.TryRemove(self.transform);
foreach (var x in list)
{
if (immediately)
{
Object.DestroyImmediate(x.gameObject);
}
else
{
Object.Destroy(x.gameObject);
}
}
}
public static bool Contains(this string self, IEnumerable<string> strs, out string key)
{
bool result = false;
string newKey = default;
strs.ForEach(x =>
{
if (self.Contains(x))
{
result = true;
newKey = x;
return;
}
});
key = newKey;
return result;
}
/// <summary>
/// 带毫秒的字符转换成时间DateTime格式
/// 可处理格式:[2014-10-10 10:10:10,666 或 2014-10-10 10:10:10 666 或 2014-10-10 10:10:10.666]
/// </summary>
public static DateTime GetDateTime(this string dateTime)
{
string[] strArr = dateTime.Split(new char[] { '-', ' ', ':', ',', '.' });
DateTime dt = new DateTime(int.Parse(strArr[0]),
int.Parse(strArr[1]),
int.Parse(strArr[2]),
int.Parse(strArr[3]),
int.Parse(strArr[4]),
int.Parse(strArr[5]),
int.Parse(strArr[6]));
return dt;
}
public static bool Equality<T>(this IEnumerable<T> self, IEnumerable<T> other)
{
bool result = true;
if (self.Count() != other.Count())
{
return false;
}
self.ForEach(x =>
{
if (other.Contains(x) == false)
{
result = false;
return;
}
});
return result;
}
public static string ReservedRows(this string self, int length, bool reverse = false)
{
StringBuilder stringBuilder = new(self);
var lines = self.Split("\n");
if (reverse)
{
lines.Reverse().ToList();
}
if (lines.Length > length)
{
stringBuilder.Clear();
for (int i = lines.Length - length; i < lines.Length; i++)
{
var words = lines[i];
if (words is not "\n")
{
stringBuilder.Append(words);
}
stringBuilder.Append("\n");
}
}
return stringBuilder.ToString();
}
}
public static partial class ReflectionExtensions
{
public static void ForEachProties<T>(this object self, UnityAction<T> action = null)
{
self.GetType().GetProperties().ForEach(x =>
{
Debug.Log(x.Name);
if (x.GetCustomAttribute<ObsoleteAttribute>() == null && x.GetValue(self) is T t)
{
if (action is not null) action.Invoke(t);
}
});
}
public static void ForEachFields<T>(this object self, UnityAction<T> action = null, UnityAction<FieldInfo> infoAction = null, UnityAction<FieldInfo, T> mixAction = null)
{
self.GetType().GetFields().ForEach(x =>
{
if (x.GetCustomAttribute<ObsoleteAttribute>() == null && x.GetValue(self) is T t)
{
if (infoAction is not null) infoAction.Invoke(x);
if (action is not null) action.Invoke(t);
if (mixAction is not null) mixAction.Invoke(x, t);
}
});
}
}
public static partial class UIToolkitExtensions
{
private static Camera Camera => _camera ? _camera : _camera = Camera.main;
private static Camera _camera;
public static VisualElement Create(this VisualElement self, VisualTreeAsset asset)
{
var clone = asset.CloneTree();
self.Add(clone);
return clone;
}
public static bool Navigate(this VisualElement self, string name)
{
while (true)
{
var result = false;
var split = name.Split("/");
if (split.Length > 1)
{
var root = self;
var last = string.Empty;
foreach (var path in split)
{
var ve = root.Q(path);
if (ve is not null)
{
root = ve;
last = path;
}
}
if (root != self)
{
self = root.parent;
name = last;
continue;
}
}
else
{
foreach (var x in self.Children())
{
if (string.Equals(x.name, name, StringComparison.CurrentCultureIgnoreCase))
{
x.SetActive(true);
result = true;
}
else
{
x.SetActive(false);
}
}
return result;
}
break;
}
return false;
}
public static T Get<T>(this VisualElement self ,int index = 0) where T : VisualElement => self.Q<T>($"{typeof(T).Name}--{index}");
public static T Create<T>(this VisualElement self, string name = Constant.EmetyString) where T : VisualElement,new()
{
var element = new T();
if (string.IsNullOrEmpty(name) is false)
{
element.name = name;
}
self.Add(element);
return element;
}
public static T Create<T>(this VisualElement self,Func<VisualElement> createFactory, string name = Constant.EmetyString) where T : VisualElement, new()
{
var element = createFactory.Invoke();
if (string.IsNullOrEmpty(name) is false)
{
element.name = name;
}
self.Add(element);
return element as T;
}
public static bool GetActive(this VisualElement self) => self.style.display.value == DisplayStyle.Flex;
public static void SetActive(this VisualElement self, bool active)
{
self.style.display = new(active ? DisplayStyle.Flex : DisplayStyle.None);
}
public static void SetVisible(this VisualElement self, bool visible)
{
self.style.visibility = new(visible ? Visibility.Visible : Visibility.Hidden);
}
public static float GetOpacity(this VisualElement self) => self.style.opacity.value;
public static void SetOpacity(this VisualElement self, float value) => self.style.opacity = new(value);
public static void ScrollToBottom(this ScrollView self)
{
self.schedule.Execute(() =>
{
self.scrollOffset = new Vector2(0, float.MaxValue);
});
}
public static void ScrollToBottomAutomatic(this ScrollView self, float delay = 0.02f)
{
switch (true)
{
case true when Math.Abs(self.verticalScroller.value - self.verticalScroller.highValue) < 0.01:
case true when self.verticalScroller.highValue < 0.01f:
case true when Math.Abs(self.verticalScroller.value - self.verticalScroller.highValue) < 0.01f:
try
{
ScrollToBottom(self);
}
catch (OperationCanceledException)
{
}
break;
}
}
public static bool IsVisible(this VisualElement self,Vector3 worldPosition)
{
var cameraTrans = Camera.transform;
return Vector3.Dot(cameraTrans.forward, worldPosition - cameraTrans.position) > 0;
}
public static Vector2 GetScreenPosition(this VisualElement self, Vector3 worldPosition)
{
var panel = (self.panel ?? self.parent.panel) ?? self.parent.parent.panel;
var pos = RuntimePanelUtils
.CameraTransformWorldToPanel(panel, worldPosition, Camera);
pos.x -= self.layout.width / 2;
pos.y -= self.layout.height / 2;
return pos;
}
public static Vector2 GetPosition(this VisualElement self)
{
return new Vector2(self.style.left.value.value, self.style.top.value.value);
}
public static void SetPosition(this VisualElement self, Vector2 screenPosition)
{
self.style.right = new StyleLength(StyleKeyword.Auto);
self.style.bottom = new StyleLength(StyleKeyword.Auto);
self.style.position = Position.Absolute;
self.style.left = screenPosition.x;
self.style.top = screenPosition.y;
}
public static Vector2 SetPosition(this VisualElement self,Vector3 worldPosition)
{
var pos = self.GetScreenPosition(worldPosition);
var visible = self.IsVisible(worldPosition);
self.style.right = new StyleLength(StyleKeyword.Auto);
self.style.bottom = new StyleLength(StyleKeyword.Auto);
self.style.position = Position.Absolute;
// self.transform.position = pos;
self.style.left = pos.x;
self.style.top = pos.y;
self.SetOpacity(visible ? 1 : 0);
return pos;
}
public static void ClearTooltipsOnPointerLeave(this VisualElement visualElement)
{
visualElement.RegisterCallback<PointerLeaveEvent>(OnLeave);
return;
void OnLeave(PointerLeaveEvent evt)
{
if (string.IsNullOrEmpty(visualElement.tooltip) is false)
visualElement.tooltip = string.Empty;
}
}
public static void BlinkingCursor(this TextField tf,int interval=1000)
{
tf.schedule.Execute(() =>
{
if(tf.ClassListContains("transparentCursor"))
tf.RemoveFromClassList("transparentCursor");
else
tf.AddToClassList("transparentCursor");
}).Every(interval);
}
public static Vector2 GetAbsolutePositionInUI(this VisualElement element)
{
var position = Vector2.zero;
var currentElement = element;
// 遍历每一个父元素,并累计位置
while (currentElement != null)
{
var style = currentElement.resolvedStyle;
// 累加该元素相对于父元素的位置
position.x += style.left;
position.y += style.top;
// 移动到父元素
currentElement = currentElement.parent;
}
return position;
}
}
}