using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Text; using System.Threading; using Convention.WindowsUI; using Demo.Game; using Unity.Collections; using UnityEditor; #if UNITY_EDITOR using UnityEditor.Build; #endif using UnityEngine; using UnityEngine.Events; using UnityEngine.Rendering; using UnityEngine.SceneManagement; using UnityEngine.UI; namespace UnityEditor { } namespace Convention { public static class PlatformIndicator { #if DEBUG public static bool IsRelease => false; #else public static bool IsRelease => true; #endif public static bool IsPlatformWindows => RuntimeInformation.IsOSPlatform(OSPlatform.Windows); public static bool IsPlatformLinux => RuntimeInformation.IsOSPlatform(OSPlatform.Linux); public static bool IsPlatformOsx => RuntimeInformation.IsOSPlatform(OSPlatform.OSX); public static bool IsPlatformX64 => System.Environment.Is64BitOperatingSystem; static PlatformIndicator() { MainThreadID = Thread.CurrentThread.ManagedThreadId; } public static int MainThreadID { get; private set; } public static bool CurrentThreadIsMainThread() { return MainThreadID == Thread.CurrentThread.ManagedThreadId; } public static string CompanyName = Application.companyName; public static string ProductName = Application.productName; public static string ApplicationPath => throw new NotSupportedException("Not support to get ApplicationPath"); public static string StreamingAssetsPath => Application.streamingAssetsPath; public static string PersistentDataPath => Application.persistentDataPath; public static string DataPath => Application.dataPath; public static string CurrentWorkPath => Environment.CurrentDirectory; public static string Combine(string a, string b) => Path.Combine(a, b); public static string CombineAsDir(string a, string b) { string name = b.Replace("\\", "/"); if (name.EndsWith("/") == false) { name += "/"; } return Path.Combine(a, name); } } public static partial class Utility { public static string ConvertString(object obj) { return Convert.ToString(obj); } public static T ConvertValue(string str) { Type type = typeof(T); var parse_method = type.GetMethod("Parse"); if (parse_method != null && (parse_method.ReturnType.IsSubclassOf(type) || parse_method.ReturnType == type) && parse_method.GetParameters().Length == 1 && parse_method.GetParameters()[0].ParameterType == typeof(string)) { return (T)parse_method.Invoke(null, new object[] { str }); } throw new InvalidCastException($"\"{str}\" is cannt convert to type<{type}>"); } public static object SeekValue(object obj, string name, BindingFlags flags, out bool isSucceed) { Type type = obj.GetType(); var field = type.GetField(name, flags); isSucceed = true; if (field != null) { return field.GetValue(obj); } var property = type.GetProperty(name, flags); if (property != null) { return property.GetValue(obj); } isSucceed = false; return null; } public static object SeekValue(object obj, string name, BindingFlags flags) { Type type = obj.GetType(); var field = type.GetField(name, flags); if (field != null) { return field.GetValue(obj); } var property = type.GetProperty(name, flags); if (property != null) { return property.GetValue(obj); } return null; } public static object SeekValue(object obj, string name, Type valueType, BindingFlags flags, out bool isSucceed) { Type type = obj.GetType(); var field = type.GetField(name, flags); isSucceed = true; if (field != null && field.FieldType == valueType) { return field.GetValue(obj); } var property = type.GetProperty(name, flags); if (property != null && property.PropertyType == valueType) { return property.GetValue(obj); } isSucceed = false; return null; } public static object SeekValue(object obj, string name, Type valueType, BindingFlags flags) { Type type = obj.GetType(); var field = type.GetField(name, flags); if (field != null && field.FieldType == valueType) { return field.GetValue(obj); } var property = type.GetProperty(name, flags); if (property != null && property.PropertyType == valueType) { return property.GetValue(obj); } return null; } public static bool PushValue(object obj, object value, string name, BindingFlags flags) { Type type = obj.GetType(); var field = type.GetField(name, flags); if (field != null) { field.SetValue(obj, value); return true; } var property = type.GetProperty(name, flags); if (property != null) { property.SetValue(obj, value); return true; } return false; } public static List SeekType(Predicate pr, IEnumerable assemblys = null, int findCount = -1) { List types = new List(); if (assemblys == null) assemblys = AppDomain.CurrentDomain.GetAssemblies(); foreach (var assembly in assemblys) { foreach (var type in assembly.GetTypes()) { if (pr(type)) types.Add(type); if (types.Count == findCount) return types; } } return types; } public static List GetMemberInfos(Type type, IEnumerable cutOffType = null, bool isGetNotPublic = false, bool isGetStatic = false) { Type current = type; BindingFlags flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly; List result = new(); if (isGetNotPublic) flags |= BindingFlags.NonPublic; if (isGetStatic) flags |= BindingFlags.Static; while ((cutOffType != null && !cutOffType.Contains(current)) && current != null) { result.AddRange(current.GetFields(flags)); result.AddRange(current.GetProperties(flags)); result.AddRange(current.GetMethods(flags)); current = current.BaseType; } return result; } public static bool IsNumber([In] object data) { if (data == null) return false; var type = data.GetType(); return IsNumber(type); } public static bool IsString([In] object data) { if (data == null) return false; var type = data.GetType(); return IsString(type); } public static bool IsBinary([In] object data) { if (data == null) return false; var type = data.GetType(); return IsBinary(type); } public static bool IsArray([In] object data) { if (data == null) return false; var type = data.GetType(); return IsArray(type); } public static bool IsBool([In] object data) { if (data == null) return false; return IsBool(data.GetType()); } public static bool IsNumber([In] Type type) { return type == typeof(double) || type == typeof(float) || type == typeof(int) || type == typeof(long) || type == typeof(sbyte) || type == typeof(short) || type == typeof(ushort) || type == typeof(uint) || type == typeof(ulong) || type == typeof(char); } public static bool IsString([In] Type type) { return type == typeof(string) || type == typeof(char[]); } public static bool IsBinary([In] Type type) { return type == typeof(byte) || type == typeof(sbyte) || type == typeof(byte[]) || type == typeof(sbyte[]); } public static bool IsArray([In] Type type) { return type.IsArray; } public static bool IsBool([In] Type type) { return type == typeof(bool); } public static bool IsEnum([In] Type type) { return type.IsEnum; } public static bool HasCustomAttribute(MemberInfo member, IEnumerable attrs) { foreach (var attr in attrs) { if (member.GetCustomAttribute(attr, true) != null) return true; } return false; } public static Type GetMemberValueType(MemberInfo member) { if (member is FieldInfo field) { return field.FieldType; } else if (member is PropertyInfo property) { return property.PropertyType; } return null; } public static bool GetMemberValueType(MemberInfo member, out Type type) { if (member is FieldInfo field) { type = field.FieldType; return true; } else if (member is PropertyInfo property) { type = property.PropertyType; return true; } type = null; return false; } public static void PushValue(object target, object value, MemberInfo info) { if (info is FieldInfo field) { if (value.GetType().IsSubclassOf(field.FieldType)) field.SetValue(target, value); else { field.SetValue(target, field.FieldType.GetMethod(nameof(float.Parse)).Invoke(target, new object[] { value })); } } else if (info is PropertyInfo property) { property.SetValue(target, value); } else { throw new InvalidOperationException("info is unsupport"); } } public static object SeekValue(object target, MemberInfo info) { if (info is FieldInfo field) { return field.GetValue(target); } else if (info is PropertyInfo property) { return property.GetValue(target); } else { throw new InvalidOperationException("info is unsupport"); } } public static bool TrySeekValue(object target, MemberInfo info, out object value) { if (info is FieldInfo field) { value = field.GetValue(target); return true; } else if (info is PropertyInfo property) { value = property.GetValue(target); return true; } value = null; return false; } public static List SeekMemberInfo(object target, IEnumerable attrs, IEnumerable types, Type untilBase = null) { Type _CurType = target.GetType(); List result = new(); result.AddRange(_CurType.GetMembers(BindingFlags.Public | BindingFlags.Instance)); while (_CurType != null && _CurType != typeof(object) && _CurType != untilBase) { result.AddRange( from info in _CurType.GetMembers(BindingFlags.NonPublic | BindingFlags.Instance) where attrs == null || HasCustomAttribute(info, attrs) where types == null || (GetMemberValueType(info, out var type) && types.Contains(type)) select info ); _CurType = _CurType.BaseType; } return result; } public static List SeekMemberInfo(object target, IEnumerable names, BindingFlags flags = BindingFlags.Default) { Type _CurType = target.GetType(); List result = _CurType.GetMembers(flags).ToList(); HashSet nameSet = names.ToHashSet(); result.RemoveAll(x => nameSet.Contains(x.Name) == false); return result; } public static object InvokeMember(MemberInfo member, object target, params object[] parameters) { if (member is MethodInfo method) { return method.Invoke(target, parameters); } return null; } public static bool TryInvokeMember(MemberInfo member, object target, out object returnValue, params object[] parameters) { returnValue = null; if (member is MethodInfo method) { returnValue = method.Invoke(target, parameters); return true; } else return false; } public static T Shared(T target, out T value) { value = target; return value; } public static string NowFormat(string format = "yyyy-MM-dd_HH-mm-ss") { return DateTime.Now.ToString(format); } /// /// Marshal 相关的实用函数。 /// public static class Marshal { [Serializable] public class MarshalException : Exception { public MarshalException() { } public MarshalException(string message) : base(message) { } public MarshalException(string message, Exception inner) : base(message, inner) { } protected MarshalException( System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context) { } } private const int BlockSize = 1024 * 4; private static IntPtr s_CachedHGlobalPtr = IntPtr.Zero; private static int s_CachedHGlobalSize = 0; /// /// 获取缓存的从进程的非托管内存中分配的内存的大小。 /// public static int CachedHGlobalSize { get { return s_CachedHGlobalSize; } } /// /// 确保从进程的非托管内存中分配足够大小的内存并缓存。 /// /// 要确保从进程的非托管内存中分配内存的大小。 public static void EnsureCachedHGlobalSize(int ensureSize) { if (ensureSize < 0) { throw new MarshalException("Ensure size is invalid."); } if (s_CachedHGlobalPtr == IntPtr.Zero || s_CachedHGlobalSize < ensureSize) { FreeCachedHGlobal(); int size = (ensureSize - 1 + BlockSize) / BlockSize * BlockSize; s_CachedHGlobalPtr = System.Runtime.InteropServices.Marshal.AllocHGlobal(size); s_CachedHGlobalSize = size; } } /// /// 释放缓存的从进程的非托管内存中分配的内存。 /// public static void FreeCachedHGlobal() { if (s_CachedHGlobalPtr != IntPtr.Zero) { System.Runtime.InteropServices.Marshal.FreeHGlobal(s_CachedHGlobalPtr); s_CachedHGlobalPtr = IntPtr.Zero; s_CachedHGlobalSize = 0; } } /// /// 将数据从对象转换为二进制流。 /// /// 要转换的对象的类型。 /// 要转换的对象。 /// 存储转换结果的二进制流。 public static byte[] StructureToBytes(T structure) { return StructureToBytes(structure, System.Runtime.InteropServices.Marshal.SizeOf(typeof(T))); } /// /// 将数据从对象转换为二进制流。 /// /// 要转换的对象的类型。 /// 要转换的对象。 /// 要转换的对象的大小。 /// 存储转换结果的二进制流。 internal static byte[] StructureToBytes(T structure, int structureSize) { if (structureSize < 0) { throw new MarshalException("Structure size is invalid."); } EnsureCachedHGlobalSize(structureSize); System.Runtime.InteropServices.Marshal.StructureToPtr(structure, s_CachedHGlobalPtr, true); byte[] result = new byte[structureSize]; System.Runtime.InteropServices.Marshal.Copy(s_CachedHGlobalPtr, result, 0, structureSize); return result; } /// /// 将数据从对象转换为二进制流。 /// /// 要转换的对象的类型。 /// 要转换的对象。 /// 存储转换结果的二进制流。 public static void StructureToBytes(T structure, byte[] result) { StructureToBytes(structure, System.Runtime.InteropServices.Marshal.SizeOf(typeof(T)), result, 0); } /// /// 将数据从对象转换为二进制流。 /// /// 要转换的对象的类型。 /// 要转换的对象。 /// 要转换的对象的大小。 /// 存储转换结果的二进制流。 internal static void StructureToBytes(T structure, int structureSize, byte[] result) { StructureToBytes(structure, structureSize, result, 0); } /// /// 将数据从对象转换为二进制流。 /// /// 要转换的对象的类型。 /// 要转换的对象。 /// 存储转换结果的二进制流。 /// 写入存储转换结果的二进制流的起始位置。 public static void StructureToBytes(T structure, byte[] result, int startIndex) { StructureToBytes(structure, System.Runtime.InteropServices.Marshal.SizeOf(typeof(T)), result, startIndex); } /// /// 将数据从对象转换为二进制流。 /// /// 要转换的对象的类型。 /// 要转换的对象。 /// 要转换的对象的大小。 /// 存储转换结果的二进制流。 /// 写入存储转换结果的二进制流的起始位置。 internal static void StructureToBytes(T structure, int structureSize, byte[] result, int startIndex) { if (structureSize < 0) { throw new MarshalException("Structure size is invalid."); } if (result == null) { throw new MarshalException("Result is invalid."); } if (startIndex < 0) { throw new MarshalException("Start index is invalid."); } if (startIndex + structureSize > result.Length) { throw new MarshalException("Result length is not enough."); } EnsureCachedHGlobalSize(structureSize); System.Runtime.InteropServices.Marshal.StructureToPtr(structure, s_CachedHGlobalPtr, true); System.Runtime.InteropServices.Marshal.Copy(s_CachedHGlobalPtr, result, startIndex, structureSize); } /// /// 将数据从二进制流转换为对象。 /// /// 要转换的对象的类型。 /// 要转换的二进制流。 /// 存储转换结果的对象。 public static T BytesToStructure(byte[] buffer) { return BytesToStructure(System.Runtime.InteropServices.Marshal.SizeOf(typeof(T)), buffer, 0); } /// /// 将数据从二进制流转换为对象。 /// /// 要转换的对象的类型。 /// 要转换的二进制流。 /// 读取要转换的二进制流的起始位置。 /// 存储转换结果的对象。 public static T BytesToStructure(byte[] buffer, int startIndex) { return BytesToStructure(System.Runtime.InteropServices.Marshal.SizeOf(typeof(T)), buffer, startIndex); } /// /// 将数据从二进制流转换为对象。 /// /// 要转换的对象的类型。 /// 要转换的对象的大小。 /// 要转换的二进制流。 /// 存储转换结果的对象。 internal static T BytesToStructure(int structureSize, byte[] buffer) { return BytesToStructure(structureSize, buffer, 0); } /// /// 将数据从二进制流转换为对象。 /// /// 要转换的对象的类型。 /// 要转换的对象的大小。 /// 要转换的二进制流。 /// 读取要转换的二进制流的起始位置。 /// 存储转换结果的对象。 internal static T BytesToStructure(int structureSize, byte[] buffer, int startIndex) { if (structureSize < 0) { throw new MarshalException("Structure size is invalid."); } if (buffer == null) { throw new MarshalException("Buffer is invalid."); } if (startIndex < 0) { throw new MarshalException("Start index is invalid."); } if (startIndex + structureSize > buffer.Length) { throw new MarshalException("Buffer length is not enough."); } EnsureCachedHGlobalSize(structureSize); System.Runtime.InteropServices.Marshal.Copy(buffer, startIndex, s_CachedHGlobalPtr, structureSize); return (T)System.Runtime.InteropServices.Marshal.PtrToStructure(s_CachedHGlobalPtr, typeof(T)); } } } } namespace Convention { public static partial class ConventionUtility { //[MethodImpl(MethodImplOptions.AggressiveInlining)] public static string convert_xstring([In] object obj) { return Convert.ToString(obj); } public static _T convert_xvalue<_T>([In] string str) { Type type = typeof(_T); var parse_method = type.GetMethod(nameof(int.Parse), BindingFlags.Static | BindingFlags.Public, null, new Type[] { typeof(string) }, null); if (parse_method != null && (parse_method.ReturnType.IsSubclassOf(type) || parse_method.ReturnType == type) && parse_method.GetParameters().Length == 1 && parse_method.GetParameters()[0].ParameterType == typeof(string)) { return (_T)parse_method.Invoke(null, new object[] { str }); } throw new InvalidCastException($"\"{str}\" is cannt convert to type<{type}>"); } public static object ConventValue([In] Type type, [In] string str) { var parse_method = type.GetMethod("Parse"); if (parse_method != null && (parse_method.ReturnType.IsSubclassOf(type) || parse_method.ReturnType == type) && parse_method.GetParameters().Length == 1 && parse_method.GetParameters()[0].ParameterType == typeof(string)) { return parse_method.Invoke(null, new object[] { str }); } throw new InvalidCastException($"\"{str}\" is cannt convert to type<{type}>"); } public static string Combine([In] params object[] args) { if (args.Length == 0) return ""; if (args.Length == 1) return args[0].ToString(); return Combine(args[0]) + Combine(args[1..]); } public static string Trim([In] string str, int left_right_flag = 3) { string result = new string(str); if ((left_right_flag & (1 << 0)) == 1) result = result.TrimStart(); if ((left_right_flag & (1 << 1)) == 1) result = result.TrimEnd(); return result; } public static object SeekValue([In] object obj, [In] string name, BindingFlags flags, [Out][Opt] out bool isSucceed) { Type type = obj.GetType(); var field = type.GetField(name, flags); isSucceed = true; if (field != null) { return field.GetValue(obj); } var property = type.GetProperty(name, flags); if (property != null) { return property.GetValue(obj); } isSucceed = false; return null; } public static object SeekValue([In] object obj, [In] string name, BindingFlags flags) { Type type = obj.GetType(); var field = type.GetField(name, flags); if (field != null) { return field.GetValue(obj); } var property = type.GetProperty(name, flags); if (property != null) { return property.GetValue(obj); } return null; } public static object SeekValue([In] object obj, [In] string name, [In] Type valueType, BindingFlags flags, [Out][Opt] out bool isSucceed) { Type type = obj.GetType(); var field = type.GetField(name, flags); isSucceed = true; if (field != null && field.FieldType == valueType) { return field.GetValue(obj); } var property = type.GetProperty(name, flags); if (property != null && property.PropertyType == valueType) { return property.GetValue(obj); } isSucceed = false; return null; } public static object SeekValue([In] object obj, [In] string name, [In] Type valueType, BindingFlags flags) { Type type = obj.GetType(); var field = type.GetField(name, flags); if (field != null && field.FieldType == valueType) { return field.GetValue(obj); } var property = type.GetProperty(name, flags); if (property != null && property.PropertyType == valueType) { return property.GetValue(obj); } return null; } public static bool PushValue([In] object obj, [In] object value, [In] string name, BindingFlags flags) { Type type = obj.GetType(); var field = type.GetField(name, flags); if (field != null) { field.SetValue(obj, value); return true; } var property = type.GetProperty(name, flags); if (property != null) { property.SetValue(obj, value); return true; } return false; } public static T GetOrAddComponent(this MonoBehaviour self) where T : Component { if (self.GetComponents().Length == 0) { return self.gameObject.AddComponent(); } else { return self.gameObject.GetComponents()[0]; } } public static T SeekComponent(this MonoBehaviour self) where T : class { var results = self.gameObject.GetComponents(); if (results.Length == 0) return null; return results[0]; } public static T GetOrAddComponent(this GameObject self) where T : Component { if (self.GetComponents().Length == 0) { return self.AddComponent(); } else { return self.GetComponents()[0]; } } public static T SeekComponent(this GameObject self) where T : class { var results = self.GetComponents(); if (results.Length == 0) return null; return results[0]; } public static List SeekType(Predicate pr, IEnumerable assemblys = null, int findCount = -1) { List types = new List(); if (assemblys == null) assemblys = AppDomain.CurrentDomain.GetAssemblies(); foreach (var assembly in assemblys) { foreach (var type in assembly.GetTypes()) { if (pr(type)) types.Add(type); if (types.Count == findCount) return types; } } return types; } public static List GetMemberInfos(Type type, IEnumerable cutOffType = null, bool isGetNotPublic = false, bool isGetStatic = false) { Type current = type; BindingFlags flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly; List result = new(); if (isGetNotPublic) flags |= BindingFlags.NonPublic; if (isGetStatic) flags |= BindingFlags.Static; while ((cutOffType != null && !cutOffType.Contains(current)) && current != null) { result.AddRange(current.GetFields(flags)); result.AddRange(current.GetProperties(flags)); result.AddRange(current.GetMethods(flags)); current = current.BaseType; } return result; } } [Serializable] public class SALCheckException : Exception { public delegate bool Predicate(object val); public Attribute attribute; public SALCheckException(Attribute attribute) { this.attribute = attribute; } public SALCheckException(Attribute attribute, string message) : base(message) { this.attribute = attribute; } public SALCheckException(Attribute attribute, string message, Exception inner) : base(message, inner) { this.attribute = attribute; } } [System.AttributeUsage(AttributeTargets.Parameter, Inherited = true, AllowMultiple = false)] public class InAttribute : Attribute { } [System.AttributeUsage(AttributeTargets.Parameter, Inherited = true, AllowMultiple = false)] public class OutAttribute : Attribute { } [System.AttributeUsage(AttributeTargets.Parameter, Inherited = true, AllowMultiple = true)] public class OptAttribute : Attribute { } [System.AttributeUsage(AttributeTargets.ReturnValue, Inherited = true, AllowMultiple = false)] public class ReturnVirtualAttribute : Attribute { } [System.AttributeUsage(AttributeTargets.ReturnValue, Inherited = true, AllowMultiple = false)] public class ReturnMayNullAttribute : Attribute { } [System.AttributeUsage(AttributeTargets.ReturnValue, Inherited = true, AllowMultiple = false)] public class ReturnNotNullAttribute : Attribute { } [System.AttributeUsage(AttributeTargets.ReturnValue, Inherited = true, AllowMultiple = false)] public class ReturnSelfAttribute : Attribute { } [System.AttributeUsage(AttributeTargets.ReturnValue, Inherited = true, AllowMultiple = false)] public class ReturnNotSelfAttribute : Attribute { } #if UNITY_2017_1_OR_NEWER [System.AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.Field | AttributeTargets.Property, Inherited = true, AllowMultiple = false)] public class IsInstantiatedAttribute : Attribute { public bool isInstantiated; public IsInstantiatedAttribute(bool isInstantiated) { this.isInstantiated = isInstantiated; } } #endif [System.AttributeUsage(AttributeTargets.ReturnValue, Inherited = true, AllowMultiple = false)] public class SucceedAttribute : Attribute { private SALCheckException.Predicate pr; public SucceedAttribute([In] object succeed_when_return_value_is_equal_this_value_or_pr_is_return_true) { var prm = succeed_when_return_value_is_equal_this_value_or_pr_is_return_true.GetType().GetMethod("Invoke"); if (prm != null && prm.GetParameters().Length == 1 && prm.ReturnType == typeof(bool)) this.pr = (SALCheckException.Predicate)succeed_when_return_value_is_equal_this_value_or_pr_is_return_true; else this.pr = (object obj) => obj == succeed_when_return_value_is_equal_this_value_or_pr_is_return_true; } public bool Check([In][Opt] object value) { if (this.pr(value)) return true; throw new SALCheckException(this, $"return value<{value.ToString()[..25]}...> is not expect"); } } [System.AttributeUsage(AttributeTargets.ReturnValue, Inherited = true, AllowMultiple = false)] public class NotSucceedAttribute : Attribute { private SALCheckException.Predicate pr; public NotSucceedAttribute([In] object failed_when_return_value_is_equal_this_value_or_pr_is_return_true) { var prm = failed_when_return_value_is_equal_this_value_or_pr_is_return_true.GetType().GetMethod("Invoke"); if (prm != null && prm.GetParameters().Length == 1 && prm.ReturnType == typeof(bool)) this.pr = (SALCheckException.Predicate)failed_when_return_value_is_equal_this_value_or_pr_is_return_true; else this.pr = (object obj) => obj == failed_when_return_value_is_equal_this_value_or_pr_is_return_true; } public bool Check([In][Opt] object value) { if (this.pr(value)) throw new SALCheckException(this, $"return value<{value.ToString()[..25]}...> is not expect"); return true; } } [System.AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = false)] public class MethodReturnSelfAttribute : Attribute { } [System.AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = false)] public class MethodReturnNotSelfAttribute : Attribute { } [System.AttributeUsage(AttributeTargets.All, Inherited = true, AllowMultiple = true)] public class WhenAttribute : Attribute { private SALCheckException.Predicate pr = null; public readonly Type TypenAttribute = null; /// /// bool predicate(object)+typenAttribute /// value+typenAttribute /// /// The value will been checked or predicate /// Target Checker public WhenAttribute([In] object control_value_or_predicate, [In] Type typenAttribute) { this.TypenAttribute = typenAttribute; if (typenAttribute == typeof(OnlyNotNullModeAttribute)) { if (ConventionUtility.IsString(control_value_or_predicate)) { this.pr = (object obj) => { return new OnlyNotNullModeAttribute((string)control_value_or_predicate).Check(obj); }; return; } } else if ( typenAttribute.Name.EndsWith("SucceedAttribute") || typenAttribute.Name.StartsWith("Return") ) { return; } do { var prm = control_value_or_predicate.GetType().GetMethod("Invoke"); if (prm != null && prm.GetParameters().Length == 1 && prm.ReturnType == typeof(bool)) this.pr = (SALCheckException.Predicate)control_value_or_predicate; else this.pr = (object obj) => obj == control_value_or_predicate; } while (false); } /// /// do nothing /// /// public WhenAttribute([In] string description) { } protected WhenAttribute() { } #if UNITY_EDITOR /// /// The value is /// is :member value is not null /// Default:predicate(target) /// /// /// #endif public virtual bool Check([In][Opt] object value) { if (TypenAttribute == typeof(OnlyNotNullModeAttribute)) { return pr(value); } else { if (pr == null) //throw new SALCheckException(this, "you should not check at this"); return true; if (this.pr(value)) return true; return false; } } [System.AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = true, AllowMultiple = true)] public abstract class WhenMemberValueAttribute : WhenAttribute { public readonly string Name; public readonly object Value; protected object InjectGetValue(object target) { return ConventionUtility.SeekValue(target, Name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); } public override bool Check(object target) { throw new NotImplementedException(); } public WhenMemberValueAttribute(string Name, object value) { this.Name = Name; this.Value = value; } } [System.AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = true, AllowMultiple = true)] public class IsAttribute : WhenMemberValueAttribute { public override bool Check(object target) { if (this.Value is Type) { var targetValue = this.InjectGetValue(target); if (targetValue == null) return false; else return targetValue.GetType().IsSubclassOf(this.Value as Type); } if (this.Value != null) return this.Value.Equals(this.InjectGetValue(target)); var injectValue = this.InjectGetValue(target); if (injectValue != null) return injectValue.Equals(this.Value); return injectValue == null && this.Value == null; } public IsAttribute(string Name, object value) : base(Name, value) { } } [System.AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = true, AllowMultiple = true)] public class NotAttribute : IsAttribute { public override bool Check(object target) { return !base.Check(target); } public NotAttribute(string Name, object value) : base(Name, value) { } } } [System.AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = false)] public class IgnoreAttribute : Attribute { } [System.AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = false)] public class ProjectContextLabelAttribute : Attribute { public enum ContextLabelType { Content, Resources, Setting } public static void DebugError(string mainName, string message, ContextLabelType type, UnityEngine.Object obj) { string labelname = type switch { ContextLabelType.Setting => "setting", ContextLabelType.Resources => "resources", ContextLabelType.Content => "content", _ => "context-label" }; Debug.LogError($"{mainName} - {message} due to missing {labelname}.", obj); } } [System.AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = false)] public class SettingAttribute : ProjectContextLabelAttribute { } [System.AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = false)] public class ResourcesAttribute : ProjectContextLabelAttribute { } [System.AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = false)] public class ContentAttribute : ProjectContextLabelAttribute { } [System.AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = false)] public class OnlyPlayModeAttribute : Attribute { } [System.AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = false)] public class OnlyNotNullModeAttribute : Attribute { public string Name; public bool Check(object target) { if (IsSelf()) { #if UNITY_2017_1_OR_NEWER if (target is UnityEngine.Object && (target as UnityEngine.Object) == null) return false; #endif return target != null; } var field = target.GetType().GetField(Name, BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { object value = field.GetValue(target); if (value == null) return false; #if UNITY_2017_1_OR_NEWER if (value is UnityEngine.Object && (value as UnityEngine.Object) == null) return false; #endif return true; } var property = target.GetType().GetProperty(Name, BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null) { object value = property.GetValue(target); if (value == null) return false; #if UNITY_2017_1_OR_NEWER if (value is UnityEngine.Object && (value as UnityEngine.Object) == null) return false; #endif return true; } return false; } public bool IsSelf() => Name == null || Name.Length == 0; /// /// binding to target field /// /// public OnlyNotNullModeAttribute(string fieldName) { this.Name = fieldName; } /// /// binding to self /// public OnlyNotNullModeAttribute() { this.Name = null; } } [System.AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = false)] public class HopeNotNullAttribute : Attribute { public bool Check(object target) { return target != null; } public HopeNotNullAttribute() { } } [System.AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.ReturnValue, Inherited = false, AllowMultiple = false)] public class PercentageAttribute : Attribute { public float min = 0, max = 100; public PercentageAttribute([In] float min, [In] float max) { this.min = min; this.max = max; } } [System.AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Class | AttributeTargets.Class | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.Interface | AttributeTargets.GenericParameter, Inherited = false, AllowMultiple = false)] public class ArgPackageAttribute : Attribute { public Type[] UsedFor; public ArgPackageAttribute([In][Opt] params Type[] usedFor) { UsedFor = usedFor; } } [System.AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = false)] public class TODOAttribute : Attribute { public bool Check(object any) { throw new InvalidOperationException("TODO"); throw new NotImplementedException(); } } [System.AttributeUsage(AttributeTargets.All, Inherited = true, AllowMultiple = false)] public class ImportantAttribute : Attribute { public string description; public ImportantAttribute(string description) { this.description = description; } public ImportantAttribute() { } } [System.AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)] public class DescriptionAttribute : Attribute { public string description; public DescriptionAttribute(string description) { this.description = description; } } [System.AttributeUsage(AttributeTargets.All, Inherited = true, AllowMultiple = true)] public class TypeCheckAttribute : Attribute { public readonly Type[] typens; public readonly string description; public TypeCheckAttribute(string description, params Type[] typens) { this.typens = typens; this.description = description; } public TypeCheckAttribute(params Type[] typens) : this("Type Check Failed: value is not sub class of " + $"{string.Join(",", typens.ToList().ConvertAll(x => x.Name))}" + ", current is ${type}", typens) { } public bool Check(object target) { return target != null && typens.Any(x => target.GetType().IsSubclassOf(x) || x.IsInterface && target.GetType().GetInterface(x.Name) != null); } } public static partial class ConventionUtility { public static event Action InitExtensionEnvCalls; #if UNITY_EDITOR [UnityEditor.MenuItem("Convention/InitExtensionEnv", priority = 100000)] #endif public static void InitExtensionEnv() { UnityEngine.Application.quitting += () => { GameObject.Destroy(s_CoroutineStarter.gameObject); s_CoroutineStarter = null; }; InitExtensionEnvCalls(); GlobalConfig.InitExtensionEnv(); UnityExtension.InitExtensionEnv(); ES3Plugin.InitExtensionEnv(); } public static int MainThreadID { get; private set; } public static bool CurrentThreadIsMainThread() { return MainThreadID == Thread.CurrentThread.ManagedThreadId; } private static CoroutineMonoStarterUtil s_CoroutineStarter; private static CoroutineMonoStarterUtil CoroutineStarter { get { if (s_CoroutineStarter == null) { s_CoroutineStarter = new GameObject($"{nameof(ConventionUtility)}-{nameof(CoroutineStarter)}").AddComponent(); } return s_CoroutineStarter; } } public static GameObject Singleton => CoroutineStarter.gameObject; private class CoroutineMonoStarterUtil : MonoBehaviour { internal float waitClock; private void Update() { waitClock = Time.realtimeSinceStartup; MainThreadID = Thread.CurrentThread.ManagedThreadId; } private void OnDestroy() { s_CoroutineStarter = null; } } /// /// 包装即将用于协程的迭代器成为一个防止假死机的迭代器 /// /// /// public static IEnumerator AvoidFakeStop(IEnumerator ir, float fps = 5) { Stack loadingTask = new(); loadingTask.Push(ir); float maxWaitClock = 1 / fps; while (loadingTask.Count > 0) { // 防止大量无延迟函数的使用导致假死机 //if (Time.realtimeSinceStartup - CoroutineStarter.waitClock > maxWaitClock) { yield return null; } if (loadingTask.Peek().MoveNext()) { if (loadingTask.Peek().Current is IEnumerator next) loadingTask.Push(next); } else { loadingTask.Pop(); } } } /// /// 包装即将用于协程的迭代器成为一个防止假死机的迭代器 /// /// /// public static IEnumerator AvoidFakeStopWithoutDeepStack(IEnumerator ir, float fps = 5) { float maxWaitClock = 1 / fps; while (ir.MoveNext()) { // 防止大量无延迟函数的使用导致假死机 if (Time.realtimeSinceStartup - CoroutineStarter.waitClock > maxWaitClock) { yield return null; } } } public static Coroutine StartCoroutine(IEnumerator coroutine) { return CoroutineStarter.StartCoroutine(coroutine); } public static void CloseCoroutine(Coroutine coroutine) { CoroutineStarter.StopCoroutine(coroutine); } public static void StopAllCoroutine() { CoroutineStarter.StopAllCoroutines(); } public class ActionStepCoroutineWrapper { private struct YieldInstructionWrapper { public YieldInstruction UnityYieldInstruction; public CustomYieldInstruction CustomYieldInstruction; public YieldInstructionWrapper(YieldInstruction unityYieldInstruction, CustomYieldInstruction customYieldInstruction) { this.UnityYieldInstruction = unityYieldInstruction; this.CustomYieldInstruction = customYieldInstruction; } public static YieldInstructionWrapper Make(YieldInstruction unityYieldInstruction) { return new(unityYieldInstruction, null); } public static YieldInstructionWrapper Make(CustomYieldInstruction customYieldInstruction) { return new(null, customYieldInstruction); } } private List> steps = new(); public ActionStepCoroutineWrapper Update(Action action) { steps.Add(new(new(), action)); return this; } public ActionStepCoroutineWrapper Wait(float time, Action action) { steps.Add(new(YieldInstructionWrapper.Make(new WaitForSeconds(time)), action)); return this; } public ActionStepCoroutineWrapper FixedUpdate(Action action) { steps.Add(new(YieldInstructionWrapper.Make(new WaitForFixedUpdate()), action)); return this; } public ActionStepCoroutineWrapper Next(Action action) { steps.Add(new(YieldInstructionWrapper.Make(new WaitForEndOfFrame()), action)); return this; } public ActionStepCoroutineWrapper Until(Func pr, Action action) { steps.Add(new(YieldInstructionWrapper.Make(new WaitUntil(pr)), action)); return this; } private static IEnumerator Execute(List> steps) { foreach (var (waiting, action) in steps) { if (waiting.UnityYieldInstruction != null) yield return waiting.UnityYieldInstruction; else yield return waiting.CustomYieldInstruction; action(); } } ~ActionStepCoroutineWrapper() { this.Invoke(); } public void Invoke() { StartCoroutine(Execute(new List>(steps))); steps.Clear(); } } [return: ReturnNotSelf] public static ActionStepCoroutineWrapper CreateSteps() => new(); public static bool IsNumber([In] object data) { if (data == null) return false; var type = data.GetType(); return IsNumber(type); } public static bool IsString([In] object data) { if (data == null) return false; var type = data.GetType(); return IsString(type); } public static bool IsBinary([In] object data) { if (data == null) return false; var type = data.GetType(); return IsBinary(type); } public static bool IsArray([In] object data) { if (data == null) return false; var type = data.GetType(); return IsArray(type); } public static bool IsBool([In] object data) { if (data == null) return false; return IsBool(data.GetType()); } public static bool IsNumber([In] Type type) { return type == typeof(double) || type == typeof(float) || type == typeof(int) || type == typeof(long) || type == typeof(sbyte) || type == typeof(short) || type == typeof(ushort) || type == typeof(uint) || type == typeof(ulong) || type == typeof(char); } public static bool IsString([In] Type type) { return type == typeof(string) || type == typeof(char[]); } public static bool IsBinary([In] Type type) { return type == typeof(byte) || type == typeof(sbyte) || type == typeof(byte[]) || type == typeof(sbyte[]); } public static bool IsArray([In] Type type) { return type.IsArray; } public static bool IsBool([In] Type type) { return type == typeof(bool); } public static bool IsEnum([In] Type type) { return type.IsEnum; } public static bool IsImage([In] Type type) { return type.IsSubclassOf(typeof(Texture)) || type == typeof(Sprite); } public static bool HasCustomAttribute([In] MemberInfo member, [In] IEnumerable attrs) { foreach (var attr in attrs) { if (member.GetCustomAttribute(attr, true) != null) return true; } return false; } [return: ReturnMayNull] public static Type GetMemberValueType([In] MemberInfo member) { if (member is FieldInfo field) { return field.FieldType; } else if (member is PropertyInfo property) { return property.PropertyType; } return null; } public static bool GetMemberValueType([In] MemberInfo member, [Out] out Type type) { if (member is FieldInfo field) { type = field.FieldType; return true; } else if (member is PropertyInfo property) { type = property.PropertyType; return true; } type = null; return false; } public static void PushValue([In] object target, [In][Opt, When("If you sure")] object value, [In] MemberInfo info) { if (info is FieldInfo field) { if (value.GetType().IsSubclassOf(field.FieldType)) field.SetValue(target, value); else { field.SetValue(target, field.FieldType.GetMethod(nameof(float.Parse)).Invoke(target, new object[] { value })); } } else if (info is PropertyInfo property) { property.SetValue(target, value); } else { throw new InvalidOperationException("info is unsupport"); } } public static object SeekValue([In] object target, [In] MemberInfo info) { if (info is FieldInfo field) { return field.GetValue(target); } else if (info is PropertyInfo property) { return property.GetValue(target); } else { throw new InvalidOperationException("info is unsupport"); } } public static bool TrySeekValue([In] object target, [In] MemberInfo info, [Out] out object value) { if (info is FieldInfo field) { value = field.GetValue(target); return true; } else if (info is PropertyInfo property) { value = property.GetValue(target); return true; } value = null; return false; } public static List SeekMemberInfoFromType( [In] Type targetType, [In, Opt] IEnumerable attrs, [In, Opt] IEnumerable types, [In, Opt] Type untilBase = null ) { List result = new(); result.AddRange(targetType.GetMembers(BindingFlags.Public | BindingFlags.Instance)); while (targetType != null && targetType != typeof(object) && targetType != untilBase) { result.AddRange( from info in targetType.GetMembers(BindingFlags.NonPublic | BindingFlags.Instance) where attrs == null || HasCustomAttribute(info, attrs) where types == null || (GetMemberValueType(info, out var type) && types.Contains(type)) select info ); targetType = targetType.BaseType; } return result; } public static List SeekMemberInfo( [In] object target, [In, Opt] IEnumerable attrs, [In, Opt] IEnumerable types, [In, Opt] Type untilBase = null ) { return SeekMemberInfoFromType(target.GetType(), attrs, types, untilBase); } public static List SeekMemberInfoFromType([In] Type target, IEnumerable names, BindingFlags flags = BindingFlags.Public | BindingFlags.Instance) { List result = target.GetMembers(flags).ToList(); HashSet nameSet = names.ToHashSet(); result.RemoveAll(x => nameSet.Contains(x.Name) == false); return result; } public static List SeekMemberInfo([In] object target, IEnumerable names, BindingFlags flags = BindingFlags.Public | BindingFlags.Instance) { return SeekMemberInfoFromType(target.GetType(), names, flags); } public static object InvokeMember([In] MemberInfo member, [In] object target, params object[] parameters) { if (member is MethodInfo method) { return method.Invoke(target, parameters); } return null; } public static bool TryInvokeMember([In] MemberInfo member, object target, out object returnValue, params object[] parameters) { returnValue = null; if (member is MethodInfo method) { returnValue = method.Invoke(target, parameters); return true; } else return false; } } #if UNITY_2017_1_OR_NEWER namespace Internal { public interface IRectTransform { UnityEngine.RectTransform rectTransform { get; } } } #endif public static partial class ConventionUtility { public static UnityEvent WrapperAction2Event(params UnityAction[] actions) { var result = new UnityEvent(); foreach (var action in actions) { result.AddListener(action); } return result; } public static UnityEvent WrapperAction2Event(params UnityAction[] actions) { var result = new UnityEvent(); foreach (var action in actions) { result.AddListener(action); } return result; } public static UnityEvent WrapperAction2Event(params UnityAction[] actions) { var result = new UnityEvent(); foreach (var action in actions) { result.AddListener(action); } return result; } public static UnityEvent WrapperAction2Event(params UnityAction[] actions) { var result = new UnityEvent(); foreach (var action in actions) { result.AddListener(action); } return result; } public static UnityEvent WrapperAction2Event(params UnityAction[] actions) { var result = new UnityEvent(); foreach (var action in actions) { result.AddListener(action); } return result; } } public abstract class MonoSingleton : MonoBehaviour where T : MonoSingleton { [Setting, Ignore] private static T m_instance; public static T instance { get => m_instance; protected set => m_instance = value; } public virtual bool IsDontDestroyOnLoad { get => false; } protected virtual void Awake() { if (instance != null) { this.gameObject.SetActive(false); return; } if (IsDontDestroyOnLoad && this.transform.parent == null) DontDestroyOnLoad(this); instance = (T)this; } public static bool IsAvailable() { return instance != null; } } } namespace Convention { public static class UnityExtension { public static void InitExtensionEnv() { AsyncOperationExtension.InitExtensionEnv(); RectTransformExtension.InitExtensionEnv(); } } public static class AsyncOperationExtension { public static void InitExtensionEnv() { CompletedHelper.InitExtensionEnv(); } public static void MarkCompleted(this AsyncOperation operation, [In] Action action) { operation.completed += new CompletedHelper(action).InternalCompleted; } private class CompletedHelper { static CompletedHelper() => helpers = new(); public static void InitExtensionEnv() => helpers.Clear(); private static readonly List helpers = new(); readonly Action action; public CompletedHelper([In] Action action) { helpers.Add(this); this.action = action; } ~CompletedHelper() { helpers.Remove(this); } public void InternalCompleted(AsyncOperation obj) { if (obj.progress < 0.99f) return; action.Invoke(); helpers.Remove(this); } } } public static partial class RectTransformExtension { private static bool IsDisableAdjustSizeToContainsChilds2DeferUpdates = false; public static void InitExtensionEnv() { IsDisableAdjustSizeToContainsChilds2DeferUpdates = false; } public class AdjustSizeIgnore : MonoBehaviour { } #if UNITY_EDITOR [UnityEditor.MenuItem("Convention/DisableAdjustSize", priority = 100000)] #endif public static void DisableAdjustSizeToContainsChilds2DeferUpdates() { IsDisableAdjustSizeToContainsChilds2DeferUpdates = true; } #if UNITY_EDITOR [UnityEditor.MenuItem("Convention/EnableAdjustSize", priority = 100000)] #endif public static void AppleAndEnableAdjustSizeToContainsChilds() { IsDisableAdjustSizeToContainsChilds2DeferUpdates = false; } public static void AdjustSizeToContainsChilds([In] RectTransform rectTransform, Vector2 min, Vector2 max, RectTransform.Axis? axis) { if (IsDisableAdjustSizeToContainsChilds2DeferUpdates) return; LayoutRebuilder.ForceRebuildLayoutImmediate(rectTransform); bool stats = false; List currentList = new(), nextList = new(); var corners = new Vector3[4]; foreach (RectTransform item in rectTransform) { currentList.Add(item); } do { currentList.AddRange(nextList); nextList.Clear(); foreach (RectTransform childTransform in currentList) { if (childTransform.gameObject.activeInHierarchy == false) continue; if (childTransform.name.ToLower().Contains("")) continue; if (childTransform.name.ToLower().Contains($"")) continue; if (childTransform.GetComponents().Length != 0) continue; stats = true; foreach (RectTransform item in childTransform) { nextList.Add(item); } childTransform.GetWorldCorners(corners); foreach (var corner in corners) { Vector2 localCorner = rectTransform.InverseTransformPoint(corner); if (float.IsNaN(localCorner.x) || float.IsNaN(localCorner.y)) break; min.x = Mathf.Min(min.x, localCorner.x); min.y = Mathf.Min(min.y, localCorner.y); max.x = Mathf.Max(max.x, localCorner.x); max.y = Mathf.Max(max.y, localCorner.y); } } currentList.Clear(); } while (nextList.Count > 0); if (stats) { if ((axis.HasValue && axis.Value == RectTransform.Axis.Vertical) || (rectTransform.anchorMin.x == 0 && rectTransform.anchorMax.x == 1 && rectTransform.anchorMin.y == rectTransform.anchorMax.y)) { rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, max.y - min.y); } else if ((axis.HasValue && axis.Value == RectTransform.Axis.Horizontal) || (rectTransform.anchorMin.y == 0 && rectTransform.anchorMax.y == 1 && rectTransform.anchorMin.x == rectTransform.anchorMax.x)) { rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, max.x - min.x); } else { rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, max.x - min.x); rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, max.y - min.y); } } } public static void AdjustSizeToContainsChilds([In] RectTransform rectTransform, RectTransform.Axis axis) { if (IsDisableAdjustSizeToContainsChilds2DeferUpdates) return; Vector2 min = new Vector2(float.MaxValue, float.MaxValue); Vector2 max = new Vector2(float.MinValue, float.MinValue); AdjustSizeToContainsChilds(rectTransform, min, max, axis); } public static void AdjustSizeToContainsChilds([In] RectTransform rectTransform) { if (IsDisableAdjustSizeToContainsChilds2DeferUpdates) return; Vector2 min = new Vector2(float.MaxValue, float.MaxValue); Vector2 max = new Vector2(float.MinValue, float.MinValue); AdjustSizeToContainsChilds(rectTransform, min, max, null); } internal static void SetParentAndResizeWithoutNotifyBaseWindowPlane([In] RectTransform parent, [In] RectTransform child, Rect rect, bool isAdjustSizeToContainsChilds) { child.SetParent(parent, false); child.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Left, rect.x, rect.width); child.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Top, rect.y, rect.height); if (isAdjustSizeToContainsChilds) AdjustSizeToContainsChilds(parent); } internal static void SetParentAndResizeWithoutNotifyBaseWindowPlane(RectTransform parent, RectTransform child, bool isAdjustSizeToContainsChilds) { child.SetParent(parent, false); if (isAdjustSizeToContainsChilds) AdjustSizeToContainsChilds(parent); } public static void SetParentAndResize(RectTransform parent, RectTransform child, Rect rect, bool isAdjustSizeToContainsChilds) { if (parent.GetComponents().Length != 0) { parent.GetComponents()[0].AddChild(child, rect, isAdjustSizeToContainsChilds); } else { SetParentAndResizeWithoutNotifyBaseWindowPlane(parent, child, rect, isAdjustSizeToContainsChilds); } } public static void SetParentAndResize(RectTransform parent, RectTransform child, bool isAdjustSizeToContainsChilds) { if (parent.GetComponents().Length != 0) { parent.GetComponents()[0].AddChild(child, isAdjustSizeToContainsChilds); } else { SetParentAndResizeWithoutNotifyBaseWindowPlane(parent, child, isAdjustSizeToContainsChilds); } } public static bool IsVisible([In] RectTransform rectTransform, [In, Opt] Camera camera = null) { if (camera == null) camera = Camera.main; Transform camTransform = camera.transform; Vector3[] corners = new Vector3[4]; rectTransform.GetWorldCorners(corners); foreach (var worldPos in corners) { Vector2 viewPos = camera.WorldToViewportPoint(worldPos); Vector3 dir = (worldPos - camTransform.position).normalized; float dot = Vector3.Dot(camTransform.forward, dir); if (dot <= 0 || viewPos.x < 0 || viewPos.x > 1 || viewPos.y < 0 || viewPos.y > 1) return false; } return true; } } public static partial class SkyExtension { public static Material GetSky() { return RenderSettings.skybox; } public static void Load([In][Opt, When("If you sure")] Material skybox) { RenderSettings.skybox = skybox; } public static void Rotation(float angle) { RenderSettings.skybox.SetFloat("_Rotation", angle); } } public static partial class SceneExtension { public static void Load(string name) { SceneManager.LoadScene(name, LoadSceneMode.Additive); } public static void Load(string name, out AsyncOperation async) { async = SceneManager.LoadSceneAsync(name, LoadSceneMode.Additive); } public static void Unload(string name) { SceneManager.UnloadSceneAsync(name); } public static Scene GetScene(string name) { return SceneManager.GetSceneByName(name); } } public static class GameObjectExtension { /// /// 递归设置GameObject及其所有子物体的Layer /// public static void SetLayerRecursively(GameObject gameObject, int layer) { gameObject.layer = layer; foreach (Transform t in gameObject.transform) { SetLayerRecursively(t.gameObject, layer); } } /// /// 递归设置GameObject及其所有子物体的Tag /// public static void SetTagRecursively(GameObject gameObject, string tag) { gameObject.tag = tag; foreach (Transform t in gameObject.transform) { SetTagRecursively(t.gameObject, tag); } } /// /// 递归启用/禁用所有Collider组件 /// public static void SetCollisionRecursively(GameObject gameObject, bool enabled) { var colliders = gameObject.GetComponentsInChildren(); foreach (var collider in colliders) { collider.enabled = enabled; } } /// /// 递归启用/禁用所有Renderer组件 /// public static void SetVisualRecursively(GameObject gameObject, bool enabled) { var renderers = gameObject.GetComponentsInChildren(); foreach (var renderer in renderers) { renderer.enabled = enabled; } } /// /// 获取指定Tag的所有子组件 /// public static T[] GetComponentsInChildrenWithTag(GameObject gameObject, string tag) where T : Component { List results = new List(); if (gameObject.CompareTag(tag)) { var component = gameObject.GetComponent(); if (component != null) results.Add(component); } foreach (Transform t in gameObject.transform) { results.AddRange(GetComponentsInChildrenWithTag(t.gameObject, tag)); } return results.ToArray(); } /// /// 在父物体中查找组件 /// public static T GetComponentInParents(GameObject gameObject) where T : Component { for (Transform t = gameObject.transform; t != null; t = t.parent) { T result = t.GetComponent(); if (result != null) return result; } return null; } /// /// 获取或添加组件 /// public static T GetOrAddComponent(GameObject gameObject) where T : Component { T component = gameObject.GetComponent(); return component ?? gameObject.AddComponent(); } } public static class TransformExtension { /// /// 获取所有子物体 /// public static List GetAllChildren(this Transform transform) { List children = new List(); foreach (Transform child in transform) { children.Add(child); children.AddRange(child.GetAllChildren()); } return children; } /// /// 销毁所有子物体 /// public static void DestroyAllChildren(this Transform transform) { for (int i = transform.childCount - 1; i >= 0; i--) { UnityEngine.Object.Destroy(transform.GetChild(i).gameObject); } } /// /// 设置父物体并保持世界坐标 /// public static void SetParentKeepWorldPosition(this Transform transform, Transform parent) { Vector3 worldPos = transform.position; Quaternion worldRot = transform.rotation; transform.SetParent(parent); transform.position = worldPos; transform.rotation = worldRot; } } public static class CoroutineExtension { /// /// 延迟执行 /// public static IEnumerator Delay(float delay, Action action) { yield return new WaitForSeconds(delay); action?.Invoke(); } /// /// 延迟执行并返回结果 /// public static IEnumerator Delay(float delay, Func action, Action callback) { yield return new WaitForSeconds(delay); callback?.Invoke(action()); } /// /// 等待直到条件满足 /// public static IEnumerator WaitUntil(Func condition, Action onComplete = null) { yield return new WaitUntil(condition); onComplete?.Invoke(); } /// /// 等待直到条件满足,带超时 /// public static IEnumerator WaitUntil(Func condition, float timeout, Action onComplete = null, Action onTimeout = null) { float elapsedTime = 0; while (!condition() && elapsedTime < timeout) { elapsedTime += Time.deltaTime; yield return null; } if (elapsedTime >= timeout) { onTimeout?.Invoke(); } else { onComplete?.Invoke(); } } /// /// 执行动画曲线 /// public static IEnumerator Animate(float duration, AnimationCurve curve, Action onUpdate) { float elapsedTime = 0; while (elapsedTime < duration) { elapsedTime += Time.deltaTime; float normalizedTime = elapsedTime / duration; float evaluatedValue = curve.Evaluate(normalizedTime); onUpdate?.Invoke(evaluatedValue); yield return null; } onUpdate?.Invoke(curve.Evaluate(1)); } /// /// 执行线性插值 /// public static IEnumerator Lerp(T start, T end, float duration, Action onUpdate, Func lerpFunction) { float elapsedTime = 0; while (elapsedTime < duration) { elapsedTime += Time.deltaTime; float normalizedTime = elapsedTime / duration; T current = lerpFunction(start, end, normalizedTime); onUpdate?.Invoke(current); yield return null; } onUpdate?.Invoke(end); } /// /// 执行Vector3插值 /// public static IEnumerator LerpVector3(Vector3 start, Vector3 end, float duration, Action onUpdate) { yield return Lerp(start, end, duration, onUpdate, Vector3.Lerp); } /// /// 执行Quaternion插值 /// public static IEnumerator LerpQuaternion(Quaternion start, Quaternion end, float duration, Action onUpdate) { yield return Lerp(start, end, duration, onUpdate, Quaternion.Lerp); } /// /// 执行float插值 /// public static IEnumerator LerpFloat(float start, float end, float duration, Action onUpdate) { yield return Lerp(start, end, duration, onUpdate, Mathf.Lerp); } } public static class ScriptingDefineUtility { #if UNITY_EDITOR #if UNITY_6000_0_OR_NEWER public static void Add(string define, NamedBuildTarget target, bool log = false) { string definesString = PlayerSettings.GetScriptingDefineSymbols(target); if (definesString.Contains(define)) return; string[] allDefines = definesString.Split(';'); ArrayUtility.Add(ref allDefines, define); definesString = string.Join(";", allDefines); PlayerSettings.SetScriptingDefineSymbols(target, definesString); Debug.Log("Added \"" + define + "\" from " + EditorUserBuildSettings.selectedBuildTargetGroup + " Scripting define in Player Settings"); } public static void Remove(string define, NamedBuildTarget target, bool log = false) { string definesString = PlayerSettings.GetScriptingDefineSymbols(target); if (!definesString.Contains(define)) return; string[] allDefines = definesString.Split(';'); ArrayUtility.Remove(ref allDefines, define); definesString = string.Join(";", allDefines); PlayerSettings.SetScriptingDefineSymbols(target, definesString); Debug.Log("Removed \"" + define + "\" from " + EditorUserBuildSettings.selectedBuildTargetGroup + " Scripting define in Player Settings"); } public static void Add(string define, BuildTargetGroup target, bool log = false) { Add(define, NamedBuildTarget.FromBuildTargetGroup(target), log); } public static void Remove(string define, BuildTargetGroup target, bool log = false) { Remove(define, NamedBuildTarget.FromBuildTargetGroup(target), log); } #else public static void Add(string define, BuildTargetGroup target, bool log = false) { string definesString = PlayerSettings.GetScriptingDefineSymbolsForGroup(target); if (definesString.Contains(define)) return; string[] allDefines = definesString.Split(';'); ArrayUtility.Add(ref allDefines, define); definesString = string.Join(";", allDefines); PlayerSettings.SetScriptingDefineSymbolsForGroup(target, definesString); Debug.Log("Added \"" + define + "\" from " + EditorUserBuildSettings.selectedBuildTargetGroup + " Scripting define in Player Settings"); } public static void Remove(string define, BuildTargetGroup target, bool log = false) { string definesString = PlayerSettings.GetScriptingDefineSymbolsForGroup(target); if (!definesString.Contains(define)) return; string[] allDefines = definesString.Split(';'); ArrayUtility.Remove(ref allDefines, define); definesString = string.Join(";", allDefines); PlayerSettings.SetScriptingDefineSymbolsForGroup(target, definesString); Debug.Log("Removed \"" + define + "\" from " + EditorUserBuildSettings.selectedBuildTargetGroup + " Scripting define in Player Settings"); } #endif #endif } } namespace Convention { public static class StringExtension { public static void InitExtensionEnv() { CurrentStringTransformer = null; MyLazyTransformer.Clear(); } public static string LimitString([In] object data, int maxLength = 50) { return LimitString(data.ToString(), maxLength); } public static string LimitString([In] in string data, int maxLength = 50) { if (data.Length <= maxLength) return data; var insideStr = "\n...\n...\n"; int headLength = maxLength / 2; int tailLength = maxLength - headLength - insideStr.Length; return data[..headLength] + insideStr + data[^tailLength..]; } public enum Side { Left, Right, Center } public static string FillString([In] object data, int maxLength = 50, char fillChar = ' ', Side side = Side.Right) { return FillString(data.ToString(), maxLength, fillChar, side); } public static string FillString([In] in string data, int maxLength = 50, char fillChar = ' ', Side side = Side.Right) { if (data.Length >= maxLength) return data; var fillStr = new string(fillChar, maxLength - data.Length); switch (side) { case Side.Left: return fillStr + data; case Side.Right: return data + fillStr; case Side.Center: int leftLength = (maxLength - data.Length) / 2; int rightLength = maxLength - leftLength - data.Length; return new string(fillChar, leftLength) + data + new string(fillChar, rightLength); default: return data; } } public static List BytesToStrings([In] IEnumerable bytes) { return BytesToStrings(bytes, Encoding.UTF8); } public static List BytesToStrings([In] IEnumerable bytes, Encoding encoding) { return bytes.ToList().ConvertAll(x => encoding.GetString(x)); } private static Dictionary MyLazyTransformer = new(); public class StringTransformer { [Serializable, ArgPackage] public class StringContentTree { public string leaf = null; public Dictionary branch = null; } private StringContentTree contents; public StringTransformer([In] string transformerFile) { var file = new ToolFile(transformerFile); contents = file.LoadAsRawJson(); } public string Transform([In] string stringName) { if (contents == null || contents.branch == null) return stringName; var keys = stringName.Split('.'); StringContentTree current = contents; foreach (var k in keys) { if (current.branch != null && current.branch.TryGetValue(k, out var next)) { current = next; } else { return stringName; // If any key is not found, return the original key } } return current.leaf ?? stringName; // Return leaf or original key if leaf is null } } private static StringTransformer MyCurrentStringTransformer = null; public static StringTransformer CurrentStringTransformer { get => MyCurrentStringTransformer; set { if (MyCurrentStringTransformer != value) { MyLazyTransformer.Clear(); MyCurrentStringTransformer = value; } } } public static string Transform([In] string stringName) { if (MyLazyTransformer.TryGetValue(stringName, out var result)) return result; return MyLazyTransformer[stringName] = CurrentStringTransformer != null ? CurrentStringTransformer.Transform(stringName) : stringName; } } } namespace Convention { [ArgPackage] public class ValueWrapper { private Func getter; private Action setter; public readonly Type type; public ValueWrapper([In, Opt] Func getter, [In, Opt] Action setter, [In] Type type) { this.getter = getter; this.setter = setter; this.type = type; } public bool IsChangeAble => setter != null; public bool IsObtainAble => getter != null; public void SetValue(object value) { setter(value); } public object GetValue() { return getter(); } } } namespace Convention { public static class TextureExtenion { public static Texture2D CropTexture(this Texture texture, Rect source) { RenderTexture active = RenderTexture.active; RenderTexture renderTexture = (RenderTexture.active = RenderTexture.GetTemporary(texture.width, texture.height, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Default, 8)); bool sRGBWrite = GL.sRGBWrite; GL.sRGBWrite = false; GL.Clear(clearDepth: false, clearColor: true, new Color(1f, 1f, 1f, 0f)); Graphics.Blit(texture, renderTexture); Texture2D texture2D = new Texture2D((int)source.width, (int)source.height, TextureFormat.ARGB32, mipChain: true, linear: false); texture2D.filterMode = FilterMode.Point; texture2D.ReadPixels(source, 0, 0); texture2D.Apply(); GL.sRGBWrite = sRGBWrite; RenderTexture.active = active; RenderTexture.ReleaseTemporary(renderTexture); return texture2D; } public static Texture2D CopyTexture(this Texture texture) { return CropTexture(texture, new(0, 0, texture.width, texture.height)); } public static Sprite ToSprite(this Texture2D texture) { return Sprite.Create(texture, new(0, 0, texture.width, texture.height), new(0.5f, 0.5f)); } } } namespace Convention { public static partial class ConventionUtility { public static object GetDefault([In] Type type) { if (type.IsClass) return null; else return Activator.CreateInstance(type); } } public static partial class ConventionUtility { public static byte[] ReadAllBytes(this BinaryReader reader) { const int bufferSize = 4096; using (var ms = new MemoryStream()) { byte[] buffer = new byte[bufferSize]; int count; while ((count = reader.Read(buffer, 0, buffer.Length)) != 0) ms.Write(buffer, 0, count); return ms.ToArray(); } } } } namespace Convention { public static partial class Utility { /// /// 生成期望类型的栈实例 /// /// 期望类型 /// public static object CreateStack([In] Type type) { var stackType = typeof(Stack<>).MakeGenericType(type); var stackInstance = Activator.CreateInstance(stackType); return stackInstance; } /// /// 生成期望类型的队列实例 /// /// 期望类型 /// public static object CreateQueue([In] Type type) { var queueType = typeof(Queue<>).MakeGenericType(type); var queueInstance = Activator.CreateInstance(queueType); return queueInstance; } /// /// 生成期望类型的列表实例 /// /// 期望类型 /// public static object CreateList([In] Type type) { var listType = typeof(List<>).MakeGenericType(type); var listInstance = Activator.CreateInstance(listType); return listInstance; } /// /// 生成期望键值对类型的字典实例 /// /// 期望键类型 /// 期望值类型 /// public static object CreateDictionary([In] Type keyType, [In] Type valueType) { var dictType = typeof(Dictionary<,>).MakeGenericType(keyType, valueType); var dictInstance = Activator.CreateInstance(dictType); return dictInstance; } } } namespace Convention { public static class BinarySerializeUtility { #region Base public static void WriteBool(BinaryWriter writer, bool data) { writer.Write(data); } public static bool ReadBool(BinaryReader reader) { return reader.ReadBoolean(); } public static void WriteInt(BinaryWriter writer, int data) { writer.Write(data); } public static int ReadInt(BinaryReader reader) { return reader.ReadInt32(); } public static void WriteFloat(BinaryWriter writer, float data) { writer.Write(data); } public static float ReadFloat(BinaryReader reader) { return reader.ReadSingle(); } public static void WriteChar(BinaryWriter writer, char data) { writer.Write(data); } public static char ReadChar(BinaryReader reader) { return reader.ReadChar(); } public static void WriteString(BinaryWriter writer, string data) { writer.Write(data); } public static string ReadString(BinaryReader reader) { return reader.ReadString(); } public static void WriteVec3(BinaryWriter writer, Vector3 data) { writer.Write(data.x); writer.Write(data.y); writer.Write(data.z); } public static Vector3 ReadVec3(BinaryReader reader) { float x, y, z; x = reader.ReadSingle(); y = reader.ReadSingle(); z = reader.ReadSingle(); return new Vector3(x, y, z); } public static void WriteVec2(BinaryWriter writer, Vector2 data) { writer.Write(data.x); writer.Write(data.y); } public static Vector2 ReadVec2(BinaryReader reader) { float x, y; x = reader.ReadSingle(); y = reader.ReadSingle(); return new Vector2(x, y); } #endregion #region Serialize #region Int public static void SerializeNativeArray(BinaryWriter writer, NativeArray array, int start = 0, int end = int.MaxValue) { int e = Mathf.Min(array.Length, end); writer.Write(e - start); while (start < e) { writer.Write(array[start++]); } } public static int DeserializeNativeArray(BinaryReader reader, ref NativeArray array, int start = 0) { int count = reader.ReadInt32(); if (array.Length < count) array.ResizeArray(count); for (int i = 0; i < count; i++) { array[start + i] = reader.ReadInt32(); } return count; } public static void SerializeArray(BinaryWriter writer, int[] array, int start = 0, int end = int.MaxValue) { int e = Mathf.Min(array.Length, end); writer.Write(e - start); while (start < e) { writer.Write(array[start++]); } } public static int[] DeserializeIntArray(BinaryReader reader, int start = 0) { int count = reader.ReadInt32(); int[] array = new int[count]; for (int i = 0; i < count; i++) { array[start + i] = reader.ReadInt32(); } return array; } #endregion #region Float public static void SerializeNativeArray(BinaryWriter writer, NativeArray array, int start = 0, int end = int.MaxValue) { int e = Mathf.Min(array.Length, end); writer.Write(e - start); while (start < e) { writer.Write(array[start++]); } } public static int DeserializeNativeArray(BinaryReader reader, ref NativeArray array, int start = 0) { int count = reader.ReadInt32(); if (array.Length < count) array.ResizeArray(count); for (int i = 0; i < count; i++) { array[start + i] = reader.ReadSingle(); } return count; } public static void SerializeArray(BinaryWriter writer, float[] array, int start = 0, int end = int.MaxValue) { int e = Mathf.Min(array.Length, end); writer.Write(e - start); while (start < e) { writer.Write(array[start++]); } } public static float[] DeserializeFloatArray(BinaryReader reader, int start = 0) { int count = reader.ReadInt32(); float[] array = new float[count]; for (int i = 0; i < count; i++) { array[start + i] = reader.ReadSingle(); } return array; } #endregion #region String public static void SerializeArray(BinaryWriter writer, string[] array, int start = 0, int end = int.MaxValue) { int e = Mathf.Min(array.Length, end); writer.Write(e - start); while (start < e) { writer.Write(array[start++]); } } public static string[] DeserializeStringArray(BinaryReader reader, int start = 0) { int count = reader.ReadInt32(); string[] array = new string[count]; for (int i = 0; i < count; i++) { array[start + i] = reader.ReadString(); } return array; } #endregion #region Vector3 public static void SerializeNativeArray(BinaryWriter writer, NativeArray array, int start = 0, int end = int.MaxValue) { int e = Mathf.Min(array.Length, end); writer.Write(e - start); while (start < e) { writer.Write(array[start].x); writer.Write(array[start].y); writer.Write(array[start].z); start++; } } public static int DeserializeNativeArray(BinaryReader reader, ref NativeArray array, int start = 0) { int count = reader.ReadInt32(); if (array.Length < count) array.ResizeArray(count); float x, y, z; for (int i = 0; i < count; i++) { x = reader.ReadSingle(); y = reader.ReadSingle(); z = reader.ReadSingle(); array[start + i] = new(x, y, z); } return count; } public static void SerializeArray(BinaryWriter writer, Vector3[] array, int start = 0, int end = int.MaxValue) { int e = Mathf.Min(array.Length, end); writer.Write(e - start); while (start < e) { writer.Write(array[start].x); writer.Write(array[start].y); writer.Write(array[start].z); start++; } } public static Vector3[] DeserializeVec3Array(BinaryReader reader, int start = 0) { int count = reader.ReadInt32(); Vector3[] array = new Vector3[count]; float x, y, z; for (int i = 0; i < count; i++) { x = reader.ReadSingle(); y = reader.ReadSingle(); z = reader.ReadSingle(); array[start + i] = new(x, y, z); } return array; } #endregion #region Vector2 public static void SerializeNativeArray(BinaryWriter writer, NativeArray array, int start = 0, int end = int.MaxValue) { int e = Mathf.Min(array.Length, end); writer.Write(e - start); while (start < e) { writer.Write(array[start].x); writer.Write(array[start].y); start++; } } public static int DeserializeNativeArray(BinaryReader reader, ref NativeArray array, int start = 0) { int count = reader.ReadInt32(); if (array.Length < count) array.ResizeArray(count); float x, y; for (int i = 0; i < count; i++) { x = reader.ReadSingle(); y = reader.ReadSingle(); array[start + i] = new(x, y); } return count; } public static void SerializeArray(BinaryWriter writer, Vector2[] array, int start = 0, int end = int.MaxValue) { int e = Mathf.Min(array.Length, end); writer.Write(e - start); while (start < e) { writer.Write(array[start].x); writer.Write(array[start].y); start++; } } public static Vector2[] DeserializeVec2Array(BinaryReader reader, int start = 0) { int count = reader.ReadInt32(); Vector2[] array = new Vector2[count]; float x, y; for (int i = 0; i < count; i++) { x = reader.ReadSingle(); y = reader.ReadSingle(); array[start + i] = new(x, y); } return array; } #endregion #endregion } public static partial class Utility { /// /// 获取类型的友好显示名称,支持泛型、数组等复杂类型 /// public static string GetFriendlyName(this Type type) { if (type == null) return null; // 处理泛型类型 if (type.IsGenericType) { // 特殊处理可空类型(Nullable) if (type.IsNullable()) { return $"{GetFriendlyName(type.GetGenericArguments()[0])}?"; } var sb = new StringBuilder(); // 获取类型基础名称(移除 `1, `2 等后缀) string baseName = type.Name.Contains('`') ? type.Name[..type.Name.IndexOf('`')] : type.Name; sb.Append(baseName); sb.Append('<'); // 递归处理泛型参数 Type[] args = type.GetGenericArguments(); for (int i = 0; i < args.Length; i++) { if (i > 0) sb.Append(", "); sb.Append(GetFriendlyName(args[i])); } sb.Append('>'); return sb.ToString(); } // 处理数组类型 if (type.IsArray) { string elementName = GetFriendlyName(type.GetElementType()); int rank = type.GetArrayRank(); return rank == 1 ? $"{elementName}[]" : $"{elementName}[{new string(',', rank - 1)}]"; } // 处理指针类型 if (type.IsPointer) { return $"{GetFriendlyName(type.GetElementType())}*"; } // 处理引用类型(ref/out 参数) if (type.IsByRef) { return $"{GetFriendlyName(type.GetElementType())}&"; } // 普通类型直接返回名称 return type.Name; } /// /// 判断是否为可空类型 /// public static bool IsNullable(this Type type) { return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); } } }