2026-06-08 23:25:33 +07:00
#if UNITY_EDITOR
using System.Collections ;
using System.Collections.Generic ;
using System.IO ;
using System.Text ;
using System.Text.RegularExpressions ;
using System.Reflection ;
using System.Linq ;
using UnityEngine ;
using UnityEngine.Rendering ;
using UnityEngine.Experimental.Rendering ;
using UnityEditor ;
using Type = System . Type ;
using static VFolders . Libs . VUtils ;
using static VFolders . Libs . VGUI ;
namespace VFolders.Libs
{
public static class VUtils
{
#region Text
public static string Remove ( this string s , string toRemove )
{
if ( toRemove = = "" ) return s ;
return s . Replace ( toRemove , "" ) ;
}
public static bool IsEmpty ( this string s ) = > s = = "" ;
public static bool IsNullOrEmpty ( this string s ) = > string . IsNullOrEmpty ( s ) ;
#endregion
#region IEnumerables
public static T AddAt < T > ( this List < T > l , T r , int i )
{
if ( i < 0 ) i = 0 ;
if ( i > = l . Count )
l . Add ( r ) ;
else
l . Insert ( i , r ) ;
return r ;
}
public static T RemoveLast < T > ( this List < T > l )
{
if ( ! l . Any ( ) ) return default ;
var r = l . Last ( ) ;
l . RemoveAt ( l . Count - 1 ) ;
return r ;
}
public static void Add < T > ( this List < T > list , params T [ ] items )
{
foreach ( var r in items )
list . Add ( r ) ;
}
public static int LastIndex < T > ( this List < T > l ) = > l . Count - 1 ; // toremove
// public static T GetAtWrapped<T>(this List<T> list, int i) // toremove
// {
// while (i < 0) i += list.Count;
// while (i >= list.Count) i -= list.Count;
// return list[i];
// }
#endregion
#region Linq
public static T NextTo < T > ( this IEnumerable < T > e , T to ) = > e . SkipWhile ( r = > ! r . Equals ( to ) ) . Skip ( 1 ) . FirstOrDefault ( ) ;
public static T PreviousTo < T > ( this IEnumerable < T > e , T to ) = > e . Reverse ( ) . SkipWhile ( r = > ! r . Equals ( to ) ) . Skip ( 1 ) . FirstOrDefault ( ) ;
public static T NextToOtFirst < T > ( this IEnumerable < T > e , T to ) = > e . NextTo ( to ) ? ? e . First ( ) ;
public static T PreviousToOrLast < T > ( this IEnumerable < T > e , T to ) = > e . PreviousTo ( to ) ? ? e . Last ( ) ;
public static Dictionary < TKey , TValue > MergeDictionaries < TKey , TValue > ( IEnumerable < Dictionary < TKey , TValue > > dicts )
{
if ( dicts . Count ( ) = = 0 ) return null ;
if ( dicts . Count ( ) = = 1 ) return dicts . First ( ) ;
var mergedDict = new Dictionary < TKey , TValue > ( dicts . First ( ) ) ;
foreach ( var dict in dicts . Skip ( 1 ) )
foreach ( var r in dict )
if ( ! mergedDict . ContainsKey ( r . Key ) )
mergedDict . Add ( r . Key , r . Value ) ;
return mergedDict ;
}
public static IEnumerable < T > InsertFirst < T > ( this IEnumerable < T > ie , T t ) = > new [ ] { t } . Concat ( ie ) ;
public static bool None < T > ( this IEnumerable < T > ie , System . Func < T , bool > f ) = > ! ie . Any ( f ) ;
public static bool None < T > ( this IEnumerable < T > ie ) = > ! ie . Any ( ) ;
public static int IndexOfFirst < T > ( this List < T > list , System . Func < T , bool > f ) = > list . FirstOrDefault ( f ) is T t ? list . IndexOf ( t ) : - 1 ;
public static int IndexOfLast < T > ( this List < T > list , System . Func < T , bool > f ) = > list . LastOrDefault ( f ) is T t ? list . IndexOf ( t ) : - 1 ;
public static void SortBy < T , T2 > ( this List < T > list , System . Func < T , T2 > keySelector ) where T2 : System . IComparable = > list . Sort ( ( q , w ) = > keySelector ( q ) . CompareTo ( keySelector ( w ) ) ) ;
public static void RemoveValue < TKey , TValue > ( this IDictionary < TKey , TValue > dictionary , TValue value )
{
if ( dictionary . FirstOrDefault ( r = > r . Value . Equals ( value ) ) is var kvp )
dictionary . Remove ( kvp ) ;
}
public static TValue GetValueOrDefault < TKey , TValue > ( this Dictionary < TKey , TValue > dic , TKey key ) = > dic . ContainsKey ( key ) ? dic [ key ] : default ;
#endregion
#region Reflection
public static object GetFieldValue ( this object o , string fieldName , bool exceptionIfNotFound = true )
{
var type = ( o as Type ) ? ? o . GetType ( ) ;
var target = o is Type ? null : o ;
if ( type . GetFieldInfo ( fieldName ) is FieldInfo fieldInfo )
return fieldInfo . GetValue ( target ) ;
if ( exceptionIfNotFound )
throw new System . Exception ( $"Field '{fieldName}' not found in '{type.Name}' type and its parent types" ) ;
return null ;
}
public static object GetPropertyValue ( this object o , string propertyName , bool exceptionIfNotFound = true )
{
var type = ( o as Type ) ? ? o . GetType ( ) ;
var target = o is Type ? null : o ;
if ( type . GetPropertyInfo ( propertyName ) is PropertyInfo propertyInfo )
return propertyInfo . GetValue ( target ) ;
if ( exceptionIfNotFound )
throw new System . Exception ( $"Property '{propertyName}' not found in '{type.Name}' type and its parent types" ) ;
return null ;
}
public static object GetMemberValue ( this object o , string memberName , bool exceptionIfNotFound = true )
{
var type = ( o as Type ) ? ? o . GetType ( ) ;
var target = o is Type ? null : o ;
if ( type . GetFieldInfo ( memberName ) is FieldInfo fieldInfo )
return fieldInfo . GetValue ( target ) ;
if ( type . GetPropertyInfo ( memberName ) is PropertyInfo propertyInfo )
return propertyInfo . GetValue ( target ) ;
if ( exceptionIfNotFound )
throw new System . Exception ( $"Member '{memberName}' not found in '{type.Name}' type and its parent types" ) ;
return null ;
}
public static void SetFieldValue ( this object o , string fieldName , object value , bool exceptionIfNotFound = true )
{
var type = ( o as Type ) ? ? o . GetType ( ) ;
var target = o is Type ? null : o ;
if ( type . GetFieldInfo ( fieldName ) is FieldInfo fieldInfo )
fieldInfo . SetValue ( target , value ) ;
else if ( exceptionIfNotFound )
throw new System . Exception ( $"Field '{fieldName}' not found in '{type.Name}' type and its parent types" ) ;
}
public static void SetPropertyValue ( this object o , string propertyName , object value , bool exceptionIfNotFound = true )
{
var type = ( o as Type ) ? ? o . GetType ( ) ;
var target = o is Type ? null : o ;
if ( type . GetPropertyInfo ( propertyName ) is PropertyInfo propertyInfo )
propertyInfo . SetValue ( target , value ) ;
else if ( exceptionIfNotFound )
throw new System . Exception ( $"Property '{propertyName}' not found in '{type.Name}' type and its parent types" ) ;
}
public static void SetMemberValue ( this object o , string memberName , object value , bool exceptionIfNotFound = true )
{
var type = ( o as Type ) ? ? o . GetType ( ) ;
var target = o is Type ? null : o ;
if ( type . GetFieldInfo ( memberName ) is FieldInfo fieldInfo )
fieldInfo . SetValue ( target , value ) ;
else if ( type . GetPropertyInfo ( memberName ) is PropertyInfo propertyInfo )
propertyInfo . SetValue ( target , value ) ;
else if ( exceptionIfNotFound )
throw new System . Exception ( $"Member '{memberName}' not found in '{type.Name}' type and its parent types" ) ;
}
public static object InvokeMethod ( this object o , string methodName , params object [ ] parameters )
{
var type = ( o as Type ) ? ? o . GetType ( ) ;
var target = o is Type ? null : o ;
if ( type . GetMethodInfo ( methodName , parameters . Select ( r = > r . GetType ( ) ) . ToArray ( ) ) is MethodInfo methodInfo )
return methodInfo . Invoke ( target , parameters ) ;
throw new System . Exception ( $"Method '{methodName}' not found in '{type.Name}' type, its parent types and interfaces" ) ;
}
static FieldInfo GetFieldInfo ( this Type type , string fieldName )
{
if ( fieldInfoCache . TryGetValue ( type , out var fieldInfosByNames ) )
if ( fieldInfosByNames . TryGetValue ( fieldName , out var fieldInfo ) )
return fieldInfo ;
if ( ! fieldInfoCache . ContainsKey ( type ) )
fieldInfoCache [ type ] = new Dictionary < string , FieldInfo > ( ) ;
for ( var curType = type ; curType ! = null ; curType = curType . BaseType )
if ( curType . GetField ( fieldName , maxBindingFlags ) is FieldInfo fieldInfo )
return fieldInfoCache [ type ] [ fieldName ] = fieldInfo ;
return fieldInfoCache [ type ] [ fieldName ] = null ;
}
static Dictionary < Type , Dictionary < string , FieldInfo > > fieldInfoCache = new Dictionary < Type , Dictionary < string , FieldInfo > > ( ) ;
static PropertyInfo GetPropertyInfo ( this Type type , string propertyName )
{
if ( propertyInfoCache . TryGetValue ( type , out var propertyInfosByNames ) )
if ( propertyInfosByNames . TryGetValue ( propertyName , out var propertyInfo ) )
return propertyInfo ;
if ( ! propertyInfoCache . ContainsKey ( type ) )
propertyInfoCache [ type ] = new Dictionary < string , PropertyInfo > ( ) ;
for ( var curType = type ; curType ! = null ; curType = curType . BaseType )
if ( curType . GetProperty ( propertyName , maxBindingFlags ) is PropertyInfo propertyInfo )
return propertyInfoCache [ type ] [ propertyName ] = propertyInfo ;
return propertyInfoCache [ type ] [ propertyName ] = null ;
}
static Dictionary < Type , Dictionary < string , PropertyInfo > > propertyInfoCache = new Dictionary < Type , Dictionary < string , PropertyInfo > > ( ) ;
static MethodInfo GetMethodInfo ( this Type type , string methodName , params Type [ ] argumentTypes )
{
var methodHash = methodName . GetHashCode ( ) ^ argumentTypes . Aggregate ( 0 , ( hash , r ) = > hash ^ = r . GetHashCode ( ) ) ;
if ( methodInfoCache . TryGetValue ( type , out var methodInfosByHashes ) )
if ( methodInfosByHashes . TryGetValue ( methodHash , out var methodInfo ) )
return methodInfo ;
if ( ! methodInfoCache . ContainsKey ( type ) )
methodInfoCache [ type ] = new Dictionary < int , MethodInfo > ( ) ;
for ( var curType = type ; curType ! = null ; curType = curType . BaseType )
if ( curType . GetMethod ( methodName , maxBindingFlags , null , argumentTypes , null ) is MethodInfo methodInfo )
return methodInfoCache [ type ] [ methodHash ] = methodInfo ;
foreach ( var interfaceType in type . GetInterfaces ( ) )
if ( interfaceType . GetMethod ( methodName , maxBindingFlags , null , argumentTypes , null ) is MethodInfo methodInfo )
return methodInfoCache [ type ] [ methodHash ] = methodInfo ;
return methodInfoCache [ type ] [ methodHash ] = null ;
}
static Dictionary < Type , Dictionary < int , MethodInfo > > methodInfoCache = new Dictionary < Type , Dictionary < int , MethodInfo > > ( ) ;
2026-06-09 00:38:54 +07:00
public static T GetFieldValue < T > ( this object o , string fieldName , bool exceptionIfNotFound = true )
{
var val = o . GetFieldValue ( fieldName , exceptionIfNotFound ) ;
if ( val = = null ) return default ;
if ( typeof ( T ) = = typeof ( List < int > ) & & val is System . Collections . IEnumerable ienum & & ! ( val is List < int > ) )
{
var list = new List < int > ( ) ;
foreach ( var item in ienum ) { try { list . Add ( ( int ) ( dynamic ) item ) ; } catch { list . Add ( item . GetHashCode ( ) ) ; } }
return ( T ) ( object ) list ;
}
return ( T ) val ;
}
public static T GetPropertyValue < T > ( this object o , string propertyName , bool exceptionIfNotFound = true )
{
var val = o . GetPropertyValue ( propertyName , exceptionIfNotFound ) ;
if ( val = = null ) return default ;
if ( typeof ( T ) = = typeof ( List < int > ) & & val is System . Collections . IEnumerable ienum & & ! ( val is List < int > ) )
{
var list = new List < int > ( ) ;
foreach ( var item in ienum ) { try { list . Add ( ( int ) ( dynamic ) item ) ; } catch { list . Add ( item . GetHashCode ( ) ) ; } }
return ( T ) ( object ) list ;
}
return ( T ) val ;
}
public static T GetMemberValue < T > ( this object o , string memberName , bool exceptionIfNotFound = true )
{
var val = o . GetMemberValue ( memberName , exceptionIfNotFound ) ;
if ( val = = null ) return default ;
if ( typeof ( T ) = = typeof ( List < int > ) & & val is System . Collections . IEnumerable ienum & & ! ( val is List < int > ) )
{
var list = new List < int > ( ) ;
foreach ( var item in ienum ) { try { list . Add ( ( int ) ( dynamic ) item ) ; } catch { list . Add ( item . GetHashCode ( ) ) ; } }
return ( T ) ( object ) list ;
}
return ( T ) val ;
}
2026-06-08 23:25:33 +07:00
public static T InvokeMethod < T > ( this object o , string methodName , params object [ ] parameters ) = > ( T ) o . InvokeMethod ( methodName , parameters ) ;
public static List < Type > GetSubclasses ( this Type t ) = > t . Assembly . GetTypes ( ) . Where ( type = > type . IsSubclassOf ( t ) ) . ToList ( ) ;
public static object GetDefaultValue ( this FieldInfo f , params object [ ] constructorVars ) = > f . GetValue ( System . Activator . CreateInstance ( ( ( MemberInfo ) f ) . ReflectedType , constructorVars ) ) ;
public static object GetDefaultValue ( this FieldInfo f ) = > f . GetValue ( System . Activator . CreateInstance ( ( ( MemberInfo ) f ) . ReflectedType ) ) ;
public static IEnumerable < FieldInfo > GetFieldsWithoutBase ( this Type t ) = > t . GetFields ( ) . Where ( r = > ! t . BaseType . GetFields ( ) . Any ( rr = > rr . Name = = r . Name ) ) ;
public static IEnumerable < PropertyInfo > GetPropertiesWithoutBase ( this Type t ) = > t . GetProperties ( ) . Where ( r = > ! t . BaseType . GetProperties ( ) . Any ( rr = > rr . Name = = r . Name ) ) ;
public const BindingFlags maxBindingFlags = ( BindingFlags ) 62 ;
#endregion
#region Math
public static bool Approx ( this float f1 , float f2 ) = > Mathf . Approximately ( f1 , f2 ) ;
public static bool CloseTo ( this float f1 , float f2 , float distance ) = > f1 . DistTo ( f2 ) < = distance ;
public static float DistTo ( this float f1 , float f2 ) = > Mathf . Abs ( f1 - f2 ) ;
public static float Dist ( float f1 , float f2 ) = > Mathf . Abs ( f1 - f2 ) ;
public static float Avg ( float f1 , float f2 ) = > ( f1 + f2 ) / 2 ;
public static float Abs ( this float f ) = > Mathf . Abs ( f ) ;
public static int Abs ( this int f ) = > Mathf . Abs ( f ) ;
public static float Sign ( this float f ) = > Mathf . Sign ( f ) ;
public static float Clamp ( this float f , float f0 , float f1 ) = > Mathf . Clamp ( f , f0 , f1 ) ;
public static int Clamp ( this int f , int f0 , int f1 ) = > Mathf . Clamp ( f , f0 , f1 ) ;
public static float Clamp01 ( this float f ) = > Mathf . Clamp ( f , 0 , 1 ) ;
public static Vector2 Clamp01 ( this Vector2 f ) = > new Vector2 ( f . x . Clamp01 ( ) , f . y . Clamp01 ( ) ) ;
public static Vector3 Clamp01 ( this Vector3 f ) = > new Vector3 ( f . x . Clamp01 ( ) , f . y . Clamp01 ( ) , f . z . Clamp01 ( ) ) ;
public static float Pow ( this float f , float pow ) = > Mathf . Pow ( f , pow ) ;
public static int Pow ( this int f , int pow ) = > ( int ) Mathf . Pow ( f , pow ) ;
public static float Round ( this float f ) = > Mathf . Round ( f ) ;
public static float Ceil ( this float f ) = > Mathf . Ceil ( f ) ;
public static float Floor ( this float f ) = > Mathf . Floor ( f ) ;
public static int RoundToInt ( this float f ) = > Mathf . RoundToInt ( f ) ;
public static int CeilToInt ( this float f ) = > Mathf . CeilToInt ( f ) ;
public static int FloorToInt ( this float f ) = > Mathf . FloorToInt ( f ) ;
public static int ToInt ( this float f ) = > ( int ) f ;
public static float ToFloat ( this int f ) = > ( float ) f ;
public static float Sqrt ( this float f ) = > Mathf . Sqrt ( f ) ;
public static float Max ( this float f , float ff ) = > Mathf . Max ( f , ff ) ;
public static float Min ( this float f , float ff ) = > Mathf . Min ( f , ff ) ;
public static int Max ( this int f , int ff ) = > Mathf . Max ( f , ff ) ;
public static int Min ( this int f , int ff ) = > Mathf . Min ( f , ff ) ;
public static float Loop ( this float f , float boundMin , float boundMax )
{
while ( f < boundMin ) f + = boundMax - boundMin ;
while ( f > boundMax ) f - = boundMax - boundMin ;
return f ;
}
public static float Loop ( this float f , float boundMax ) = > f . Loop ( 0 , boundMax ) ;
public static float PingPong ( this float f , float boundMin , float boundMax ) = > boundMin + Mathf . PingPong ( f - boundMin , boundMax - boundMin ) ;
public static float PingPong ( this float f , float boundMax ) = > f . PingPong ( 0 , boundMax ) ;
public static float TriangleArea ( Vector2 A , Vector2 B , Vector2 C ) = > Vector3 . Cross ( A - B , A - C ) . z . Abs ( ) / 2 ;
public static Vector2 LineIntersection ( Vector2 A , Vector2 B , Vector2 C , Vector2 D )
{
var a1 = B . y - A . y ;
var b1 = A . x - B . x ;
var c1 = a1 * A . x + b1 * A . y ;
var a2 = D . y - C . y ;
var b2 = C . x - D . x ;
var c2 = a2 * C . x + b2 * C . y ;
var d = a1 * b2 - a2 * b1 ;
var x = ( b2 * c1 - b1 * c2 ) / d ;
var y = ( a1 * c2 - a2 * c1 ) / d ;
return new Vector2 ( x , y ) ;
}
public static float ProjectOn ( this Vector2 v , Vector2 on ) = > Vector3 . Project ( v , on ) . magnitude ;
public static float AngleTo ( this Vector2 v , Vector2 to ) = > Vector2 . Angle ( v , to ) ;
public static Vector2 Rotate ( this Vector2 v , float deg ) = > Quaternion . AngleAxis ( deg , Vector3 . forward ) * v ;
public static float Smoothstep ( this float f ) { f = f . Clamp01 ( ) ; return f * f * ( 3 - 2 * f ) ; }
public static float InverseLerp ( this Vector2 v , Vector2 a , Vector2 b )
{
var ab = b - a ;
var av = v - a ;
return Vector2 . Dot ( av , ab ) / Vector2 . Dot ( ab , ab ) ;
}
public static bool IsOdd ( this int i ) = > i % 2 = = 1 ;
public static bool IsEven ( this int i ) = > i % 2 = = 0 ;
public static bool IsInRange ( this int i , int a , int b ) = > i > = a & & i < = b ;
public static bool IsInRange ( this float i , float a , float b ) = > i > = a & & i < = b ;
public static bool IsInRangeOf ( this int i , IList list ) = > i . IsInRange ( 0 , list . Count - 1 ) ;
public static bool IsInRangeOf < T > ( this int i , T [ ] array ) = > i . IsInRange ( 0 , array . Length - 1 ) ;
#endregion
#region Lerping
public static float LerpT ( float lerpSpeed , float deltaTime ) = > 1 - Mathf . Exp ( - lerpSpeed * 2f * deltaTime ) ;
public static float LerpT ( float lerpSpeed ) = > LerpT ( lerpSpeed , Time . deltaTime ) ;
public static float Lerp ( float f1 , float f2 , float t ) = > Mathf . LerpUnclamped ( f1 , f2 , t ) ;
public static float Lerp ( ref float f1 , float f2 , float t ) = > f1 = Lerp ( f1 , f2 , t ) ;
public static Vector2 Lerp ( Vector2 f1 , Vector2 f2 , float t ) = > Vector2 . LerpUnclamped ( f1 , f2 , t ) ;
public static Vector2 Lerp ( ref Vector2 f1 , Vector2 f2 , float t ) = > f1 = Lerp ( f1 , f2 , t ) ;
public static Vector3 Lerp ( Vector3 f1 , Vector3 f2 , float t ) = > Vector3 . LerpUnclamped ( f1 , f2 , t ) ;
public static Vector3 Lerp ( ref Vector3 f1 , Vector3 f2 , float t ) = > f1 = Lerp ( f1 , f2 , t ) ;
public static Color Lerp ( Color f1 , Color f2 , float t ) = > Color . LerpUnclamped ( f1 , f2 , t ) ;
public static Color Lerp ( ref Color f1 , Color f2 , float t ) = > f1 = Lerp ( f1 , f2 , t ) ;
public static float Lerp ( float current , float target , float speed , float deltaTime ) = > Mathf . Lerp ( current , target , LerpT ( speed , deltaTime ) ) ;
public static float Lerp ( ref float current , float target , float speed , float deltaTime ) = > current = Lerp ( current , target , speed , deltaTime ) ;
public static Vector2 Lerp ( Vector2 current , Vector2 target , float speed , float deltaTime ) = > Vector2 . Lerp ( current , target , LerpT ( speed , deltaTime ) ) ;
public static Vector2 Lerp ( ref Vector2 current , Vector2 target , float speed , float deltaTime ) = > current = Lerp ( current , target , speed , deltaTime ) ;
public static Vector3 Lerp ( Vector3 current , Vector3 target , float speed , float deltaTime ) = > Vector3 . Lerp ( current , target , LerpT ( speed , deltaTime ) ) ;
public static Vector3 Lerp ( ref Vector3 current , Vector3 target , float speed , float deltaTime ) = > current = Lerp ( current , target , speed , deltaTime ) ;
public static float SmoothDamp ( float current , float target , float speed , ref float derivative , float deltaTime ) = > Mathf . SmoothDamp ( current , target , ref derivative , . 5f / speed , Mathf . Infinity , deltaTime ) ;
public static float SmoothDamp ( ref float current , float target , float speed , ref float derivative , float deltaTime ) = > current = SmoothDamp ( current , target , speed , ref derivative , deltaTime ) ;
public static float SmoothDamp ( float current , float target , float speed , ref float derivative ) = > SmoothDamp ( current , target , speed , ref derivative , Time . deltaTime ) ;
public static float SmoothDamp ( ref float current , float target , float speed , ref float derivative ) = > current = SmoothDamp ( current , target , speed , ref derivative , Time . deltaTime ) ;
public static Vector2 SmoothDamp ( Vector2 current , Vector2 target , float speed , ref Vector2 derivative , float deltaTime ) = > Vector2 . SmoothDamp ( current , target , ref derivative , . 5f / speed , Mathf . Infinity , deltaTime ) ;
public static Vector2 SmoothDamp ( ref Vector2 current , Vector2 target , float speed , ref Vector2 derivative , float deltaTime ) = > current = SmoothDamp ( current , target , speed , ref derivative , deltaTime ) ;
public static Vector2 SmoothDamp ( Vector2 current , Vector2 target , float speed , ref Vector2 derivative ) = > SmoothDamp ( current , target , speed , ref derivative , Time . deltaTime ) ;
public static Vector2 SmoothDamp ( ref Vector2 current , Vector2 target , float speed , ref Vector2 derivative ) = > current = SmoothDamp ( current , target , speed , ref derivative , Time . deltaTime ) ;
public static Vector3 SmoothDamp ( Vector3 current , Vector3 target , float speed , ref Vector3 derivative , float deltaTime ) = > Vector3 . SmoothDamp ( current , target , ref derivative , . 5f / speed , Mathf . Infinity , deltaTime ) ;
public static Vector3 SmoothDamp ( ref Vector3 current , Vector3 target , float speed , ref Vector3 derivative , float deltaTime ) = > current = SmoothDamp ( current , target , speed , ref derivative , deltaTime ) ;
public static Vector3 SmoothDamp ( Vector3 current , Vector3 target , float speed , ref Vector3 derivative ) = > SmoothDamp ( current , target , speed , ref derivative , Time . deltaTime ) ;
public static Vector3 SmoothDamp ( ref Vector3 current , Vector3 target , float speed , ref Vector3 derivative ) = > current = SmoothDamp ( current , target , speed , ref derivative , Time . deltaTime ) ;
#endregion
#region Colors
public static Color HSLToRGB ( float h , float s , float l )
{
float hue2Rgb ( float v1 , float v2 , float vH )
{
if ( vH < 0f )
vH + = 1f ;
if ( vH > 1f )
vH - = 1f ;
if ( 6f * vH < 1f )
return v1 + ( v2 - v1 ) * 6f * vH ;
if ( 2f * vH < 1f )
return v2 ;
if ( 3f * vH < 2f )
return v1 + ( v2 - v1 ) * ( 2f / 3f - vH ) * 6f ;
return v1 ;
}
if ( s . Approx ( 0 ) ) return new Color ( l , l , l ) ;
float k1 ;
if ( l < . 5f )
k1 = l * ( 1f + s ) ;
else
k1 = l + s - s * l ;
var k2 = 2f * l - k1 ;
float r , g , b ;
r = hue2Rgb ( k2 , k1 , h + 1f / 3 ) ;
g = hue2Rgb ( k2 , k1 , h ) ;
b = hue2Rgb ( k2 , k1 , h - 1f / 3 ) ;
return new Color ( r , g , b ) ;
}
public static Color LCHtoRGB ( float l , float c , float h )
{
l * = 100 ;
c * = 100 ;
h * = 360 ;
double xw = 0.948110 ;
double yw = 1.00000 ;
double zw = 1.07304 ;
float a = c * Mathf . Cos ( Mathf . Deg2Rad * h ) ;
float b = c * Mathf . Sin ( Mathf . Deg2Rad * h ) ;
float fy = ( l + 16 ) / 116 ;
float fx = fy + ( a / 500 ) ;
float fz = fy - ( b / 200 ) ;
float x = ( float ) System . Math . Round ( xw * ( ( System . Math . Pow ( fx , 3 ) > 0.008856 ) ? System . Math . Pow ( fx , 3 ) : ( ( fx - 16 / 116 ) / 7.787 ) ) , 5 ) ;
float y = ( float ) System . Math . Round ( yw * ( ( System . Math . Pow ( fy , 3 ) > 0.008856 ) ? System . Math . Pow ( fy , 3 ) : ( ( fy - 16 / 116 ) / 7.787 ) ) , 5 ) ;
float z = ( float ) System . Math . Round ( zw * ( ( System . Math . Pow ( fz , 3 ) > 0.008856 ) ? System . Math . Pow ( fz , 3 ) : ( ( fz - 16 / 116 ) / 7.787 ) ) , 5 ) ;
float r = x * 3.2406f - y * 1.5372f - z * 0.4986f ;
float g = - x * 0.9689f + y * 1.8758f + z * 0.0415f ;
float bValue = x * 0.0557f - y * 0.2040f + z * 1.0570f ;
r = r > 0.0031308f ? 1.055f * ( float ) System . Math . Pow ( r , 1 / 2.4 ) - 0.055f : r * 12.92f ;
g = g > 0.0031308f ? 1.055f * ( float ) System . Math . Pow ( g , 1 / 2.4 ) - 0.055f : g * 12.92f ;
bValue = bValue > 0.0031308f ? 1.055f * ( float ) System . Math . Pow ( bValue , 1 / 2.4 ) - 0.055f : bValue * 12.92f ;
// r = (float)System.Math.Round(System.Math.Max(0, System.Math.Min(1, r)));
// g = (float)System.Math.Round(System.Math.Max(0, System.Math.Min(1, g)));
// bValue = (float)System.Math.Round(System.Math.Max(0, System.Math.Min(1, bValue)));
return new Color ( r , g , bValue ) ;
}
public static Color Greyscale ( float brightness , float alpha = 1 ) = > new Color ( brightness , brightness , brightness , alpha ) ;
public static Color SetAlpha ( this Color color , float alpha ) { color . a = alpha ; return color ; }
public static Color MultiplyAlpha ( this Color color , float k ) { color . a = k ; return color ; }
#endregion
#region Rects
public static Rect Resize ( this Rect rect , float px ) { rect . x + = px ; rect . y + = px ; rect . width - = px * 2 ; rect . height - = px * 2 ; return rect ; }
public static Rect SetPos ( this Rect rect , Vector2 v ) = > rect . SetPos ( v . x , v . y ) ;
public static Rect SetPos ( this Rect rect , float x , float y ) { rect . x = x ; rect . y = y ; return rect ; }
public static Rect SetX ( this Rect rect , float x ) = > rect . SetPos ( x , rect . y ) ;
public static Rect SetY ( this Rect rect , float y ) = > rect . SetPos ( rect . x , y ) ;
public static Rect SetXMax ( this Rect rect , float xMax ) { rect . xMax = xMax ; return rect ; }
public static Rect SetYMax ( this Rect rect , float yMax ) { rect . yMax = yMax ; return rect ; }
public static Rect SetMidPos ( this Rect r , Vector2 v ) = > r . SetPos ( v ) . MoveX ( - r . width / 2 ) . MoveY ( - r . height / 2 ) ;
public static Rect Move ( this Rect rect , Vector2 v ) { rect . position + = v ; return rect ; }
public static Rect Move ( this Rect rect , float x , float y ) { rect . x + = x ; rect . y + = y ; return rect ; }
public static Rect MoveX ( this Rect rect , float px ) { rect . x + = px ; return rect ; }
public static Rect MoveY ( this Rect rect , float px ) { rect . y + = px ; return rect ; }
public static Rect SetWidth ( this Rect rect , float f ) { rect . width = f ; return rect ; }
public static Rect SetWidthFromMid ( this Rect rect , float px ) { rect . x + = rect . width / 2 ; rect . width = px ; rect . x - = rect . width / 2 ; return rect ; }
public static Rect SetWidthFromRight ( this Rect rect , float px ) { rect . x + = rect . width ; rect . width = px ; rect . x - = rect . width ; return rect ; }
public static Rect SetHeight ( this Rect rect , float f ) { rect . height = f ; return rect ; }
public static Rect SetHeightFromMid ( this Rect rect , float px ) { rect . y + = rect . height / 2 ; rect . height = px ; rect . y - = rect . height / 2 ; return rect ; }
public static Rect SetHeightFromBottom ( this Rect rect , float px ) { rect . y + = rect . height ; rect . height = px ; rect . y - = rect . height ; return rect ; }
public static Rect AddWidth ( this Rect rect , float f ) = > rect . SetWidth ( rect . width + f ) ;
public static Rect AddWidthFromMid ( this Rect rect , float f ) = > rect . SetWidthFromMid ( rect . width + f ) ;
public static Rect AddWidthFromRight ( this Rect rect , float f ) = > rect . SetWidthFromRight ( rect . width + f ) ;
public static Rect AddHeight ( this Rect rect , float f ) = > rect . SetHeight ( rect . height + f ) ;
public static Rect AddHeightFromMid ( this Rect rect , float f ) = > rect . SetHeightFromMid ( rect . height + f ) ;
public static Rect AddHeightFromBottom ( this Rect rect , float f ) = > rect . SetHeightFromBottom ( rect . height + f ) ;
public static Rect SetSize ( this Rect rect , Vector2 v ) = > rect . SetWidth ( v . x ) . SetHeight ( v . y ) ;
public static Rect SetSize ( this Rect rect , float w , float h ) = > rect . SetWidth ( w ) . SetHeight ( h ) ;
public static Rect SetSize ( this Rect rect , float f ) { rect . height = rect . width = f ; return rect ; }
public static Rect SetSizeFromMid ( this Rect r , Vector2 v ) = > r . Move ( r . size / 2 ) . SetSize ( v ) . Move ( - v / 2 ) ;
public static Rect SetSizeFromMid ( this Rect r , float x , float y ) = > r . SetSizeFromMid ( new Vector2 ( x , y ) ) ;
public static Rect SetSizeFromMid ( this Rect r , float f ) = > r . SetSizeFromMid ( new Vector2 ( f , f ) ) ;
public static Rect AlignToPixelGrid ( this Rect r ) = > GUIUtility . AlignRectToDevice ( r ) ;
#endregion
#region Vectors
public static Vector2 AddX ( this Vector2 v , float f ) = > new Vector2 ( v . x + f , v . y + 0 ) ;
public static Vector2 AddY ( this Vector2 v , float f ) = > new Vector2 ( v . x + 0 , v . y + f ) ;
public static Vector3 AddX ( this Vector3 v , float f ) = > new Vector3 ( v . x + f , v . y + 0 , v . z + 0 ) ;
public static Vector3 AddY ( this Vector3 v , float f ) = > new Vector3 ( v . x + 0 , v . y + f , v . z + 0 ) ;
public static Vector3 AddZ ( this Vector3 v , float f ) = > new Vector3 ( v . x + 0 , v . y + 0 , v . z + f ) ;
public static Vector2 xx ( this Vector3 v ) { return new Vector2 ( v . x , v . x ) ; }
public static Vector2 xy ( this Vector3 v ) { return new Vector2 ( v . x , v . y ) ; }
public static Vector2 xz ( this Vector3 v ) { return new Vector2 ( v . x , v . z ) ; }
public static Vector2 yx ( this Vector3 v ) { return new Vector2 ( v . y , v . x ) ; }
public static Vector2 yy ( this Vector3 v ) { return new Vector2 ( v . y , v . y ) ; }
public static Vector2 yz ( this Vector3 v ) { return new Vector2 ( v . y , v . z ) ; }
public static Vector2 zx ( this Vector3 v ) { return new Vector2 ( v . z , v . x ) ; }
public static Vector2 zy ( this Vector3 v ) { return new Vector2 ( v . z , v . y ) ; }
public static Vector2 zz ( this Vector3 v ) { return new Vector2 ( v . z , v . z ) ; }
#endregion
#region Textures
public static Texture2D CreateTexture2D ( int width , int height , GraphicsFormat graphicsFormat = GraphicsFormat . R8G8B8A8_SRGB , bool useMips = false )
{
return new Texture2D ( width , height , graphicsFormat , useMips ? TextureCreationFlags . MipChain : TextureCreationFlags . None ) ;
}
public static RenderTexture CreateRT ( int width , int height , GraphicsFormat graphicsFormat = GraphicsFormat . R8G8B8A8_SRGB , bool useMips = false , bool autoGenerateMips = true , bool useDepth = false )
{
var rt = new RenderTexture ( width , height , useDepth ? 24 : 0 , graphicsFormat ) ;
rt . useMipMap = useMips ;
rt . autoGenerateMips = autoGenerateMips ;
rt . enableRandomWrite = true ;
return rt ;
}
public static RenderTexture GetTemporaryRT ( int width , int height , GraphicsFormat graphicsFormat = GraphicsFormat . R8G8B8A8_SRGB , bool useMips = false , bool autoGenerateMips = true , bool useDepth = false )
{
var rt = RenderTexture . GetTemporary ( width , height , useDepth ? 24 : 0 , graphicsFormat ) ;
rt . useMipMap = useMips ;
rt . autoGenerateMips = autoGenerateMips ;
rt . enableRandomWrite = true ;
return rt ;
}
public static RenderTexture CreateRT ( this RenderTextureDescriptor descriptor ) = > new RenderTexture ( descriptor ) ;
public static RenderTexture CreateRT ( this RenderTextureDescriptor descriptor , int resolution )
{
descriptor . width = descriptor . height = resolution ;
return descriptor . CreateRT ( ) ;
}
public static RenderTexture CreateRT ( this RenderTextureDescriptor descriptor , int width , int height )
{
descriptor . width = width ;
descriptor . height = height ;
return descriptor . CreateRT ( ) ;
}
public static RenderTexture CreateRT ( this RenderTextureDescriptor descriptor , float resolution ) = > descriptor . GetTemporaryRT ( Mathf . RoundToInt ( resolution ) ) ;
public static RenderTexture CreateRT ( this RenderTextureDescriptor descriptor , float width , float height ) = > descriptor . CreateRT ( Mathf . RoundToInt ( width ) , Mathf . RoundToInt ( height ) ) ;
public static RenderTexture GetTemporaryRT ( this RenderTextureDescriptor descriptor ) = > RenderTexture . GetTemporary ( descriptor ) ;
public static RenderTexture GetTemporaryRT ( this RenderTextureDescriptor descriptor , int resolution )
{
descriptor . width = descriptor . height = resolution ;
return descriptor . GetTemporaryRT ( ) ;
}
public static RenderTexture GetTemporaryRT ( this RenderTextureDescriptor descriptor , int width , int height )
{
descriptor . width = width ;
descriptor . height = height ;
return descriptor . GetTemporaryRT ( ) ;
}
public static RenderTexture GetTemporaryRT ( this RenderTextureDescriptor descriptor , float resolution ) = > descriptor . GetTemporaryRT ( Mathf . RoundToInt ( resolution ) ) ;
public static RenderTexture GetTemporaryRT ( this RenderTextureDescriptor descriptor , float width , float height ) = > descriptor . GetTemporaryRT ( Mathf . RoundToInt ( width ) , Mathf . RoundToInt ( height ) ) ;
public static void ReleaseTemporary ( this RenderTexture rt ) { if ( rt ) RenderTexture . ReleaseTemporary ( rt ) ; }
public static Texture2D ToTexture2D ( this RenderTexture rt )
{
var texture2D = CreateTexture2D ( rt . width , rt . height , rt . graphicsFormat , rt . useMipMap ) ;
texture2D . ReadPixelsFrom ( rt ) ;
texture2D . Apply ( ) ;
return texture2D ;
}
public static RenderTexture ToRenderTexture ( this Texture2D texture2d )
{
var rt = CreateRT ( texture2d . width , texture2d . height , texture2d . graphicsFormat , texture2d . mipmapCount > 1 ) ;
Graphics . CopyTexture ( texture2d , rt ) ;
return rt ;
}
public static void ReadPixelsFrom ( this Texture2D texture2D , RenderTexture renderTexture )
{
var prevActive = RenderTexture . active ;
RenderTexture . active = renderTexture ;
texture2D . ReadPixels ( new Rect ( 0 , 0 , renderTexture . width , renderTexture . height ) , 0 , 0 ) ;
RenderTexture . active = prevActive ;
}
// public static void CopyTo(this RenderTexture source, Texture2D target) // todo to readpixels overload
// {
// var prevActive = RenderTexture.active;
// RenderTexture.active = source;
// target.ReadPixels(new Rect(0, 0, source.width, source.height), 0, 0);
// target.Apply();
// RenderTexture.active = prevActive;
// // somewhere in unity source code reading is done like this, but it throws out of bounds read exception on win:
// // if (!SystemInfo.graphicsUVStartsAtTop || SystemInfo.graphicsDeviceType == GraphicsDeviceType.Metal)
// // texture2d.ReadPixels(new Rect(0, 0, texture2d.width, texture2d.height), 0, 0);
// // else
// // texture2d.ReadPixels(new Rect(0, texture2d.height, texture2d.width, texture2d.height), 0, 0);
// // this was used in the legacy rt.CopyToTexture2D extension method
// }
public static Texture2D CreateCopy ( this Texture2D texture2d )
{
var copy = CreateTexture2D ( texture2d . width , texture2d . height , texture2d . graphicsFormat , texture2d . mipmapCount > 1 ) ;
Graphics . CopyTexture ( texture2d , copy ) ;
return copy ;
}
public static Texture2D CreateResizedCopy ( this Texture2D texture2d , int w , int h )
{
var rt = GetTemporaryRT ( w , h , texture2d . graphicsFormat . GetCompatibleForRendering ( ) , false ) ;
Graphics . Blit ( texture2d , rt ) ;
var resizedCopy = CreateTexture2D ( w , h , texture2d . graphicsFormat . GetCompatibleForRendering ( ) , texture2d . mipmapCount > 1 ) ;
resizedCopy . ReadPixelsFrom ( rt ) ;
resizedCopy . Apply ( ) ;
if ( RenderTexture . active = = rt )
RenderTexture . active = null ;
rt . ReleaseTemporary ( ) ;
return resizedCopy ;
}
public static void FillWithColor ( this Texture2D texture2d , Color color )
{
var pixels = new Color32 [ texture2d . width * texture2d . height ] ;
var color32 = ( Color32 ) color ;
for ( int i = 0 ; i < pixels . Length ; i + + )
pixels [ i ] = color32 ;
texture2d . SetPixels32 ( pixels ) ;
texture2d . Apply ( ) ;
}
public static RenderTexture FillWithColor ( this RenderTexture rt , Color color ) // todo builtin shader or GL.clear
{
var mat = new Material ( Shader . Find ( "Hidden/VBlitColor" ) ) ;
mat . SetColor ( "_color" , color ) ;
Graphics . Blit ( null , rt , mat ) ;
mat . Destroy ( ) ;
return rt ;
}
public static GraphicsFormat GetCompatibleForRendering ( this GraphicsFormat graphicsFormat )
{
#if UNITY_2023_2_OR_NEWER
return SystemInfo . GetCompatibleFormat ( graphicsFormat , GraphicsFormatUsage . Render ) ;
#else
return SystemInfo . GetCompatibleFormat ( graphicsFormat , FormatUsage . Render ) ;
#endif
}
#if UNITY_EDITOR
public static void SavePNG ( this Texture2D texture2d , string path ) = > File . WriteAllBytes ( path , texture2d . EncodeToPNG ( ) ) ;
public static void SetImportSettings ( this Texture2D texture2d , int? maxSize = null , bool? useMips = null , bool? sRGB = null , bool? isReadable = null , bool? useCompression = null )
{
var importer = texture2d . GetImporter ( ) ;
if ( useCompression ! = null )
importer . textureCompression = useCompression . GetValueOrDefault ( ) ? TextureImporterCompression . Compressed : TextureImporterCompression . Uncompressed ;
if ( sRGB ! = null )
importer . sRGBTexture = sRGB . GetValueOrDefault ( ) ;
if ( maxSize ! = null )
importer . maxTextureSize = maxSize . GetValueOrDefault ( ) ;
if ( useMips ! = null )
importer . mipmapEnabled = useMips . GetValueOrDefault ( ) ;
// if (texture2d.format == TextureFormat.R16 || texture2d.format == TextureFormat.RG32)
if ( texture2d . format = = TextureFormat . R16 )
{
var platformSettings = importer . GetDefaultPlatformTextureSettings ( ) ;
platformSettings . format = TextureImporterFormat . R16 ;
if ( maxSize ! = null )
platformSettings . maxTextureSize = maxSize . GetValueOrDefault ( ) ;
importer . SetPlatformTextureSettings ( platformSettings ) ;
}
}
public static TextureImporter GetImporter ( this Texture2D t ) = > ( TextureImporter ) AssetImporter . GetAtPath ( AssetDatabase . GetAssetPath ( t ) ) ;
#endif
#endregion
#region Compute
[System.Serializable]
public class GaussianKernel
{
public GaussianKernel ( bool isEvenSize = false , int radius = 7 , float sharpness = . 5f )
{
this . isEvenSize = isEvenSize ;
this . radius = radius ;
this . sharpness = sharpness ;
}
public bool isEvenSize = false ;
public int radius = 7 ;
public float sharpness = . 5f ;
public int size = > radius * 2 + ( isEvenSize ? 0 : 1 ) ;
public float sigma = > 1 - Mathf . Pow ( sharpness , . 1f ) * . 99999f ;
public float [ , ] Array2d ( )
{
float [ , ] kr = new float [ size , size ] ;
if ( size = = 1 ) { kr [ 0 , 0 ] = 1 ; return kr ; }
var a = - 2f * radius * radius / Mathf . Log ( sigma ) ;
var sum = 0f ;
for ( int y = 0 ; y < size ; y + + )
for ( int x = 0 ; x < size ; x + + )
{
var rX = size % 2 = = 1 ? ( x - radius ) : ( x - radius ) + . 5f ;
var rY = size % 2 = = 1 ? ( y - radius ) : ( y - radius ) + . 5f ;
var dist = Mathf . Sqrt ( rX * rX + rY * rY ) ;
kr [ x , y ] = Mathf . Exp ( - dist * dist / a ) ;
sum + = kr [ x , y ] ;
}
for ( int y = 0 ; y < size ; y + + )
for ( int x = 0 ; x < size ; x + + )
kr [ x , y ] / = sum ;
return kr ;
}
public float [ ] ArrayFlat ( )
{
var gk = Array2d ( ) ;
float [ ] flat = new float [ size * size ] ;
for ( int i = 0 ; i < size ; i + + )
for ( int j = 0 ; j < size ; j + + )
flat [ ( i * size + j ) ] = gk [ i , j ] ;
return flat ;
}
}
#endregion
#region Drawing
public static void DrawLine ( Vector3 v0 , Vector3 v1 , Color color = default , Camera cam = null , int layer = 0 , bool drawAboveAll = false )
{
if ( color = = default )
color = Color . white ;
var mesh = new Mesh ( ) ;
mesh . SetVertices ( new [ ] { v0 , v1 } ) ;
mesh . SetIndices ( new [ ] { 0 , 1 } , MeshTopology . Lines , 0 ) ;
Graphics . DrawMesh ( mesh , Vector3 . zero , Quaternion . identity , _GetDrawingMat ( color , drawAboveAll ) , layer , cam , 0 ) ;
}
public static void DrawDot ( Vector3 position , float size = 1 , Color color = default , Camera cam = null , int layer = 0 , bool drawAboveAll = false )
{
if ( color = = default )
color = Color . white ;
if ( ! _sphereMesh )
{
int latitudes = 8 ;
int longtitudes = 10 ;
Vector3 point ( int la , int lo ) = > Quaternion . AngleAxis ( 360f * lo / ( longtitudes - 1 ) , Vector3 . up ) * Quaternion . AngleAxis ( 180f * la / ( latitudes - 1 ) , Vector3 . right ) * Vector3 . up / 2 ;
int index ( int la , int lo ) = > la * longtitudes + ( lo = = longtitudes ? 0 : lo ) ;
var verts = new List < Vector3 > ( ) ;
var tris = new List < int > ( ) ;
for ( int la = 0 ; la < latitudes ; la + + )
for ( int lo = 0 ; lo < longtitudes ; lo + + )
{
verts . Add ( point ( la , lo ) ) ;
if ( la = = latitudes - 1 ) continue ;
tris . Add ( index ( la , lo ) ) ;
tris . Add ( index ( la + 1 , lo ) ) ;
tris . Add ( index ( la + 1 , lo + 1 ) ) ;
tris . Add ( index ( la , lo ) ) ;
tris . Add ( index ( la + 1 , lo + 1 ) ) ;
tris . Add ( index ( la , lo + 1 ) ) ;
}
_sphereMesh = new Mesh ( ) ;
_sphereMesh . SetVertices ( verts ) ;
_sphereMesh . SetIndices ( tris , MeshTopology . Triangles , 0 ) ;
}
;
Graphics . DrawMesh ( _sphereMesh , Matrix4x4 . TRS ( position , Quaternion . identity , Vector3 . one * size ) , _GetDrawingMat ( color , drawAboveAll ) , layer , cam , 0 ) ;
}
static Material _GetDrawingMat ( Color color , bool zTestAlways )
{
var p = new DrawingMatParams { color = color , zTestAlways = zTestAlways } ;
if ( ! _drawingMatsByParams . ContainsKey ( p ) )
{
_drawingMatsByParams [ p ] = new Material ( Shader . Find ( "Hidden/Internal-Colored" ) ) ;
_drawingMatsByParams [ p ] . SetColor ( "_Color" , color ) ;
_drawingMatsByParams [ p ] . SetInt ( "_ZTest" , zTestAlways ? 0 : 4 ) ;
}
return _drawingMatsByParams [ p ] ;
}
static Mesh _sphereMesh ;
struct DrawingMatParams { public Color color ; public bool zTestAlways ; }
static Dictionary < DrawingMatParams , Material > _drawingMatsByParams = new Dictionary < DrawingMatParams , Material > ( ) ;
#endregion
#region GameObjects
public static bool IsPrefab ( this GameObject go ) = > go . scene . name = = null | | go . scene . name = = go . name ;
public static Bounds GetBounds ( this GameObject go , bool local = false )
{
Bounds bounds = default ;
foreach ( var r in go . GetComponentsInChildren < MeshRenderer > ( ) )
{
var b = local ? r . gameObject . GetComponent < MeshFilter > ( ) . sharedMesh . bounds : r . bounds ;
if ( bounds = = default )
bounds = b ;
else
bounds . Encapsulate ( b ) ;
}
foreach ( var r in go . GetComponentsInChildren < Terrain > ( ) )
{
var b = local ? new Bounds ( r . terrainData . size / 2 , r . terrainData . size ) : new Bounds ( r . transform . position + r . terrainData . size / 2 , r . terrainData . size ) ;
if ( bounds = = default )
bounds = b ;
else
bounds . Encapsulate ( new Bounds ( r . transform . position + r . terrainData . size / 2 , r . terrainData . size ) ) ;
}
if ( bounds = = default )
bounds . center = go . transform . position ;
return bounds ;
}
#endregion
#region Transform
public static List < Transform > GetChildren ( this Transform t )
{
var l = new List < Transform > ( ) ;
for ( int i = 0 ; i < t . childCount ; i + + )
l . Add ( t . GetChild ( i ) ) ;
return l ;
}
public static Transform ResetLocals ( this Transform t )
{
t . localPosition = t . localEulerAngles = Vector3 . zero ;
t . localScale = Vector3 . one ;
return t ;
}
public static Transform DestroyChildren ( this Transform t )
{
while ( t . childCount > 0 )
if ( Application . isPlaying )
Object . Destroy ( t . GetChild ( 0 ) . gameObject ) ;
else
Object . DestroyImmediate ( t . GetChild ( 0 ) . gameObject ) ;
return t ;
}
#endregion
#region Camera
public static float FOVByFocusArea ( float focusDistance , float focusArea , float cameraAspectRatio ) = > 2 * Mathf . Atan ( Mathf . Sqrt ( focusArea / focusDistance / focusDistance / cameraAspectRatio ) / 2 ) * Mathf . Rad2Deg ;
public static float FOVByFocusHeight ( float focusDistance , float focusHeight ) = > 2 * Mathf . Atan ( focusHeight / focusDistance / 2 ) * Mathf . Rad2Deg ;
public static float FOVByFocusWidth ( float focusDistance , float focusWidth , float cameraAspectRatio ) = > 2 * Mathf . Atan ( focusWidth * cameraAspectRatio / focusDistance / 2 ) * Mathf . Rad2Deg ;
public static float FOVByFocusHeightWidthMax ( float focusDistance , float focusWidth , float focusHeight , float cameraAspectRatio ) = > 2 * Mathf . Atan ( Mathf . Max ( focusWidth * cameraAspectRatio , focusHeight ) / focusDistance / 2 ) * Mathf . Rad2Deg ;
#endregion
#region Objects
public static Object [ ] FindObjects ( Type type )
{
#if UNITY_2023_1_OR_NEWER
return Object . FindObjectsByType ( type , FindObjectsSortMode . None ) ;
#else
return Object . FindObjectsOfType ( type ) ;
#endif
}
public static T [ ] FindObjects < T > ( ) where T : Object
{
#if UNITY_2023_1_OR_NEWER
return Object . FindObjectsByType < T > ( FindObjectsSortMode . None ) ;
#else
return Object . FindObjectsOfType < T > ( ) ;
#endif
}
public static void Destroy ( this Object r )
{
if ( Application . isPlaying )
Object . Destroy ( r ) ;
else
Object . DestroyImmediate ( r ) ;
}
public static void DestroyImmediate ( this Object o ) = > Object . DestroyImmediate ( o ) ;
#endregion
#region GlobalID
#if UNITY_EDITOR
[System.Serializable]
public struct GlobalID : System . IEquatable < GlobalID >
{
public Object GetObject ( ) = > GlobalObjectId . GlobalObjectIdentifierToObjectSlow ( globalObjectId ) ;
public int GetObjectInstanceId ( ) = > GlobalObjectId . GlobalObjectIdentifierToInstanceIDSlow ( globalObjectId ) ;
public string guid = > globalObjectId . assetGUID . ToString ( ) ;
public ulong fileId = > globalObjectId . targetObjectId ;
public bool isNull = > globalObjectId . identifierType = = 0 ;
public bool isAsset = > globalObjectId . identifierType = = 1 ;
public bool isSceneObject = > globalObjectId . identifierType = = 2 ;
public GlobalObjectId globalObjectId = > _globalObjectId . Equals ( default ) & & GlobalObjectId . TryParse ( globalObjectIdString , out var r ) ? _globalObjectId = r : _globalObjectId ;
public GlobalObjectId _globalObjectId ;
public GlobalID ( Object o ) = > globalObjectIdString = ( _globalObjectId = GlobalObjectId . GetGlobalObjectIdSlow ( o ) ) . ToString ( ) ;
public GlobalID ( string s ) = > globalObjectIdString = GlobalObjectId . TryParse ( s , out _globalObjectId ) ? s : s ;
public string globalObjectIdString ;
public bool Equals ( GlobalID other ) = > this . globalObjectIdString . Equals ( other . globalObjectIdString ) ;
public static bool operator = = ( GlobalID a , GlobalID b ) = > a . Equals ( b ) ;
public static bool operator ! = ( GlobalID a , GlobalID b ) = > ! a . Equals ( b ) ;
public override bool Equals ( object other ) = > other is GlobalID otherglobalID & & this . Equals ( otherglobalID ) ;
public override int GetHashCode ( ) = > globalObjectIdString = = null ? 0 : globalObjectIdString . GetHashCode ( ) ;
public override string ToString ( ) = > globalObjectIdString ;
}
public static GlobalID GetGlobalID ( this Object o ) = > new GlobalID ( o ) ;
public static int [ ] GetObjectInstanceIds ( this IEnumerable < GlobalID > globalIDs )
{
var goids = globalIDs . Select ( r = > r . globalObjectId ) . ToArray ( ) ;
var iids = new int [ goids . Length ] ;
GlobalObjectId . GlobalObjectIdentifiersToInstanceIDsSlow ( goids , iids ) ;
return iids ;
}
#endif
#endregion
#region Paths
public static string GetParentPath ( this string path ) = > path . Substring ( 0 , path . LastIndexOf ( '/' ) ) ;
public static bool HasParentPath ( this string path ) = > path . Contains ( '/' ) & & path . GetParentPath ( ) ! = "" ;
public static string ToGlobalPath ( this string localPath ) = > Application . dataPath + "/" + localPath . Substring ( 0 , localPath . Length - 1 ) ;
public static string ToLocalPath ( this string globalPath ) = > "Assets" + globalPath . Remove ( Application . dataPath ) ;
public static string CombinePath ( this string p , string p2 ) = > Path . Combine ( p , p2 ) ;
public static bool IsSubpathOf ( this string path , string of ) = > path . StartsWith ( of + "/" ) | | of = = "" ;
public static string GetDirectory ( this string pathOrDirectory )
{
var directory = pathOrDirectory . Contains ( '.' ) ? pathOrDirectory . Substring ( 0 , pathOrDirectory . LastIndexOf ( '/' ) ) : pathOrDirectory ;
if ( directory . Contains ( '.' ) )
directory = directory . Substring ( 0 , directory . LastIndexOf ( '/' ) ) ;
return directory ;
}
public static bool DirectoryExists ( this string pathOrDirectory ) = > Directory . Exists ( pathOrDirectory . GetDirectory ( ) ) ;
public static string EnsureDirExists ( this string pathOrDirectory ) // todo to EnsureDirectoryExists
{
var directory = pathOrDirectory . GetDirectory ( ) ;
if ( directory . HasParentPath ( ) & & ! Directory . Exists ( directory . GetParentPath ( ) ) )
EnsureDirExists ( directory . GetParentPath ( ) ) ;
if ( ! Directory . Exists ( directory ) )
Directory . CreateDirectory ( directory ) ;
return pathOrDirectory ;
}
public static string ClearDir ( this string dir )
{
if ( ! Directory . Exists ( dir ) ) return dir ;
var diri = new DirectoryInfo ( dir ) ;
foreach ( var r in diri . EnumerateFiles ( ) ) r . Delete ( ) ;
foreach ( var r in diri . EnumerateDirectories ( ) ) r . Delete ( true ) ;
return dir ;
}
#if UNITY_EDITOR
public static string EnsurePathIsUnique ( this string path )
{
if ( ! path . DirectoryExists ( ) ) return path ;
var s = AssetDatabase . GenerateUniqueAssetPath ( path ) ; // returns empty if parent dir doesnt exist
return s = = "" ? path : s ;
}
public static void EnsureDirExistsAndRevealInFinder ( string dir )
{
EnsureDirExists ( dir ) ;
UnityEditor . EditorUtility . OpenWithDefaultApp ( dir ) ;
}
#endif
#endregion
#region AssetDatabase
#if UNITY_EDITOR
public static AssetImporter GetImporter ( this Object t ) = > AssetImporter . GetAtPath ( AssetDatabase . GetAssetPath ( t ) ) ;
public static string ToPath ( this string guid ) = > AssetDatabase . GUIDToAssetPath ( guid ) ; // returns empty string if not found
public static List < string > ToPaths ( this IEnumerable < string > guids ) = > guids . Select ( r = > r . ToPath ( ) ) . ToList ( ) ;
public static string GetFilename ( this string path , bool withExtension = false ) = > withExtension ? Path . GetFileName ( path ) : Path . GetFileNameWithoutExtension ( path ) ; // prev GetName
public static string GetExtension ( this string path ) = > Path . GetExtension ( path ) ;
public static string ToGuid ( this string pathInProject ) = > AssetDatabase . AssetPathToGUID ( pathInProject ) ;
public static List < string > ToGuids ( this IEnumerable < string > pathsInProject ) = > pathsInProject . Select ( r = > r . ToGuid ( ) ) . ToList ( ) ;
public static string GetPath ( this Object o ) = > AssetDatabase . GetAssetPath ( o ) ;
public static string GetGuid ( this Object o ) = > AssetDatabase . AssetPathToGUID ( AssetDatabase . GetAssetPath ( o ) ) ;
public static string GetScriptPath ( string scriptName ) = > AssetDatabase . FindAssets ( "t: script " + scriptName , null ) . FirstOrDefault ( ) ? . ToPath ( ) ? ? "scirpt not found" ;
// toremove
public static Object LoadGuid ( this string guid ) = > AssetDatabase . LoadAssetAtPath ( guid . ToPath ( ) , typeof ( Object ) ) ;
public static T LoadGuid < T > ( this string guid ) where T : Object = > AssetDatabase . LoadAssetAtPath < T > ( guid . ToPath ( ) ) ;
public static List < string > FindAllAssetsOfType_guids ( Type type ) = > AssetDatabase . FindAssets ( "t:" + type . Name ) . ToList ( ) ;
public static List < string > FindAllAssetsOfType_guids ( Type type , string path ) = > AssetDatabase . FindAssets ( "t:" + type . Name , new [ ] { path } ) . ToList ( ) ;
public static List < T > FindAllAssetsOfType < T > ( ) where T : Object = > FindAllAssetsOfType_guids ( typeof ( T ) ) . Select ( r = > ( T ) r . LoadGuid ( ) ) . ToList ( ) ;
public static List < T > FindAllAssetsOfType < T > ( string path ) where T : Object = > FindAllAssetsOfType_guids ( typeof ( T ) , path ) . Select ( r = > ( T ) r . LoadGuid ( ) ) . ToList ( ) ;
public static T Reimport < T > ( this T t ) where T : Object { AssetDatabase . ImportAsset ( t . GetPath ( ) , ImportAssetOptions . ForceUpdate ) ; return t ; }
#endif
#endregion
#region Serialization
[System.Serializable]
public class SerializableDictionary < TKey , TValue > : Dictionary < TKey , TValue > , ISerializationCallbackReceiver
{
public List < TKey > keys = new List < TKey > ( ) ;
public List < TValue > values = new List < TValue > ( ) ;
public void OnBeforeSerialize ( )
{
keys . Clear ( ) ;
values . Clear ( ) ;
foreach ( KeyValuePair < TKey , TValue > kvp in this )
{
keys . Add ( kvp . Key ) ;
values . Add ( kvp . Value ) ;
}
}
public void OnAfterDeserialize ( )
{
this . Clear ( ) ;
for ( int i = 0 ; i < keys . Count ; i + + )
this [ keys [ i ] ] = values [ i ] ;
}
}
#endregion
#region Editor
#if UNITY_EDITOR
public static void ToggleDefineDisabledInScript ( Type scriptType )
{
var path = GetScriptPath ( scriptType . Name ) ;
var lines = File . ReadAllLines ( path ) ;
if ( lines . First ( ) . StartsWith ( "#define DISABLED" ) )
File . WriteAllLines ( path , lines . Skip ( 1 ) ) ;
else
File . WriteAllLines ( path , lines . Prepend ( "#define DISABLED // this line was added by VUtils.ToggleDefineDisabledInScript" ) ) ;
AssetDatabase . ImportAsset ( path ) ;
}
public static bool ScriptHasDefineDisabled ( Type scriptType ) = > File . ReadLines ( GetScriptPath ( scriptType . Name ) ) . First ( ) . StartsWith ( "#define DISABLED" ) ;
public static void SetDefineDisabledInScript ( Type scriptType , bool defineDisabled )
{
if ( ScriptHasDefineDisabled ( scriptType ) ! = defineDisabled )
ToggleDefineDisabledInScript ( scriptType ) ;
}
public static int GetProjectId ( ) = > Application . dataPath . GetHashCode ( ) ;
public static void PingObject ( Object o , bool select = false , bool focusProjectWindow = true )
{
if ( select )
{
Selection . activeObject = null ;
Selection . activeObject = o ;
}
if ( focusProjectWindow ) EditorUtility . FocusProjectWindow ( ) ;
EditorGUIUtility . PingObject ( o ) ;
}
public static void PingObject ( string guid , bool select = false , bool focusProjectWindow = true ) = > PingObject ( AssetDatabase . LoadAssetAtPath < Object > ( guid . ToPath ( ) ) ) ;
public static void OpenFolder ( string path )
{
var folder = AssetDatabase . LoadAssetAtPath ( path , typeof ( Object ) ) ;
var t = typeof ( Editor ) . Assembly . GetType ( "UnityEditor.ProjectBrowser" ) ;
var w = ( EditorWindow ) t . GetField ( "s_LastInteractedProjectBrowser" ) . GetValue ( null ) ;
var m_ListAreaState = t . GetField ( "m_ListAreaState" , maxBindingFlags ) . GetValue ( w ) ;
m_ListAreaState . GetType ( ) . GetField ( "m_SelectedInstanceIDs" ) . SetValue ( m_ListAreaState , new List < int > { folder . GetInstanceID ( ) } ) ;
t . GetMethod ( "OpenSelectedFolders" , maxBindingFlags ) . Invoke ( null , null ) ;
}
public static void Dirty ( this Object o ) = > UnityEditor . EditorUtility . SetDirty ( o ) ;
public static void RecordUndo ( this Object o )
{
Undo . RecordObject ( o , "" ) ;
}
#if UNITY_2021_1_OR_NEWER
public static void Save ( this Object o ) = > AssetDatabase . SaveAssetIfDirty ( o ) ;
#else
public static void Save ( this Object o ) { }
#endif
public static EditorWindow OpenObjectPicker < T > ( Object obj = null , bool allowSceneObjects = false , string searchFilter = "" , int controlID = 0 ) where T : Object
{
EditorGUIUtility . ShowObjectPicker < T > ( obj , allowSceneObjects , searchFilter , controlID ) ;
return Resources . FindObjectsOfTypeAll ( typeof ( Editor ) . Assembly . GetType ( "UnityEditor.ObjectSelector" ) ) . FirstOrDefault ( ) as EditorWindow ;
}
public static EditorWindow OpenColorPicker ( System . Action < Color > colorChangedCallback , Color color , bool showAlpha = true , bool hdr = false )
{
typeof ( Editor ) . Assembly . GetType ( "UnityEditor.ColorPicker" ) . InvokeMethod ( "Show" , colorChangedCallback , color , showAlpha , hdr ) ;
return typeof ( Editor ) . Assembly . GetType ( "UnityEditor.ColorPicker" ) . GetPropertyValue < EditorWindow > ( "instance" ) ;
}
public static void MoveTo ( this EditorWindow window , Vector2 position , bool ensureFitsOnScreen = true )
{
if ( ! ensureFitsOnScreen ) { window . position = window . position . SetPos ( position ) ; return ; }
var windowRect = window . position ;
var unityWindowRect = EditorGUIUtility . GetMainWindowPosition ( ) ;
position . x = position . x . Max ( unityWindowRect . position . x ) ;
position . y = position . y . Max ( unityWindowRect . position . y ) ;
position . x = position . x . Min ( unityWindowRect . xMax - windowRect . width ) ;
position . y = position . y . Min ( unityWindowRect . yMax - windowRect . height ) ;
window . position = windowRect . SetPos ( position ) ;
}
public static void RemoveEditorErrors ( ) = > removeEditorErrorsMethod . Invoke ( null , new object [ ] { 1 } ) ;
static MethodInfo removeEditorErrorsMethod = System . AppDomain . CurrentDomain . GetAssemblies ( ) . FirstOrDefault ( r = > r . GetName ( ) . ToString ( ) . Contains ( "UnityEditor.CoreModule" ) ) . GetTypes ( ) . First ( r = > r . Name . Contains ( "LogEntry" ) ) . GetMethod ( "RemoveLogEntriesByMode" , BindingFlags . Static | BindingFlags . NonPublic ) ;
#endif
#endregion
}
public static class VGUI
{
#region Colors
public static class GUIColors
{
public static Color windowBackground = > isDarkTheme ? Greyscale ( . 22f ) : Greyscale ( . 78f ) ; // prev backgroundCol
public static Color pressedButtonBackground = > isDarkTheme ? new Color ( . 48f , . 76f , 1f , 1f ) * 1.4f : new Color ( . 48f , . 7f , 1f , 1f ) * 1.2f ; // prev pressedButtonCol
public static Color greyedOutTint = > Greyscale ( . 7f ) ;
public static Color selectedBackground = > isDarkTheme ? new Color ( . 17f , . 365f , . 535f ) : new Color ( . 2f , . 375f , . 555f ) * 1.2f ;
}
#endregion
#region Shortcuts
public static Rect lastRect = > GUILayoutUtility . GetLastRect ( ) ;
public static bool isDarkTheme = > EditorGUIUtility . isProSkin ;
public static float GetLabelWidth ( this string s ) = > GUI . skin . label . CalcSize ( new GUIContent ( s ) ) . x ;
public static float GetLabelWidth ( this string s , int fotSize )
{
SetLabelFontSize ( fotSize ) ;
var r = s . GetLabelWidth ( ) ;
ResetLabelStyle ( ) ;
return r ;
}
public static float GetLabelWidth ( this string s , bool isBold )
{
if ( isBold )
SetLabelBold ( ) ;
var r = s . GetLabelWidth ( ) ;
if ( isBold )
ResetLabelStyle ( ) ;
return r ;
}
public static void SetGUIEnabled ( bool enabled ) { _prevGuiEnabled = GUI . enabled ; GUI . enabled = enabled ; }
public static void ResetGUIEnabled ( ) = > GUI . enabled = _prevGuiEnabled ;
static bool _prevGuiEnabled = true ;
public static void SetLabelFontSize ( int size ) = > GUI . skin . label . fontSize = size ;
public static void SetLabelBold ( ) = > GUI . skin . label . fontStyle = FontStyle . Bold ;
public static void SetLabelAlignmentCenter ( ) = > GUI . skin . label . alignment = TextAnchor . MiddleCenter ;
public static void ResetLabelStyle ( )
{
GUI . skin . label . fontSize = 0 ;
GUI . skin . label . fontStyle = FontStyle . Normal ;
GUI . skin . label . alignment = TextAnchor . MiddleLeft ;
}
public static void SetGUIColor ( Color c )
{
if ( ! _guiColorModified )
_defaultGuiColor = GUI . color ;
_guiColorModified = true ;
GUI . color = _defaultGuiColor * c ;
}
public static void ResetGUIColor ( )
{
GUI . color = _guiColorModified ? _defaultGuiColor : Color . white ;
_guiColorModified = false ;
}
static bool _guiColorModified ;
static Color _defaultGuiColor ;
#endregion
#region Events
public struct WrappedEvent
{
public Event e ;
public bool isNull = > e = = null ;
public bool isRepaint = > isNull ? default : e . type = = EventType . Repaint ;
public bool isLayout = > isNull ? default : e . type = = EventType . Layout ;
public bool isUsed = > isNull ? default : e . type = = EventType . Used ;
public bool isMouseLeaveWindow = > isNull ? default : e . type = = EventType . MouseLeaveWindow ;
public bool isMouseEnterWindow = > isNull ? default : e . type = = EventType . MouseEnterWindow ;
public bool isContextClick = > isNull ? default : e . type = = EventType . ContextClick ;
public bool isKeyDown = > isNull ? default : e . type = = EventType . KeyDown ;
public bool isKeyUp = > isNull ? default : e . type = = EventType . KeyUp ;
public KeyCode keyCode = > isNull ? default : e . keyCode ;
public char characted = > isNull ? default : e . character ;
public bool isExecuteCommand = > isNull ? default : e . type = = EventType . ExecuteCommand ;
public string commandName = > isNull ? default : e . commandName ;
public bool isMouse = > isNull ? default : e . isMouse ;
public bool isMouseDown = > isNull ? default : e . type = = EventType . MouseDown ;
public bool isMouseUp = > isNull ? default : e . type = = EventType . MouseUp ;
public bool isMouseDrag = > isNull ? default : e . type = = EventType . MouseDrag ;
public bool isMouseMove = > isNull ? default : e . type = = EventType . MouseMove ;
public bool isScroll = > isNull ? default : e . type = = EventType . ScrollWheel ;
public int mouseButton = > isNull ? default : e . button ;
public int clickCount = > isNull ? default : e . clickCount ;
public Vector2 mousePosition = > isNull ? default : e . mousePosition ;
public Vector2 mousePosition_screenSpace = > isNull ? default : GUIUtility . GUIToScreenPoint ( e . mousePosition ) ;
public Vector2 mouseDelta = > isNull ? default : e . delta ;
public bool isDragUpdate = > isNull ? default : e . type = = EventType . DragUpdated ;
public bool isDragPerform = > isNull ? default : e . type = = EventType . DragPerform ;
public bool isDragExit = > isNull ? default : e . type = = EventType . DragExited ;
public EventModifiers modifiers = > isNull ? default : e . modifiers ;
public bool holdingAnyModifierKey = > modifiers ! = EventModifiers . None ;
public bool holdingAlt = > isNull ? default : e . alt ;
public bool holdingShift = > isNull ? default : e . shift ;
public bool holdingCtrl = > isNull ? default : e . control ;
public bool holdingCmd = > isNull ? default : e . command ;
public bool holdingCmdOrCtrl = > isNull ? default : e . command | | e . control ;
public bool holdingAltOnly = > isNull ? default : e . modifiers = = EventModifiers . Alt ; // in some sessions FunctionKey is always pressed?
public bool holdingShiftOnly = > isNull ? default : e . modifiers = = EventModifiers . Shift ; // in some sessions FunctionKey is always pressed?
public bool holdingCtrlOnly = > isNull ? default : e . modifiers = = EventModifiers . Control ;
public bool holdingCmdOnly = > isNull ? default : e . modifiers = = EventModifiers . Command ;
public bool holdingCmdOrCtrlOnly = > isNull ? default : ( e . modifiers = = EventModifiers . Command | | e . modifiers = = EventModifiers . Control ) ;
public EventType type = > e . type ;
public void Use ( ) = > e ? . Use ( ) ;
public WrappedEvent ( Event e ) = > this . e = e ;
public override string ToString ( ) = > e . ToString ( ) ;
}
public static WrappedEvent Wrap ( this Event e ) = > new WrappedEvent ( e ) ;
public static WrappedEvent curEvent = > ( Event . current ? ? _fi_s_Current . GetValue ( null ) as Event ) . Wrap ( ) ; // todo no reflection?
static FieldInfo _fi_s_Current = typeof ( Event ) . GetField ( "s_Current" , maxBindingFlags ) ;
// public static Event e => Event.current;
// public static bool ePresent => Event.current != null;
// public static UnityEngine.EventType eType => ePresent ? e.type : UnityEngine.EventType.Ignore;
// public static bool mouseDown(this Event e) => curEvent.isMouseDown && curEvent.mouseButton == 0;
// public static bool mouseUp(this Event e) => curEvent.isMouseUp && curEvent.mouseButton == 0;
// public static bool keyDown(this Event e) => curEvent.isKeyDown;
// public static bool keyUp(this Event e) => eType == EventType.KeyUp;
// public static bool holdingAlt => ePresent && (e.alt);
// public static bool holdingCmd => ePresent && (e.command || e.control);
// public static bool holdingShift => ePresent && (e.shift);
#endregion
#region Layout
public static void BeginIndent ( float f )
{
GUILayout . BeginHorizontal ( ) ;
GUILayout . Space ( f ) ;
GUILayout . BeginVertical ( ) ;
_indentLabelWidthStack . Push ( EditorGUIUtility . labelWidth ) ;
EditorGUIUtility . labelWidth - = f ;
}
public static void EndIndent ( float f = 0 )
{
GUILayout . EndVertical ( ) ;
GUILayout . Space ( f ) ;
GUILayout . EndHorizontal ( ) ;
EditorGUIUtility . labelWidth = _indentLabelWidthStack . Pop ( ) ;
}
static Stack < float > _indentLabelWidthStack = new Stack < float > ( ) ;
public static void Horizontal ( ) { if ( __hor ) GUILayout . EndHorizontal ( ) ; else GUILayout . BeginHorizontal ( ) ; __hor = ! __hor ; }
public static void Vertical ( ) { if ( __v ) GUILayout . EndVertical ( ) ; else GUILayout . BeginVertical ( ) ; __v = ! __v ; }
public static void Area ( Rect r ) { if ( __a ) GUILayout . EndArea ( ) ; else GUILayout . BeginArea ( r ) ; __a = ! __a ; }
public static void Area ( ) { if ( __a ) GUILayout . EndArea ( ) ; __a = ! __a ; }
public static void ResetUIBools ( ) { __a = __hor = __v = false ; _prevGuiEnabled = true ; }
static bool __hor , __a , __v ;
#endregion
#region Drawing
public static Rect Draw ( this Rect r ) { EditorGUI . DrawRect ( r , Color . black ) ; return r ; }
public static Rect Draw ( this Rect r , Color c ) { EditorGUI . DrawRect ( r , c ) ; return r ; }
public static Rect DrawOutline ( this Rect r ) = > r . DrawOutline ( Color . black ) ;
public static Rect DrawOutline ( this Rect r , Color c ) { OutlineRect ( r , c , false , 1 ) ; return r ; }
public static void OutlineRect ( Rect rect , Color col , bool greyedOut = false , int px = 1 )
{
bool offset = false ;
int f = px ;
Rect r ;
//
Color tint = greyedOut ? Color . white * . 74f : Color . white ;
r = rect ;
r . height = f ;
if ( offset )
{
r . x + = 1 ;
r . width - = 2 ;
r . y + = 1 ;
}
EditorGUI . DrawRect ( r , col * tint ) ;
r = rect ;
r . width = f ;
if ( offset )
{
r . y + = 1 ;
r . height - = 2 ;
r . x + = 1 ;
}
EditorGUI . DrawRect ( r , col * tint ) ;
r = rect ;
r . y + = r . height ;
r . height = f ;
r . y - = r . height ;
if ( offset )
{
r . x + = 1 ;
r . width - = 2 ;
r . y - = 1 ;
}
EditorGUI . DrawRect ( r , col * tint ) ;
r = rect ;
r . x + = r . width ;
r . width = f ;
r . x - = r . width ;
if ( offset )
{
r . y + = 1 ;
r . height - = 2 ;
r . x - = 1 ;
}
EditorGUI . DrawRect ( r , col * tint ) ;
}
public static Rect DrawWithRoundedCorners ( this Rect rect , Color color , int cornerRadius )
{
if ( ! curEvent . isRepaint ) return rect ;
cornerRadius = cornerRadius . Min ( ( rect . height / 2 ) . FloorToInt ( ) ) . Min ( ( rect . width / 2 ) . FloorToInt ( ) ) ;
GUIStyle style ;
void getStyle ( )
{
if ( _roundedStylesByCornerRadius . TryGetValue ( cornerRadius , out style ) ) return ;
var pixelsPerPoint = 2 ;
var res = cornerRadius * 2 * pixelsPerPoint ;
var pixels = new Color [ res * res ] ;
var white = Greyscale ( 1 , 1 ) ;
var clear = Greyscale ( 1 , 0 ) ;
var halfRes = res / 2 ;
for ( int x = 0 ; x < res ; x + + )
for ( int y = 0 ; y < res ; y + + )
{
var sqrMagnitude = ( new Vector2 ( x - halfRes + . 5f , y - halfRes + . 5f ) ) . sqrMagnitude ;
pixels [ x + y * res ] = sqrMagnitude < = halfRes * halfRes ? white : clear ;
}
var texture = new Texture2D ( res , res ) ;
texture . SetPropertyValue ( "pixelsPerPoint" , pixelsPerPoint ) ;
texture . hideFlags = HideFlags . DontSave ;
texture . SetPixels ( pixels ) ;
texture . Apply ( ) ;
style = new GUIStyle ( ) ;
style . normal . background = texture ;
style . alignment = TextAnchor . MiddleCenter ;
style . border = new RectOffset ( cornerRadius , cornerRadius , cornerRadius , cornerRadius ) ;
_roundedStylesByCornerRadius [ cornerRadius ] = style ;
}
void draw ( )
{
SetGUIColor ( color ) ;
style . Draw ( rect , false , false , false , false ) ;
ResetGUIColor ( ) ;
}
getStyle ( ) ;
draw ( ) ;
return rect ;
}
public static Rect DrawWithRoundedCorners ( this Rect rect , Color color , float cornerRadius ) = > rect . DrawWithRoundedCorners ( color , cornerRadius . RoundToInt ( ) ) ;
static Dictionary < int , GUIStyle > _roundedStylesByCornerRadius = new Dictionary < int , GUIStyle > ( ) ;
public static Rect DrawBlurred ( this Rect rect , Color color , int blurRadius )
{
if ( ! curEvent . isRepaint ) return rect ;
var pixelsPerPoint = . 5f ;
// var pixelsPerPoint = 1f;
var blurRadiusScaled = ( blurRadius * pixelsPerPoint ) . RoundToInt ( ) . Max ( 1 ) . Min ( 123 ) ;
var croppedRectWidth = ( rect . width * pixelsPerPoint ) . RoundToInt ( ) . Min ( blurRadiusScaled * 2 ) ;
var croppedRectHeight = ( rect . height * pixelsPerPoint ) . RoundToInt ( ) . Min ( blurRadiusScaled * 2 ) ;
var textureWidth = croppedRectWidth + blurRadiusScaled * 2 ;
var textureHeight = croppedRectHeight + blurRadiusScaled * 2 ;
if ( textureWidth < = 0 ) return rect ; // happens on 2021.1.28
if ( textureHeight < = 0 ) return rect ; // happens on 2021.1.28
GUIStyle style ;
void getStyle ( )
{
if ( _blurredStylesByTextureSize . TryGetValue ( ( textureWidth , textureHeight ) , out style ) ) return ;
// VDebug.LogStart(blurRadius + "");
var pixels = new Color [ textureWidth * textureHeight ] ;
var kernel = new GaussianKernel ( false , blurRadiusScaled ) . Array2d ( ) ;
for ( int x = 0 ; x < textureWidth ; x + + )
for ( int y = 0 ; y < textureHeight ; y + + )
{
var sum = 0f ;
for ( int xSample = ( x - blurRadiusScaled ) . Max ( blurRadiusScaled ) ; xSample < = ( x + blurRadiusScaled ) . Min ( textureWidth - 1 - blurRadiusScaled ) ; xSample + + )
for ( int ySample = ( y - blurRadiusScaled ) . Max ( blurRadiusScaled ) ; ySample < = ( y + blurRadiusScaled ) . Min ( textureHeight - 1 - blurRadiusScaled ) ; ySample + + )
sum + = kernel [ blurRadiusScaled + xSample - x , blurRadiusScaled + ySample - y ] ;
pixels [ x + y * textureWidth ] = Greyscale ( 1 , sum ) ;
}
var texture = new Texture2D ( textureWidth , textureHeight ) ;
texture . SetPropertyValue ( "pixelsPerPoint" , pixelsPerPoint ) ;
texture . hideFlags = HideFlags . DontSave ;
texture . SetPixels ( pixels ) ;
texture . Apply ( ) ;
style = new GUIStyle ( ) ;
style . normal . background = texture ;
style . alignment = TextAnchor . MiddleCenter ;
var borderX = ( ( textureWidth / 2f - 1 ) / pixelsPerPoint ) . FloorToInt ( ) ;
var borderY = ( ( textureHeight / 2f - 1 ) / pixelsPerPoint ) . FloorToInt ( ) ;
style . border = new RectOffset ( borderX , borderX , borderY , borderY ) ;
_blurredStylesByTextureSize [ ( textureWidth , textureHeight ) ] = style ;
// VDebug.LogFinish();
}
void draw ( )
{
SetGUIColor ( color ) ;
style . Draw ( rect . SetSizeFromMid ( rect . width + blurRadius * 2 , rect . height + blurRadius * 2 ) , false , false , false , false ) ;
ResetGUIColor ( ) ;
}
getStyle ( ) ;
draw ( ) ;
return rect ;
}
public static Rect DrawBlurred ( this Rect rect , Color color , float blurRadius ) = > rect . DrawBlurred ( color , blurRadius . RoundToInt ( ) ) ;
static Dictionary < ( int , int ) , GUIStyle > _blurredStylesByTextureSize = new Dictionary < ( int , int ) , GUIStyle > ( ) ;
static void DrawCurtain ( this Rect rect , Color color , int dir )
{
void genTextures ( )
{
if ( _gradientTextures ! = null ) return ;
_gradientTextures = new Texture2D [ 4 ] ;
// var pixels = Enumerable.Range(0, 256).Select(r => Greyscale(1, r / 255f));
var pixels = Enumerable . Range ( 0 , 256 ) . Select ( r = > Greyscale ( 1 , ( r / 255f ) . Smoothstep ( ) ) ) ;
var up = new Texture2D ( 1 , 256 ) ;
up . SetPixels ( pixels . Reverse ( ) . ToArray ( ) ) ;
up . Apply ( ) ;
up . hideFlags = HideFlags . DontSave ;
up . wrapMode = TextureWrapMode . Clamp ;
_gradientTextures [ 0 ] = up ;
var down = new Texture2D ( 1 , 256 ) ;
down . SetPixels ( pixels . ToArray ( ) ) ;
down . Apply ( ) ;
down . hideFlags = HideFlags . DontSave ;
down . wrapMode = TextureWrapMode . Clamp ;
_gradientTextures [ 1 ] = down ;
var left = new Texture2D ( 256 , 1 ) ;
left . SetPixels ( pixels . ToArray ( ) ) ;
left . Apply ( ) ;
left . hideFlags = HideFlags . DontSave ;
left . wrapMode = TextureWrapMode . Clamp ;
_gradientTextures [ 2 ] = left ;
var right = new Texture2D ( 256 , 1 ) ;
right . SetPixels ( pixels . Reverse ( ) . ToArray ( ) ) ;
right . Apply ( ) ;
right . hideFlags = HideFlags . DontSave ;
right . wrapMode = TextureWrapMode . Clamp ;
_gradientTextures [ 3 ] = right ;
}
void draw ( )
{
SetGUIColor ( color ) ;
GUI . DrawTexture ( rect , _gradientTextures [ dir ] ) ;
ResetGUIColor ( ) ;
}
genTextures ( ) ;
draw ( ) ;
}
public static void DrawCurtainUp ( this Rect rect , Color color ) = > rect . DrawCurtain ( color , 0 ) ;
public static void DrawCurtainDown ( this Rect rect , Color color ) = > rect . DrawCurtain ( color , 1 ) ;
public static void DrawCurtainLeft ( this Rect rect , Color color ) = > rect . DrawCurtain ( color , 2 ) ;
public static void DrawCurtainRight ( this Rect rect , Color color ) = > rect . DrawCurtain ( color , 3 ) ;
static Texture2D [ ] _gradientTextures ;
public static bool IsHovered ( this Rect r ) = > ! curEvent . isNull & & r . Contains ( curEvent . mousePosition ) ;
#endregion
#region Spacing
public static void Space ( float px = 6 ) = > GUILayout . Space ( px ) ;
public static void Divider ( float space = 15 , float yOffset = 0 )
{
GUILayout . Label ( "" , GUILayout . Height ( space ) , GUILayout . ExpandWidth ( true ) ) ;
lastRect . SetHeightFromMid ( 1 ) . SetWidthFromMid ( lastRect . width - 16 ) . MoveY ( yOffset ) . Draw ( isDarkTheme ? Color . white * . 42f : Color . white * . 72f ) ;
}
public static Rect ExpandSpace ( ) { GUILayout . Label ( "" , GUILayout . ExpandHeight ( true ) , GUILayout . ExpandWidth ( true ) ) ; return lastRect ; }
public static Rect ExpandWidthLabelRect ( ) { GUILayout . Label ( "" /* , GUILayout.Height(0) */ , GUILayout . ExpandWidth ( true ) ) ; return lastRect ; }
public static Rect ExpandWidthLabelRect ( float height ) { GUILayout . Label ( "" , GUILayout . Height ( height ) , GUILayout . ExpandWidth ( true ) ) ; return lastRect ; }
#endregion
#region Icons
public static class EditorIcons
{
public static Texture2D GetIcon ( string iconNameOrPath )
{
if ( icons_byName . TryGetValue ( iconNameOrPath , out var cachedResult ) ) return cachedResult ;
var icon = typeof ( EditorGUIUtility ) . InvokeMethod < Texture2D > ( "LoadIcon" , iconNameOrPath ) as Texture2D ;
return icons_byName [ iconNameOrPath ] = icon ;
}
static Dictionary < string , Texture2D > icons_byName = new Dictionary < string , Texture2D > ( ) ;
}
#endregion
#region Other
public static void MarkInteractive ( this Rect rect )
{
if ( ! curEvent . isRepaint ) return ;
var unclippedRect = ( Rect ) _mi_GUIClip_UnclipToWindow . Invoke ( null , new object [ ] { rect } ) ;
var curGuiView = _pi_GUIView_current . GetValue ( null ) ;
_mi_GUIView_MarkHotRegion . Invoke ( curGuiView , new object [ ] { unclippedRect } ) ;
}
static PropertyInfo _pi_GUIView_current = typeof ( Editor ) . Assembly . GetType ( "UnityEditor.GUIView" ) . GetProperty ( "current" , maxBindingFlags ) ;
static MethodInfo _mi_GUIView_MarkHotRegion = typeof ( Editor ) . Assembly . GetType ( "UnityEditor.GUIView" ) . GetMethod ( "MarkHotRegion" , maxBindingFlags ) ;
static MethodInfo _mi_GUIClip_UnclipToWindow = typeof ( GUI ) . Assembly . GetType ( "UnityEngine.GUIClip" ) . GetMethod ( "UnclipToWindow" , maxBindingFlags , null , new [ ] { typeof ( Rect ) } , null ) ;
#endregion
}
}
#endif