DebugLogConsole.cs 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362
  1. using UnityEngine;
  2. using System;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using System.Reflection;
  6. using System.Text;
  7. using Object = UnityEngine.Object;
  8. // Manages the console commands, parses console input and handles execution of commands
  9. // Supported method parameter types: int, float, bool, string, Vector2, Vector3, Vector4
  10. // Helper class to store important information about a command
  11. namespace IngameDebugConsole
  12. {
  13. public class ConsoleMethodInfo
  14. {
  15. public readonly MethodInfo method;
  16. public readonly Type[] parameterTypes;
  17. public readonly object instance;
  18. public readonly string command;
  19. public readonly string signature;
  20. public readonly string[] parameters;
  21. public ConsoleMethodInfo( MethodInfo method, Type[] parameterTypes, object instance, string command, string signature, string[] parameters )
  22. {
  23. this.method = method;
  24. this.parameterTypes = parameterTypes;
  25. this.instance = instance;
  26. this.command = command;
  27. this.signature = signature;
  28. this.parameters = parameters;
  29. }
  30. public bool IsValid()
  31. {
  32. if( !method.IsStatic && ( instance == null || instance.Equals( null ) ) )
  33. return false;
  34. return true;
  35. }
  36. }
  37. public static class DebugLogConsole
  38. {
  39. public delegate bool ParseFunction( string input, out object output );
  40. // All the commands
  41. private static readonly List<ConsoleMethodInfo> methods = new List<ConsoleMethodInfo>();
  42. private static readonly List<ConsoleMethodInfo> matchingMethods = new List<ConsoleMethodInfo>( 4 );
  43. // All the parse functions
  44. private static readonly Dictionary<Type, ParseFunction> parseFunctions = new Dictionary<Type, ParseFunction>()
  45. {
  46. { typeof( string ), ParseString },
  47. { typeof( bool ), ParseBool },
  48. { typeof( int ), ParseInt },
  49. { typeof( uint ), ParseUInt },
  50. { typeof( long ), ParseLong },
  51. { typeof( ulong ), ParseULong },
  52. { typeof( byte ), ParseByte },
  53. { typeof( sbyte ), ParseSByte },
  54. { typeof( short ), ParseShort },
  55. { typeof( ushort ), ParseUShort },
  56. { typeof( char ), ParseChar },
  57. { typeof( float ), ParseFloat },
  58. { typeof( double ), ParseDouble },
  59. { typeof( decimal ), ParseDecimal },
  60. { typeof( Vector2 ), ParseVector2 },
  61. { typeof( Vector3 ), ParseVector3 },
  62. { typeof( Vector4 ), ParseVector4 },
  63. { typeof( Quaternion ), ParseQuaternion },
  64. { typeof( Color ), ParseColor },
  65. { typeof( Color32 ), ParseColor32 },
  66. { typeof( Rect ), ParseRect },
  67. { typeof( RectOffset ), ParseRectOffset },
  68. { typeof( Bounds ), ParseBounds },
  69. { typeof( GameObject ), ParseGameObject },
  70. #if UNITY_2017_2_OR_NEWER
  71. { typeof( Vector2Int ), ParseVector2Int },
  72. { typeof( Vector3Int ), ParseVector3Int },
  73. { typeof( RectInt ), ParseRectInt },
  74. { typeof( BoundsInt ), ParseBoundsInt },
  75. #endif
  76. };
  77. // All the readable names of accepted types
  78. private static readonly Dictionary<Type, string> typeReadableNames = new Dictionary<Type, string>()
  79. {
  80. { typeof( string ), "String" },
  81. { typeof( bool ), "Boolean" },
  82. { typeof( int ), "Integer" },
  83. { typeof( uint ), "Unsigned Integer" },
  84. { typeof( long ), "Long" },
  85. { typeof( ulong ), "Unsigned Long" },
  86. { typeof( byte ), "Byte" },
  87. { typeof( sbyte ), "Short Byte" },
  88. { typeof( short ), "Short" },
  89. { typeof( ushort ), "Unsigned Short" },
  90. { typeof( char ), "Char" },
  91. { typeof( float ), "Float" },
  92. { typeof( double ), "Double" },
  93. { typeof( decimal ), "Decimal" }
  94. };
  95. // Split arguments of an entered command
  96. private static readonly List<string> commandArguments = new List<string>( 8 );
  97. // Command parameter delimeter groups
  98. private static readonly string[] inputDelimiters = new string[] { "\"\"", "''", "{}", "()", "[]" };
  99. static DebugLogConsole()
  100. {
  101. #if UNITY_EDITOR || !NETFX_CORE
  102. // Load commands in most common Unity assemblies
  103. HashSet<Assembly> assemblies = new HashSet<Assembly> { Assembly.GetAssembly( typeof( DebugLogConsole ) )};
  104. try
  105. {
  106. assemblies.Add( Assembly.Load( "Assembly-CSharp" ));
  107. }
  108. catch { }
  109. foreach( var assembly in assemblies )
  110. {
  111. foreach( var type in assembly.GetExportedTypes() )
  112. {
  113. foreach( var method in type.GetMethods( BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly ) )
  114. {
  115. foreach( var attribute in method.GetCustomAttributes( typeof( ConsoleMethodAttribute ), false ) )
  116. {
  117. ConsoleMethodAttribute consoleMethod = attribute as ConsoleMethodAttribute;
  118. if( consoleMethod != null )
  119. AddCommand( consoleMethod.Command, consoleMethod.Description, method );
  120. }
  121. }
  122. }
  123. }
  124. #endif
  125. AddCommand( "help", "Prints all commands", LogAllCommands );
  126. AddCommand( "sysinfo", "Prints system information", LogSystemInfo );
  127. }
  128. // Logs the list of available commands
  129. public static void LogAllCommands()
  130. {
  131. int length = 25;
  132. for( int i = 0; i < methods.Count; i++ )
  133. {
  134. if( methods[i].IsValid() )
  135. length += 3 + methods[i].signature.Length;
  136. }
  137. StringBuilder stringBuilder = new StringBuilder( length );
  138. stringBuilder.Append( "Available commands:" );
  139. for( int i = 0; i < methods.Count; i++ )
  140. {
  141. if( methods[i].IsValid() )
  142. stringBuilder.Append( "\n- " ).Append( methods[i].signature );
  143. }
  144. Debug.Log( stringBuilder.Append( "\n" ).ToString() );
  145. // After typing help, the log that lists all the commands should automatically be expanded for better UX
  146. if( DebugLogManager.Instance )
  147. DebugLogManager.Instance.ExpandLatestPendingLog();
  148. }
  149. // Logs system information
  150. public static void LogSystemInfo()
  151. {
  152. StringBuilder stringBuilder = new StringBuilder( 1024 );
  153. stringBuilder.Append( "Rig: " ).AppendSysInfoIfPresent( SystemInfo.deviceModel ).AppendSysInfoIfPresent( SystemInfo.processorType )
  154. .AppendSysInfoIfPresent( SystemInfo.systemMemorySize, "MB RAM" ).Append( SystemInfo.processorCount ).Append( " cores\n" );
  155. stringBuilder.Append( "OS: " ).Append( SystemInfo.operatingSystem ).Append( "\n" );
  156. stringBuilder.Append( "GPU: " ).Append( SystemInfo.graphicsDeviceName ).Append( " " ).Append( SystemInfo.graphicsMemorySize )
  157. .Append( "MB " ).Append( SystemInfo.graphicsDeviceVersion )
  158. .Append( SystemInfo.graphicsMultiThreaded ? " multi-threaded\n" : "\n" );
  159. stringBuilder.Append( "Data Path: " ).Append( Application.dataPath ).Append( "\n" );
  160. stringBuilder.Append( "Persistent Data Path: " ).Append( Application.persistentDataPath ).Append( "\n" );
  161. stringBuilder.Append( "StreamingAssets Path: " ).Append( Application.streamingAssetsPath ).Append( "\n" );
  162. stringBuilder.Append( "Temporary Cache Path: " ).Append( Application.temporaryCachePath ).Append( "\n" );
  163. stringBuilder.Append( "Device ID: " ).Append( SystemInfo.deviceUniqueIdentifier ).Append( "\n" );
  164. stringBuilder.Append( "Max Texture Size: " ).Append( SystemInfo.maxTextureSize ).Append( "\n" );
  165. #if UNITY_5_6_OR_NEWER
  166. stringBuilder.Append( "Max Cubemap Size: " ).Append( SystemInfo.maxCubemapSize ).Append( "\n" );
  167. #endif
  168. stringBuilder.Append( "Accelerometer: " ).Append( SystemInfo.supportsAccelerometer ? "supported\n" : "not supported\n" );
  169. stringBuilder.Append( "Gyro: " ).Append( SystemInfo.supportsGyroscope ? "supported\n" : "not supported\n" );
  170. stringBuilder.Append( "Location Service: " ).Append( SystemInfo.supportsLocationService ? "supported\n" : "not supported\n" );
  171. #if !UNITY_2019_1_OR_NEWER
  172. stringBuilder.Append( "Image Effects: " ).Append( SystemInfo.supportsImageEffects ? "supported\n" : "not supported\n" );
  173. stringBuilder.Append( "RenderToCubemap: " ).Append( SystemInfo.supportsRenderToCubemap ? "supported\n" : "not supported\n" );
  174. #endif
  175. stringBuilder.Append( "Compute Shaders: " ).Append( SystemInfo.supportsComputeShaders ? "supported\n" : "not supported\n" );
  176. stringBuilder.Append( "Shadows: " ).Append( SystemInfo.supportsShadows ? "supported\n" : "not supported\n" );
  177. stringBuilder.Append( "Instancing: " ).Append( SystemInfo.supportsInstancing ? "supported\n" : "not supported\n" );
  178. stringBuilder.Append( "Motion Vectors: " ).Append( SystemInfo.supportsMotionVectors ? "supported\n" : "not supported\n" );
  179. stringBuilder.Append( "3D Textures: " ).Append( SystemInfo.supports3DTextures ? "supported\n" : "not supported\n" );
  180. #if UNITY_5_6_OR_NEWER
  181. stringBuilder.Append( "3D Render Textures: " ).Append( SystemInfo.supports3DRenderTextures ? "supported\n" : "not supported\n" );
  182. #endif
  183. stringBuilder.Append( "2D Array Textures: " ).Append( SystemInfo.supports2DArrayTextures ? "supported\n" : "not supported\n" );
  184. stringBuilder.Append( "Cubemap Array Textures: " ).Append( SystemInfo.supportsCubemapArrayTextures ? "supported" : "not supported" );
  185. Debug.Log( stringBuilder.Append( "\n" ).ToString() );
  186. // After typing sysinfo, the log that lists system information should automatically be expanded for better UX
  187. if( DebugLogManager.Instance )
  188. DebugLogManager.Instance.ExpandLatestPendingLog();
  189. }
  190. private static StringBuilder AppendSysInfoIfPresent( this StringBuilder sb, string info, string postfix = null )
  191. {
  192. if( info != SystemInfo.unsupportedIdentifier )
  193. {
  194. sb.Append( info );
  195. if( postfix != null )
  196. sb.Append( postfix );
  197. sb.Append( " " );
  198. }
  199. return sb;
  200. }
  201. private static StringBuilder AppendSysInfoIfPresent( this StringBuilder sb, int info, string postfix = null )
  202. {
  203. if( info > 0 )
  204. {
  205. sb.Append( info );
  206. if( postfix != null )
  207. sb.Append( postfix );
  208. sb.Append( " " );
  209. }
  210. return sb;
  211. }
  212. // Add a custom Type to the list of recognized command parameter Types
  213. public static void AddCustomParameterType( Type type, ParseFunction parseFunction, string typeReadableName = null )
  214. {
  215. if( type == null )
  216. {
  217. Debug.LogError( "Parameter type can't be null!" );
  218. return;
  219. }
  220. else if( parseFunction == null )
  221. {
  222. Debug.LogError( "Parameter parseFunction can't be null!" );
  223. return;
  224. }
  225. parseFunctions[type] = parseFunction;
  226. if( !string.IsNullOrEmpty( typeReadableName ) )
  227. typeReadableNames[type] = typeReadableName;
  228. }
  229. // Remove a custom Type from the list of recognized command parameter Types
  230. public static void RemoveCustomParameterType( Type type )
  231. {
  232. parseFunctions.Remove( type );
  233. typeReadableNames.Remove( type );
  234. }
  235. // Add a command related with an instance method (i.e. non static method)
  236. public static void AddCommandInstance( string command, string description, string methodName, object instance )
  237. {
  238. if( instance == null )
  239. {
  240. Debug.LogError( "Instance can't be null!" );
  241. return;
  242. }
  243. AddCommand( command, description, methodName, instance.GetType(), instance );
  244. }
  245. // Add a command related with a static method (i.e. no instance is required to call the method)
  246. public static void AddCommandStatic( string command, string description, string methodName, Type ownerType )
  247. {
  248. AddCommand( command, description, methodName, ownerType );
  249. }
  250. // Add a command that can be related to either a static or an instance method
  251. public static void AddCommand( string command, string description, Action method ) { AddCommand( command, description, method.Method, method.Target ); }
  252. public static void AddCommand<T1>( string command, string description, Action<T1> method ) { AddCommand( command, description, method.Method, method.Target ); }
  253. public static void AddCommand<T1>( string command, string description, Func<T1> method ) { AddCommand( command, description, method.Method, method.Target ); }
  254. public static void AddCommand<T1, T2>( string command, string description, Action<T1, T2> method ) { AddCommand( command, description, method.Method, method.Target ); }
  255. public static void AddCommand<T1, T2>( string command, string description, Func<T1, T2> method ) { AddCommand( command, description, method.Method, method.Target ); }
  256. public static void AddCommand<T1, T2, T3>( string command, string description, Action<T1, T2, T3> method ) { AddCommand( command, description, method.Method, method.Target ); }
  257. public static void AddCommand<T1, T2, T3>( string command, string description, Func<T1, T2, T3> method ) { AddCommand( command, description, method.Method, method.Target ); }
  258. public static void AddCommand<T1, T2, T3, T4>( string command, string description, Action<T1, T2, T3, T4> method ) { AddCommand( command, description, method.Method, method.Target ); }
  259. public static void AddCommand<T1, T2, T3, T4>( string command, string description, Func<T1, T2, T3, T4> method ) { AddCommand( command, description, method.Method, method.Target ); }
  260. public static void AddCommand<T1, T2, T3, T4, T5>( string command, string description, Func<T1, T2, T3, T4, T5> method ) { AddCommand( command, description, method.Method, method.Target ); }
  261. public static void AddCommand( string command, string description, Delegate method ) { AddCommand( command, description, method.Method, method.Target ); }
  262. // Create a new command and set its properties
  263. private static void AddCommand( string command, string description, string methodName, Type ownerType, object instance = null )
  264. {
  265. // Get the method from the class
  266. MethodInfo method = ownerType.GetMethod( methodName, BindingFlags.Public | BindingFlags.NonPublic | ( instance != null ? BindingFlags.Instance : BindingFlags.Static ) );
  267. if( method == null )
  268. {
  269. Debug.LogError( methodName + " does not exist in " + ownerType );
  270. return;
  271. }
  272. AddCommand( command, description, method, instance );
  273. }
  274. private static void AddCommand( string command, string description, MethodInfo method, object instance = null )
  275. {
  276. if( string.IsNullOrEmpty( command ) )
  277. {
  278. Debug.LogError( "Command name can't be empty!" );
  279. return;
  280. }
  281. command = command.Trim();
  282. if( command.IndexOf( ' ' ) >= 0 )
  283. {
  284. Debug.LogError( "Command name can't contain whitespace: " + command );
  285. return;
  286. }
  287. // Fetch the parameters of the class
  288. ParameterInfo[] parameters = method.GetParameters();
  289. if( parameters == null )
  290. parameters = new ParameterInfo[0];
  291. // Store the parameter types in an array
  292. Type[] parameterTypes = new Type[parameters.Length];
  293. for( int i = 0; i < parameters.Length; i++ )
  294. {
  295. if( parameters[i].ParameterType.IsByRef )
  296. {
  297. Debug.LogError( "Command can't have 'out' or 'ref' parameters" );
  298. return;
  299. }
  300. Type parameterType = parameters[i].ParameterType;
  301. if( parseFunctions.ContainsKey( parameterType ) || typeof( Component ).IsAssignableFrom( parameterType ) || parameterType.IsEnum || IsSupportedArrayType( parameterType ) )
  302. parameterTypes[i] = parameterType;
  303. else
  304. {
  305. Debug.LogError( string.Concat( "Parameter ", parameters[i].Name, "'s Type ", parameterType, " isn't supported" ) );
  306. return;
  307. }
  308. }
  309. int commandIndex = FindCommandIndex( command );
  310. if( commandIndex < 0 )
  311. commandIndex = ~commandIndex;
  312. else
  313. {
  314. int commandFirstIndex = commandIndex;
  315. int commandLastIndex = commandIndex;
  316. while( commandFirstIndex > 0 && methods[commandFirstIndex - 1].command == command )
  317. commandFirstIndex--;
  318. while( commandLastIndex < methods.Count - 1 && methods[commandLastIndex + 1].command == command )
  319. commandLastIndex++;
  320. commandIndex = commandFirstIndex;
  321. for( int i = commandFirstIndex; i <= commandLastIndex; i++ )
  322. {
  323. int parameterCountDiff = methods[i].parameterTypes.Length - parameterTypes.Length;
  324. if( parameterCountDiff <= 0 )
  325. {
  326. // We are sorting the commands in 2 steps:
  327. // 1: Sorting by their 'command' names which is handled by FindCommandIndex
  328. // 2: Sorting by their parameter counts which is handled here (parameterCountDiff <= 0)
  329. commandIndex = i + 1;
  330. // Check if this command has been registered before and if it is, overwrite that command
  331. if( parameterCountDiff == 0 )
  332. {
  333. int j = 0;
  334. while( j < parameterTypes.Length && parameterTypes[j] == methods[i].parameterTypes[j] )
  335. j++;
  336. if( j >= parameterTypes.Length )
  337. {
  338. commandIndex = i;
  339. commandLastIndex--;
  340. methods.RemoveAt( i-- );
  341. continue;
  342. }
  343. }
  344. }
  345. }
  346. }
  347. // Check if this command has been registered before and if it is, overwrite that command
  348. for( int i = methods.Count - 1; i >= 0; i-- )
  349. {
  350. if( !methods[i].IsValid() )
  351. {
  352. methods.RemoveAt( i );
  353. continue;
  354. }
  355. if( methods[i].command == command && methods[i].parameterTypes.Length == parameterTypes.Length )
  356. {
  357. int j = 0;
  358. while( j < parameterTypes.Length && parameterTypes[j] == methods[i].parameterTypes[j] )
  359. j++;
  360. if( j >= parameterTypes.Length )
  361. {
  362. methods.RemoveAt( i );
  363. break;
  364. }
  365. }
  366. }
  367. // Create the command
  368. StringBuilder methodSignature = new StringBuilder( 256 );
  369. string[] parameterSignatures = new string[parameterTypes.Length];
  370. methodSignature.Append( command ).Append( ": " );
  371. if( !string.IsNullOrEmpty( description ) )
  372. methodSignature.Append( description ).Append( " -> " );
  373. methodSignature.Append( method.DeclaringType.ToString() ).Append( "." ).Append( method.Name ).Append( "(" );
  374. for( int i = 0; i < parameterTypes.Length; i++ )
  375. {
  376. int parameterSignatureStartIndex = methodSignature.Length;
  377. methodSignature.Append( GetTypeReadableName( parameterTypes[i] ) ).Append( " " ).Append( parameters[i].Name );
  378. if( i < parameterTypes.Length - 1 )
  379. methodSignature.Append( ", " );
  380. parameterSignatures[i] = methodSignature.ToString( parameterSignatureStartIndex, methodSignature.Length - parameterSignatureStartIndex );
  381. }
  382. methodSignature.Append( ")" );
  383. Type returnType = method.ReturnType;
  384. if( returnType != typeof( void ) )
  385. methodSignature.Append( " : " ).Append( GetTypeReadableName( returnType ) );
  386. methods.Insert( commandIndex, new ConsoleMethodInfo( method, parameterTypes, instance, command, methodSignature.ToString(), parameterSignatures ) );
  387. }
  388. // Remove all commands with the matching command name from the console
  389. public static void RemoveCommand( string command )
  390. {
  391. if( !string.IsNullOrEmpty( command ) )
  392. {
  393. for( int i = methods.Count - 1; i >= 0; i-- )
  394. {
  395. if( methods[i].command == command )
  396. methods.RemoveAt( i );
  397. }
  398. }
  399. }
  400. // Remove all commands with the matching method from the console
  401. public static void RemoveCommand( Action method ) { RemoveCommand( method.Method ); }
  402. public static void RemoveCommand<T1>( Action<T1> method ) { RemoveCommand( method.Method ); }
  403. public static void RemoveCommand<T1>( Func<T1> method ) { RemoveCommand( method.Method ); }
  404. public static void RemoveCommand<T1, T2>( Action<T1, T2> method ) { RemoveCommand( method.Method ); }
  405. public static void RemoveCommand<T1, T2>( Func<T1, T2> method ) { RemoveCommand( method.Method ); }
  406. public static void RemoveCommand<T1, T2, T3>( Action<T1, T2, T3> method ) { RemoveCommand( method.Method ); }
  407. public static void RemoveCommand<T1, T2, T3>( Func<T1, T2, T3> method ) { RemoveCommand( method.Method ); }
  408. public static void RemoveCommand<T1, T2, T3, T4>( Action<T1, T2, T3, T4> method ) { RemoveCommand( method.Method ); }
  409. public static void RemoveCommand<T1, T2, T3, T4>( Func<T1, T2, T3, T4> method ) { RemoveCommand( method.Method ); }
  410. public static void RemoveCommand<T1, T2, T3, T4, T5>( Func<T1, T2, T3, T4, T5> method ) { RemoveCommand( method.Method ); }
  411. public static void RemoveCommand( Delegate method ) { RemoveCommand( method.Method ); }
  412. public static void RemoveCommand( MethodInfo method )
  413. {
  414. if( method != null )
  415. {
  416. for( int i = methods.Count - 1; i >= 0; i-- )
  417. {
  418. if( methods[i].method == method )
  419. methods.RemoveAt( i );
  420. }
  421. }
  422. }
  423. // Returns the first command that starts with the entered argument
  424. public static string GetAutoCompleteCommand( string commandStart )
  425. {
  426. int commandIndex = FindCommandIndex( commandStart );
  427. if( commandIndex < 0 )
  428. commandIndex = ~commandIndex;
  429. string result = null;
  430. for( int i = commandIndex; i >= 0 && methods[i].command.StartsWith( commandStart ); i-- )
  431. result = methods[i].command;
  432. if( result == null )
  433. {
  434. for( int i = commandIndex + 1; i < methods.Count && methods[i].command.StartsWith( commandStart ); i++ )
  435. result = methods[i].command;
  436. }
  437. return result;
  438. }
  439. // Parse the command and try to execute it
  440. public static void ExecuteCommand( string command )
  441. {
  442. if( command == null )
  443. return;
  444. command = command.Trim();
  445. if( command.Length == 0 )
  446. return;
  447. // Split the command's arguments
  448. commandArguments.Clear();
  449. FetchArgumentsFromCommand( command, commandArguments );
  450. // Find all matching commands
  451. matchingMethods.Clear();
  452. bool parameterCountMismatch = false;
  453. int commandIndex = FindCommandIndex( commandArguments[0] );
  454. if( commandIndex >= 0 )
  455. {
  456. string _command = commandArguments[0];
  457. int commandLastIndex = commandIndex;
  458. while( commandIndex > 0 && methods[commandIndex - 1].command == _command )
  459. commandIndex--;
  460. while( commandLastIndex < methods.Count - 1 && methods[commandLastIndex + 1].command == _command )
  461. commandLastIndex++;
  462. while( commandIndex <= commandLastIndex )
  463. {
  464. if( !methods[commandIndex].IsValid() )
  465. {
  466. methods.RemoveAt( commandIndex );
  467. commandLastIndex--;
  468. }
  469. else
  470. {
  471. // Check if number of parameters match
  472. if( methods[commandIndex].parameterTypes.Length == commandArguments.Count - 1 )
  473. matchingMethods.Add( methods[commandIndex] );
  474. else
  475. parameterCountMismatch = true;
  476. commandIndex++;
  477. }
  478. }
  479. }
  480. if( matchingMethods.Count == 0 )
  481. {
  482. if( parameterCountMismatch )
  483. Debug.LogWarning( string.Concat( "ERROR: ", commandArguments[0], " doesn't take ", commandArguments.Count - 1, " parameter(s)" ) );
  484. else
  485. Debug.LogWarning( "ERROR: can't find command: " + commandArguments[0] );
  486. return;
  487. }
  488. ConsoleMethodInfo methodToExecute = null;
  489. object[] parameters = new object[commandArguments.Count - 1];
  490. string errorMessage = null;
  491. for( int i = 0; i < matchingMethods.Count && methodToExecute == null; i++ )
  492. {
  493. ConsoleMethodInfo methodInfo = matchingMethods[i];
  494. // Parse the parameters into objects
  495. bool success = true;
  496. for( int j = 0; j < methodInfo.parameterTypes.Length && success; j++ )
  497. {
  498. try
  499. {
  500. string argument = commandArguments[j + 1];
  501. Type parameterType = methodInfo.parameterTypes[j];
  502. object val;
  503. if( ParseArgument( argument, parameterType, out val ) )
  504. parameters[j] = val;
  505. else
  506. {
  507. success = false;
  508. errorMessage = string.Concat( "ERROR: couldn't parse ", argument, " to ", GetTypeReadableName( parameterType ) );
  509. }
  510. }
  511. catch( Exception e )
  512. {
  513. success = false;
  514. errorMessage = "ERROR: " + e.ToString();
  515. }
  516. }
  517. if( success )
  518. methodToExecute = methodInfo;
  519. }
  520. if( methodToExecute == null )
  521. Debug.LogWarning( !string.IsNullOrEmpty( errorMessage ) ? errorMessage : "ERROR: something went wrong" );
  522. else
  523. {
  524. Debug.Log( "Executing command: " + commandArguments[0] );
  525. // Execute the method associated with the command
  526. object result = methodToExecute.method.Invoke( methodToExecute.instance, parameters );
  527. if( methodToExecute.method.ReturnType != typeof( void ) )
  528. {
  529. // Print the returned value to the console
  530. if( result == null || result.Equals( null ) )
  531. Debug.Log( "Value returned: null" );
  532. else
  533. Debug.Log( "Value returned: " + result.ToString() );
  534. }
  535. }
  536. }
  537. public static void FetchArgumentsFromCommand( string command, List<string> commandArguments )
  538. {
  539. for( int i = 0; i < command.Length; i++ )
  540. {
  541. if( char.IsWhiteSpace( command[i] ) )
  542. continue;
  543. int delimiterIndex = IndexOfDelimiterGroup( command[i] );
  544. if( delimiterIndex >= 0 )
  545. {
  546. int endIndex = IndexOfDelimiterGroupEnd( command, delimiterIndex, i + 1 );
  547. commandArguments.Add( command.Substring( i + 1, endIndex - i - 1 ) );
  548. i = ( endIndex < command.Length - 1 && command[endIndex + 1] == ',' ) ? endIndex + 1 : endIndex;
  549. }
  550. else
  551. {
  552. int endIndex = IndexOfChar( command, ' ', i + 1 );
  553. commandArguments.Add( command.Substring( i, command[endIndex - 1] == ',' ? endIndex - 1 - i : endIndex - i ) );
  554. i = endIndex;
  555. }
  556. }
  557. }
  558. // Finds all commands that have a matching signature with command
  559. // - caretIndexIncrements: indices inside "string command" that separate two arguments in the command. This is used to
  560. // figure out which argument the caret is standing on
  561. // - commandName: command's name (first argument)
  562. internal static void GetCommandSuggestions( string command, List<ConsoleMethodInfo> matchingCommands, List<int> caretIndexIncrements, ref string commandName, out int numberOfParameters )
  563. {
  564. bool commandNameCalculated = false;
  565. bool commandNameFullyTyped = false;
  566. numberOfParameters = -1;
  567. for( int i = 0; i < command.Length; i++ )
  568. {
  569. if( char.IsWhiteSpace( command[i] ) )
  570. continue;
  571. int delimiterIndex = IndexOfDelimiterGroup( command[i] );
  572. if( delimiterIndex >= 0 )
  573. {
  574. int endIndex = IndexOfDelimiterGroupEnd( command, delimiterIndex, i + 1 );
  575. if( !commandNameCalculated )
  576. {
  577. commandNameCalculated = true;
  578. commandNameFullyTyped = command.Length > endIndex;
  579. int commandNameLength = endIndex - i - 1;
  580. if( commandName == null || commandNameLength == 0 || commandName.Length != commandNameLength || command.IndexOf( commandName, i + 1, commandNameLength ) != i + 1 )
  581. commandName = command.Substring( i + 1, commandNameLength );
  582. }
  583. i = ( endIndex < command.Length - 1 && command[endIndex + 1] == ',' ) ? endIndex + 1 : endIndex;
  584. caretIndexIncrements.Add( i + 1 );
  585. }
  586. else
  587. {
  588. int endIndex = IndexOfChar( command, ' ', i + 1 );
  589. if( !commandNameCalculated )
  590. {
  591. commandNameCalculated = true;
  592. commandNameFullyTyped = command.Length > endIndex;
  593. int commandNameLength = command[endIndex - 1] == ',' ? endIndex - 1 - i : endIndex - i;
  594. if( commandName == null || commandNameLength == 0 || commandName.Length != commandNameLength || command.IndexOf( commandName, i, commandNameLength ) != i )
  595. commandName = command.Substring( i, commandNameLength );
  596. }
  597. i = endIndex;
  598. caretIndexIncrements.Add( i );
  599. }
  600. numberOfParameters++;
  601. }
  602. if( !commandNameCalculated )
  603. commandName = string.Empty;
  604. if( !string.IsNullOrEmpty( commandName ) )
  605. {
  606. int commandIndex = FindCommandIndex( commandName );
  607. if( commandIndex < 0 )
  608. commandIndex = ~commandIndex;
  609. int commandLastIndex = commandIndex;
  610. if( !commandNameFullyTyped )
  611. {
  612. // Match all commands that start with commandName
  613. if( commandIndex < methods.Count && methods[commandIndex].command.StartsWith( commandName ) )
  614. {
  615. while( commandIndex > 0 && methods[commandIndex - 1].command.StartsWith( commandName ) )
  616. commandIndex--;
  617. while( commandLastIndex < methods.Count - 1 && methods[commandLastIndex + 1].command.StartsWith( commandName ) )
  618. commandLastIndex++;
  619. }
  620. else
  621. commandLastIndex = -1;
  622. }
  623. else
  624. {
  625. // Match only the commands that are equal to commandName
  626. if( commandIndex < methods.Count && methods[commandIndex].command == commandName )
  627. {
  628. while( commandIndex > 0 && methods[commandIndex - 1].command == commandName )
  629. commandIndex--;
  630. while( commandLastIndex < methods.Count - 1 && methods[commandLastIndex + 1].command == commandName )
  631. commandLastIndex++;
  632. }
  633. else
  634. commandLastIndex = -1;
  635. }
  636. for( ; commandIndex <= commandLastIndex; commandIndex++ )
  637. {
  638. if( methods[commandIndex].parameterTypes.Length >= numberOfParameters )
  639. matchingCommands.Add( methods[commandIndex] );
  640. }
  641. }
  642. }
  643. // Find the index of the delimiter group that 'c' belongs to
  644. private static int IndexOfDelimiterGroup( char c )
  645. {
  646. for( int i = 0; i < inputDelimiters.Length; i++ )
  647. {
  648. if( c == inputDelimiters[i][0] )
  649. return i;
  650. }
  651. return -1;
  652. }
  653. private static int IndexOfDelimiterGroupEnd( string command, int delimiterIndex, int startIndex )
  654. {
  655. char startChar = inputDelimiters[delimiterIndex][0];
  656. char endChar = inputDelimiters[delimiterIndex][1];
  657. // Check delimiter's depth for array support (e.g. [[1 2] [3 4]] for Vector2 array)
  658. int depth = 1;
  659. for( int i = startIndex; i < command.Length; i++ )
  660. {
  661. char c = command[i];
  662. if( c == endChar && --depth <= 0 )
  663. return i;
  664. else if( c == startChar )
  665. depth++;
  666. }
  667. return command.Length;
  668. }
  669. // Find the index of char in the string, or return the length of string instead of -1
  670. private static int IndexOfChar( string command, char c, int startIndex )
  671. {
  672. int result = command.IndexOf( c, startIndex );
  673. if( result < 0 )
  674. result = command.Length;
  675. return result;
  676. }
  677. // Find command's index in the list of registered commands using binary search
  678. private static int FindCommandIndex( string command )
  679. {
  680. int min = 0;
  681. int max = methods.Count - 1;
  682. while( min <= max )
  683. {
  684. int mid = ( min + max ) / 2;
  685. int comparison = command.CompareTo( methods[mid].command );
  686. if( comparison == 0 )
  687. return mid;
  688. else if( comparison < 0 )
  689. max = mid - 1;
  690. else
  691. min = mid + 1;
  692. }
  693. return ~min;
  694. }
  695. public static bool IsSupportedArrayType( Type type )
  696. {
  697. if( type.IsArray )
  698. {
  699. if( type.GetArrayRank() != 1 )
  700. return false;
  701. type = type.GetElementType();
  702. }
  703. else if( type.IsGenericType )
  704. {
  705. if( type.GetGenericTypeDefinition() != typeof( List<> ) )
  706. return false;
  707. type = type.GetGenericArguments()[0];
  708. }
  709. else
  710. return false;
  711. return parseFunctions.ContainsKey( type ) || typeof( Component ).IsAssignableFrom( type ) || type.IsEnum;
  712. }
  713. public static string GetTypeReadableName( Type type )
  714. {
  715. string result;
  716. if( typeReadableNames.TryGetValue( type, out result ) )
  717. return result;
  718. if( IsSupportedArrayType( type ) )
  719. {
  720. Type elementType = type.IsArray ? type.GetElementType() : type.GetGenericArguments()[0];
  721. if( typeReadableNames.TryGetValue( elementType, out result ) )
  722. return result + "[]";
  723. else
  724. return elementType.Name + "[]";
  725. }
  726. return type.Name;
  727. }
  728. public static bool ParseArgument( string input, Type argumentType, out object output )
  729. {
  730. ParseFunction parseFunction;
  731. if( parseFunctions.TryGetValue( argumentType, out parseFunction ) )
  732. return parseFunction( input, out output );
  733. else if( typeof( Component ).IsAssignableFrom( argumentType ) )
  734. return ParseComponent( input, argumentType, out output );
  735. else if( argumentType.IsEnum )
  736. return ParseEnum( input, argumentType, out output );
  737. else if( IsSupportedArrayType( argumentType ) )
  738. return ParseArray( input, argumentType, out output );
  739. else
  740. {
  741. output = null;
  742. return false;
  743. }
  744. }
  745. public static bool ParseString( string input, out object output )
  746. {
  747. output = input;
  748. return true;
  749. }
  750. public static bool ParseBool( string input, out object output )
  751. {
  752. if( input == "1" || input.ToLowerInvariant() == "true" )
  753. {
  754. output = true;
  755. return true;
  756. }
  757. if( input == "0" || input.ToLowerInvariant() == "false" )
  758. {
  759. output = false;
  760. return true;
  761. }
  762. output = false;
  763. return false;
  764. }
  765. public static bool ParseInt( string input, out object output )
  766. {
  767. int value;
  768. bool result = int.TryParse( input, out value );
  769. output = value;
  770. return result;
  771. }
  772. public static bool ParseUInt( string input, out object output )
  773. {
  774. uint value;
  775. bool result = uint.TryParse( input, out value );
  776. output = value;
  777. return result;
  778. }
  779. public static bool ParseLong( string input, out object output )
  780. {
  781. long value;
  782. bool result = long.TryParse( !input.EndsWith( "L", StringComparison.OrdinalIgnoreCase ) ? input : input.Substring( 0, input.Length - 1 ), out value );
  783. output = value;
  784. return result;
  785. }
  786. public static bool ParseULong( string input, out object output )
  787. {
  788. ulong value;
  789. bool result = ulong.TryParse( !input.EndsWith( "L", StringComparison.OrdinalIgnoreCase ) ? input : input.Substring( 0, input.Length - 1 ), out value );
  790. output = value;
  791. return result;
  792. }
  793. public static bool ParseByte( string input, out object output )
  794. {
  795. byte value;
  796. bool result = byte.TryParse( input, out value );
  797. output = value;
  798. return result;
  799. }
  800. public static bool ParseSByte( string input, out object output )
  801. {
  802. sbyte value;
  803. bool result = sbyte.TryParse( input, out value );
  804. output = value;
  805. return result;
  806. }
  807. public static bool ParseShort( string input, out object output )
  808. {
  809. short value;
  810. bool result = short.TryParse( input, out value );
  811. output = value;
  812. return result;
  813. }
  814. public static bool ParseUShort( string input, out object output )
  815. {
  816. ushort value;
  817. bool result = ushort.TryParse( input, out value );
  818. output = value;
  819. return result;
  820. }
  821. public static bool ParseChar( string input, out object output )
  822. {
  823. char value;
  824. bool result = char.TryParse( input, out value );
  825. output = value;
  826. return result;
  827. }
  828. public static bool ParseFloat( string input, out object output )
  829. {
  830. float value;
  831. bool result = float.TryParse( !input.EndsWith( "f", StringComparison.OrdinalIgnoreCase ) ? input : input.Substring( 0, input.Length - 1 ), out value );
  832. output = value;
  833. return result;
  834. }
  835. public static bool ParseDouble( string input, out object output )
  836. {
  837. double value;
  838. bool result = double.TryParse( !input.EndsWith( "f", StringComparison.OrdinalIgnoreCase ) ? input : input.Substring( 0, input.Length - 1 ), out value );
  839. output = value;
  840. return result;
  841. }
  842. public static bool ParseDecimal( string input, out object output )
  843. {
  844. decimal value;
  845. bool result = decimal.TryParse( !input.EndsWith( "f", StringComparison.OrdinalIgnoreCase ) ? input : input.Substring( 0, input.Length - 1 ), out value );
  846. output = value;
  847. return result;
  848. }
  849. public static bool ParseVector2( string input, out object output )
  850. {
  851. return ParseVector( input, typeof( Vector2 ), out output );
  852. }
  853. public static bool ParseVector3( string input, out object output )
  854. {
  855. return ParseVector( input, typeof( Vector3 ), out output );
  856. }
  857. public static bool ParseVector4( string input, out object output )
  858. {
  859. return ParseVector( input, typeof( Vector4 ), out output );
  860. }
  861. public static bool ParseQuaternion( string input, out object output )
  862. {
  863. return ParseVector( input, typeof( Quaternion ), out output );
  864. }
  865. public static bool ParseColor( string input, out object output )
  866. {
  867. return ParseVector( input, typeof( Color ), out output );
  868. }
  869. public static bool ParseColor32( string input, out object output )
  870. {
  871. return ParseVector( input, typeof( Color32 ), out output );
  872. }
  873. public static bool ParseRect( string input, out object output )
  874. {
  875. return ParseVector( input, typeof( Rect ), out output );
  876. }
  877. public static bool ParseRectOffset( string input, out object output )
  878. {
  879. return ParseVector( input, typeof( RectOffset ), out output );
  880. }
  881. public static bool ParseBounds( string input, out object output )
  882. {
  883. return ParseVector( input, typeof( Bounds ), out output );
  884. }
  885. #if UNITY_2017_2_OR_NEWER
  886. public static bool ParseVector2Int( string input, out object output )
  887. {
  888. return ParseVector( input, typeof( Vector2Int ), out output );
  889. }
  890. public static bool ParseVector3Int( string input, out object output )
  891. {
  892. return ParseVector( input, typeof( Vector3Int ), out output );
  893. }
  894. public static bool ParseRectInt( string input, out object output )
  895. {
  896. return ParseVector( input, typeof( RectInt ), out output );
  897. }
  898. public static bool ParseBoundsInt( string input, out object output )
  899. {
  900. return ParseVector( input, typeof( BoundsInt ), out output );
  901. }
  902. #endif
  903. public static bool ParseGameObject( string input, out object output )
  904. {
  905. output = input == "null" ? null : GameObject.Find( input );
  906. return true;
  907. }
  908. public static bool ParseComponent( string input, Type componentType, out object output )
  909. {
  910. GameObject gameObject = input == "null" ? null : GameObject.Find( input );
  911. output = gameObject ? gameObject.GetComponent( componentType ) : null;
  912. return true;
  913. }
  914. public static bool ParseEnum( string input, Type enumType, out object output )
  915. {
  916. const int NONE = 0, OR = 1, AND = 2;
  917. int outputInt = 0;
  918. int operation = 0; // 0: nothing, 1: OR with outputInt, 2: AND with outputInt
  919. for( int i = 0; i < input.Length; i++ )
  920. {
  921. string enumStr;
  922. int orIndex = input.IndexOf( '|', i );
  923. int andIndex = input.IndexOf( '&', i );
  924. if( orIndex < 0 )
  925. enumStr = input.Substring( i, ( andIndex < 0 ? input.Length : andIndex ) - i ).Trim();
  926. else
  927. enumStr = input.Substring( i, ( andIndex < 0 ? orIndex : Mathf.Min( andIndex, orIndex ) ) - i ).Trim();
  928. int value;
  929. if( !int.TryParse( enumStr, out value ) )
  930. {
  931. if( Enum.IsDefined( enumType, enumStr ) )
  932. value = Convert.ToInt32( Enum.Parse( enumType, enumStr ) );
  933. else
  934. {
  935. output = null;
  936. return false;
  937. }
  938. }
  939. if( operation == NONE )
  940. outputInt = value;
  941. else if( operation == OR )
  942. outputInt |= value;
  943. else
  944. outputInt &= value;
  945. if( orIndex >= 0 )
  946. {
  947. if( andIndex > orIndex )
  948. {
  949. operation = AND;
  950. i = andIndex;
  951. }
  952. else
  953. {
  954. operation = OR;
  955. i = orIndex;
  956. }
  957. }
  958. else if( andIndex >= 0 )
  959. {
  960. operation = AND;
  961. i = andIndex;
  962. }
  963. else
  964. i = input.Length;
  965. }
  966. output = Enum.ToObject( enumType, outputInt );
  967. return true;
  968. }
  969. public static bool ParseArray( string input, Type arrayType, out object output )
  970. {
  971. List<string> valuesToParse = new List<string>( 2 );
  972. FetchArgumentsFromCommand( input, valuesToParse );
  973. IList result = (IList) Activator.CreateInstance( arrayType, new object[1] { valuesToParse.Count } );
  974. output = result;
  975. if( arrayType.IsArray )
  976. {
  977. Type elementType = arrayType.GetElementType();
  978. for( int i = 0; i < valuesToParse.Count; i++ )
  979. {
  980. object obj;
  981. if( !ParseArgument( valuesToParse[i], elementType, out obj ) )
  982. return false;
  983. result[i] = obj;
  984. }
  985. }
  986. else
  987. {
  988. Type elementType = arrayType.GetGenericArguments()[0];
  989. for( int i = 0; i < valuesToParse.Count; i++ )
  990. {
  991. object obj;
  992. if( !ParseArgument( valuesToParse[i], elementType, out obj ) )
  993. return false;
  994. result.Add( obj );
  995. }
  996. }
  997. return true;
  998. }
  999. // Create a vector of specified type (fill the blank slots with 0 or ignore unnecessary slots)
  1000. private static bool ParseVector( string input, Type vectorType, out object output )
  1001. {
  1002. List<string> tokens = new List<string>( input.Replace( ',', ' ' ).Trim().Split( ' ' ) );
  1003. for( int i = tokens.Count - 1; i >= 0; i-- )
  1004. {
  1005. tokens[i] = tokens[i].Trim();
  1006. if( tokens[i].Length == 0 )
  1007. tokens.RemoveAt( i );
  1008. }
  1009. float[] tokenValues = new float[tokens.Count];
  1010. for( int i = 0; i < tokens.Count; i++ )
  1011. {
  1012. object val;
  1013. if( !ParseFloat( tokens[i], out val ) )
  1014. {
  1015. if( vectorType == typeof( Vector3 ) )
  1016. output = Vector3.zero;
  1017. else if( vectorType == typeof( Vector2 ) )
  1018. output = Vector2.zero;
  1019. else
  1020. output = Vector4.zero;
  1021. return false;
  1022. }
  1023. tokenValues[i] = (float) val;
  1024. }
  1025. if( vectorType == typeof( Vector3 ) )
  1026. {
  1027. Vector3 result = Vector3.zero;
  1028. for( int i = 0; i < tokenValues.Length && i < 3; i++ )
  1029. result[i] = tokenValues[i];
  1030. output = result;
  1031. }
  1032. else if( vectorType == typeof( Vector2 ) )
  1033. {
  1034. Vector2 result = Vector2.zero;
  1035. for( int i = 0; i < tokenValues.Length && i < 2; i++ )
  1036. result[i] = tokenValues[i];
  1037. output = result;
  1038. }
  1039. else if( vectorType == typeof( Vector4 ) )
  1040. {
  1041. Vector4 result = Vector4.zero;
  1042. for( int i = 0; i < tokenValues.Length && i < 4; i++ )
  1043. result[i] = tokenValues[i];
  1044. output = result;
  1045. }
  1046. else if( vectorType == typeof( Quaternion ) )
  1047. {
  1048. Quaternion result = Quaternion.identity;
  1049. for( int i = 0; i < tokenValues.Length && i < 4; i++ )
  1050. result[i] = tokenValues[i];
  1051. output = result;
  1052. }
  1053. else if( vectorType == typeof( Color ) )
  1054. {
  1055. Color result = Color.black;
  1056. for( int i = 0; i < tokenValues.Length && i < 4; i++ )
  1057. result[i] = tokenValues[i];
  1058. output = result;
  1059. }
  1060. else if( vectorType == typeof( Color32 ) )
  1061. {
  1062. Color32 result = new Color32( 0, 0, 0, 255 );
  1063. if( tokenValues.Length > 0 )
  1064. result.r = (byte) Mathf.RoundToInt( tokenValues[0] );
  1065. if( tokenValues.Length > 1 )
  1066. result.g = (byte) Mathf.RoundToInt( tokenValues[1] );
  1067. if( tokenValues.Length > 2 )
  1068. result.b = (byte) Mathf.RoundToInt( tokenValues[2] );
  1069. if( tokenValues.Length > 3 )
  1070. result.a = (byte) Mathf.RoundToInt( tokenValues[3] );
  1071. output = result;
  1072. }
  1073. else if( vectorType == typeof( Rect ) )
  1074. {
  1075. Rect result = Rect.zero;
  1076. if( tokenValues.Length > 0 )
  1077. result.x = tokenValues[0];
  1078. if( tokenValues.Length > 1 )
  1079. result.y = tokenValues[1];
  1080. if( tokenValues.Length > 2 )
  1081. result.width = tokenValues[2];
  1082. if( tokenValues.Length > 3 )
  1083. result.height = tokenValues[3];
  1084. output = result;
  1085. }
  1086. else if( vectorType == typeof( RectOffset ) )
  1087. {
  1088. RectOffset result = new RectOffset();
  1089. if( tokenValues.Length > 0 )
  1090. result.left = Mathf.RoundToInt( tokenValues[0] );
  1091. if( tokenValues.Length > 1 )
  1092. result.right = Mathf.RoundToInt( tokenValues[1] );
  1093. if( tokenValues.Length > 2 )
  1094. result.top = Mathf.RoundToInt( tokenValues[2] );
  1095. if( tokenValues.Length > 3 )
  1096. result.bottom = Mathf.RoundToInt( tokenValues[3] );
  1097. output = result;
  1098. }
  1099. else if( vectorType == typeof( Bounds ) )
  1100. {
  1101. Vector3 center = Vector3.zero;
  1102. for( int i = 0; i < tokenValues.Length && i < 3; i++ )
  1103. center[i] = tokenValues[i];
  1104. Vector3 size = Vector3.zero;
  1105. for( int i = 3; i < tokenValues.Length && i < 6; i++ )
  1106. size[i - 3] = tokenValues[i];
  1107. output = new Bounds( center, size );
  1108. }
  1109. #if UNITY_2017_2_OR_NEWER
  1110. else if( vectorType == typeof( Vector3Int ) )
  1111. {
  1112. Vector3Int result = Vector3Int.zero;
  1113. for( int i = 0; i < tokenValues.Length && i < 3; i++ )
  1114. result[i] = Mathf.RoundToInt( tokenValues[i] );
  1115. output = result;
  1116. }
  1117. else if( vectorType == typeof( Vector2Int ) )
  1118. {
  1119. Vector2Int result = Vector2Int.zero;
  1120. for( int i = 0; i < tokenValues.Length && i < 2; i++ )
  1121. result[i] = Mathf.RoundToInt( tokenValues[i] );
  1122. output = result;
  1123. }
  1124. else if( vectorType == typeof( RectInt ) )
  1125. {
  1126. RectInt result = new RectInt();
  1127. if( tokenValues.Length > 0 )
  1128. result.x = Mathf.RoundToInt( tokenValues[0] );
  1129. if( tokenValues.Length > 1 )
  1130. result.y = Mathf.RoundToInt( tokenValues[1] );
  1131. if( tokenValues.Length > 2 )
  1132. result.width = Mathf.RoundToInt( tokenValues[2] );
  1133. if( tokenValues.Length > 3 )
  1134. result.height = Mathf.RoundToInt( tokenValues[3] );
  1135. output = result;
  1136. }
  1137. else if( vectorType == typeof( BoundsInt ) )
  1138. {
  1139. Vector3Int center = Vector3Int.zero;
  1140. for( int i = 0; i < tokenValues.Length && i < 3; i++ )
  1141. center[i] = Mathf.RoundToInt( tokenValues[i] );
  1142. Vector3Int size = Vector3Int.zero;
  1143. for( int i = 3; i < tokenValues.Length && i < 6; i++ )
  1144. size[i - 3] = Mathf.RoundToInt( tokenValues[i] );
  1145. output = new BoundsInt( center, size );
  1146. }
  1147. #endif
  1148. else
  1149. {
  1150. output = null;
  1151. return false;
  1152. }
  1153. return true;
  1154. }
  1155. }
  1156. }