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(this T self) where T : IEquatable { 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(this IEnumerable e) { return e.ElementAt(UnityEngine.Random.Range(0, e.Count())); } public static T Random(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 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 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(), (properties, jToken) => { properties.Add(jToken.Path, jToken.ToString()); return properties; }); return tmpKeys; } public static Dictionary JsonToDictionary(this string jsonSrting, params string[] replace) { replace.ForEach(x => { jsonSrting = jsonSrting.Replace(x, string.Empty); }); Dictionary dic = JsonConvert.DeserializeObject>(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 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(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(this Component self, out T component) { component = self.GetComponentInChildren(true); if (component is not null) { return true; } component = self.GetComponentInParent(true); if (component is not null) { return true; } component = self.GetComponent(); return component is not null; } public static bool TryGetFirstOrDefault(this IEnumerable self, out T value) { value = default; if (self.IsValid()) { value = self.ElementAt(0); return true; } return default; } public static TValue GetOrDefault(this IDictionary 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().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 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; } /// /// 带毫秒的字符转换成时间(DateTime)格式 /// 可处理格式:[2014-10-10 10:10:10,666 或 2014-10-10 10:10:10 666 或 2014-10-10 10:10:10.666] /// 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(this IEnumerable self, IEnumerable 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(this object self, UnityAction action = null) { self.GetType().GetProperties().ForEach(x => { Debug.Log(x.Name); if (x.GetCustomAttribute() == null && x.GetValue(self) is T t) { if (action is not null) action.Invoke(t); } }); } public static void ForEachFields(this object self, UnityAction action = null, UnityAction infoAction = null, UnityAction mixAction = null) { self.GetType().GetFields().ForEach(x => { if (x.GetCustomAttribute() == 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(this VisualElement self ,int index = 0) where T : VisualElement => self.Q($"{typeof(T).Name}--{index}"); public static T Create(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(this VisualElement self,Func 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(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; } } }