794 lines
28 KiB
C#
794 lines
28 KiB
C#
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;
|
||
}
|
||
}
|
||
} |