Net.Like.Xue.Tokyo/Assets/BITKit/Unity/Scripts/Utility/Extensions.cs

797 lines
28 KiB
C#
Raw Normal View History

2024-11-03 16:42:23 +08:00
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 UnityEngine.UIElements;
2025-02-24 23:03:39 +08:00
using Object = UnityEngine.Object;
2024-11-03 16:42:23 +08:00
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
{
2025-02-24 23:03:39 +08:00
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);
}
2024-11-03 16:42:23 +08:00
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;
}
2025-02-24 23:03:39 +08:00
public static Vector2 TransientRotationAxis(Vector2 transientAxis)
{
transientAxis.x = TransientRotationAxis(transientAxis.x);
transientAxis.y = TransientRotationAxis(transientAxis.y);
return transientAxis;
}
2024-11-03 16:42:23 +08:00
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 Mathf.Max(Mathf.Abs(self.x), Mathf.Abs(self.y));
}
public static float GetLength(this Vector3 self)
{
return Mathf.Max(Mathf.Abs(self.x), Mathf.Abs(self.y), Mathf.Abs(self.z));
}
public static float GetValue(this Vector3 self)
{
var addValue = self.x + self.y + self.z;
var subtractValue = self.x - self.y - self.z;
if (Math.Abs(MathF.Abs(addValue) - Mathf.Abs(subtractValue)) < 0.01f)
{
return addValue;
}
else
{
var sb = new StringBuilder();
sb.AppendLine("Not a valid vector");
sb.AppendLine($"{self.x}+{self.y}+{self.z} = {addValue}");
sb.AppendLine($"{self.x}-{self.y}-{self.z} = {subtractValue}");
sb.AppendLine($"{addValue}");
sb.AppendLine($"{subtractValue}");
throw new Exception(sb.ToString());
}
}
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)
{
2025-02-24 23:03:39 +08:00
return (transientAxis %= 360f) switch
2024-11-03 16:42:23 +08:00
{
2025-02-24 23:03:39 +08:00
> 180f => transientAxis - 360f,
< -180f => transientAxis + 360f,
2024-11-03 16:42:23 +08:00
_ => transientAxis,
};
}
public static float WrapAngle(float angle)
{
2025-02-24 23:03:39 +08:00
angle %= 360f;
if (angle > 180f)
return angle - 360f;
2024-11-03 16:42:23 +08:00
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 TryGetComponentsInParent<T>(this GameObject self, out T[] components)
{
return TryGetComponentsInParent(self.transform, out components);
}
public static bool TryGetComponentsInParent<T>(this Component self, out T[] components)
{
components = self.GetComponentsInParent<T>();
return components.IsValid();
}
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);
2025-03-10 18:06:44 +08:00
if (component is not null)
{
return true;
}
component = self.GetComponent<T>();
2024-11-03 16:42:23 +08:00
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; ;
}
2025-02-24 23:03:39 +08:00
public static void DestoryChilds(this GameObject self,bool immediately=false)
2024-11-03 16:42:23 +08:00
{
2025-02-24 23:03:39 +08:00
if (!self) return;
var list = self.GetComponentsInChildren<Transform>().ToList();
list.TryRemove(self.transform);
foreach (var x in list)
2024-11-03 16:42:23 +08:00
{
2025-02-24 23:03:39 +08:00
if (immediately)
2024-11-03 16:42:23 +08:00
{
2025-02-24 23:03:39 +08:00
Object.DestroyImmediate(x.gameObject);
}
else
{
Object.Destroy(x.gameObject);
}
2024-11-03 16:42:23 +08:00
}
}
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
{
public static VisualElement Create(this VisualElement self, VisualTreeAsset asset)
{
var clone = asset.CloneTree();
self.Add(clone);
return clone;
}
public static bool Entry(this VisualElement self, string name,bool visibleOnEmpty=false)
{
var result=false;
foreach (var x in self.Children())
{
if (string.Equals(x.name, name))
{
x.SetActive(true);
result = true;
}
else
{
x.SetActive(false);
}
}
self.SetActive(visibleOnEmpty || result);
return result;
}
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.verticalScroller.value =
self.verticalScroller.highValue > 0 ? self.verticalScroller.highValue : 0;
}
public static async 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
{
await Task.Delay(TimeSpan.FromSeconds(delay), BITApp.CancellationToken);
ScrollToBottom(self);
}
catch (OperationCanceledException)
{
}
break;
}
}
public static bool IsVisible(this VisualElement self,Vector3 worldPosition)
{
var cameraTrans = Camera.main.transform;
return Vector3.Dot(cameraTrans.forward, worldPosition - cameraTrans.position) > 0;
}
public static Vector2 GetScreenPosition(this VisualElement self, Vector3 worldPosition)
{
try
{
var panel = self.panel;
if (panel is null)
{
panel = self.parent.panel;
}
if (panel is null)
{
panel = self.parent.parent.panel;
}
var pos = RuntimePanelUtils
.CameraTransformWorldToPanel(panel, worldPosition, Camera.main);
pos.x -= self.layout.width / 2;
pos.y -= self.layout.height / 2;
return pos;
}
catch (Exception e)
{
Debug.LogException(e);
}
return default;
}
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 void 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);
}
2025-02-24 23:03:39 +08:00
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;
}
2024-11-03 16:42:23 +08:00
}
}