Util.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735
  1. //======= Copyright (c) Valve Corporation, All rights reserved. ===============
  2. //
  3. // Purpose: Utility functions used in several places
  4. //
  5. //=============================================================================
  6. using UnityEngine;
  7. using System;
  8. using System.Collections;
  9. using System.Collections.Generic;
  10. using System.IO;
  11. using System.Linq;
  12. namespace Valve.VR.InteractionSystem
  13. {
  14. //-------------------------------------------------------------------------
  15. public static class Util
  16. {
  17. public const float FeetToMeters = 0.3048f;
  18. public const float FeetToCentimeters = 30.48f;
  19. public const float InchesToMeters = 0.0254f;
  20. public const float InchesToCentimeters = 2.54f;
  21. public const float MetersToFeet = 3.28084f;
  22. public const float MetersToInches = 39.3701f;
  23. public const float CentimetersToFeet = 0.0328084f;
  24. public const float CentimetersToInches = 0.393701f;
  25. public const float KilometersToMiles = 0.621371f;
  26. public const float MilesToKilometers = 1.60934f;
  27. //-------------------------------------------------
  28. // Remap num from range 1 to range 2
  29. //-------------------------------------------------
  30. public static float RemapNumber( float num, float low1, float high1, float low2, float high2 )
  31. {
  32. return low2 + ( num - low1 ) * ( high2 - low2 ) / ( high1 - low1 );
  33. }
  34. //-------------------------------------------------
  35. public static float RemapNumberClamped( float num, float low1, float high1, float low2, float high2 )
  36. {
  37. return Mathf.Clamp( RemapNumber( num, low1, high1, low2, high2 ), Mathf.Min( low2, high2 ), Mathf.Max( low2, high2 ) );
  38. }
  39. //-------------------------------------------------
  40. public static float Approach( float target, float value, float speed )
  41. {
  42. float delta = target - value;
  43. if ( delta > speed )
  44. value += speed;
  45. else if ( delta < -speed )
  46. value -= speed;
  47. else
  48. value = target;
  49. return value;
  50. }
  51. //-------------------------------------------------
  52. public static Vector3 BezierInterpolate3( Vector3 p0, Vector3 c0, Vector3 p1, float t )
  53. {
  54. Vector3 p0c0 = Vector3.Lerp( p0, c0, t );
  55. Vector3 c0p1 = Vector3.Lerp( c0, p1, t );
  56. return Vector3.Lerp( p0c0, c0p1, t );
  57. }
  58. //-------------------------------------------------
  59. public static Vector3 BezierInterpolate4( Vector3 p0, Vector3 c0, Vector3 c1, Vector3 p1, float t )
  60. {
  61. Vector3 p0c0 = Vector3.Lerp( p0, c0, t );
  62. Vector3 c0c1 = Vector3.Lerp( c0, c1, t );
  63. Vector3 c1p1 = Vector3.Lerp( c1, p1, t );
  64. Vector3 x = Vector3.Lerp( p0c0, c0c1, t );
  65. Vector3 y = Vector3.Lerp( c0c1, c1p1, t );
  66. //Debug.DrawRay(p0, Vector3.forward);
  67. //Debug.DrawRay(c0, Vector3.forward);
  68. //Debug.DrawRay(c1, Vector3.forward);
  69. //Debug.DrawRay(p1, Vector3.forward);
  70. //Gizmos.DrawSphere(p0c0, 0.5F);
  71. //Gizmos.DrawSphere(c0c1, 0.5F);
  72. //Gizmos.DrawSphere(c1p1, 0.5F);
  73. //Gizmos.DrawSphere(x, 0.5F);
  74. //Gizmos.DrawSphere(y, 0.5F);
  75. return Vector3.Lerp( x, y, t );
  76. }
  77. //-------------------------------------------------
  78. public static Vector3 Vector3FromString( string szString )
  79. {
  80. string[] szParseString = szString.Substring( 1, szString.Length - 1 ).Split( ',' );
  81. float x = float.Parse( szParseString[0] );
  82. float y = float.Parse( szParseString[1] );
  83. float z = float.Parse( szParseString[2] );
  84. Vector3 vReturn = new Vector3( x, y, z );
  85. return vReturn;
  86. }
  87. //-------------------------------------------------
  88. public static Vector2 Vector2FromString( string szString )
  89. {
  90. string[] szParseString = szString.Substring( 1, szString.Length - 1 ).Split( ',' );
  91. float x = float.Parse( szParseString[0] );
  92. float y = float.Parse( szParseString[1] );
  93. Vector3 vReturn = new Vector2( x, y );
  94. return vReturn;
  95. }
  96. //-------------------------------------------------
  97. public static float Normalize( float value, float min, float max )
  98. {
  99. float normalizedValue = ( value - min ) / ( max - min );
  100. return normalizedValue;
  101. }
  102. //-------------------------------------------------
  103. public static Vector3 Vector2AsVector3( Vector2 v )
  104. {
  105. return new Vector3( v.x, 0.0f, v.y );
  106. }
  107. //-------------------------------------------------
  108. public static Vector2 Vector3AsVector2( Vector3 v )
  109. {
  110. return new Vector2( v.x, v.z );
  111. }
  112. //-------------------------------------------------
  113. public static float AngleOf( Vector2 v )
  114. {
  115. float fDist = v.magnitude;
  116. if ( v.y >= 0.0f )
  117. {
  118. return Mathf.Acos( v.x / fDist );
  119. }
  120. else
  121. {
  122. return Mathf.Acos( -v.x / fDist ) + Mathf.PI;
  123. }
  124. }
  125. //-------------------------------------------------
  126. public static float YawOf( Vector3 v )
  127. {
  128. float fDist = v.magnitude;
  129. if ( v.z >= 0.0f )
  130. {
  131. return Mathf.Acos( v.x / fDist );
  132. }
  133. else
  134. {
  135. return Mathf.Acos( -v.x / fDist ) + Mathf.PI;
  136. }
  137. }
  138. //-------------------------------------------------
  139. public static void Swap<T>( ref T lhs, ref T rhs )
  140. {
  141. T temp = lhs;
  142. lhs = rhs;
  143. rhs = temp;
  144. }
  145. //-------------------------------------------------
  146. public static void Shuffle<T>( T[] array )
  147. {
  148. for ( int i = array.Length - 1; i > 0; i-- )
  149. {
  150. int r = UnityEngine.Random.Range( 0, i );
  151. Swap( ref array[i], ref array[r] );
  152. }
  153. }
  154. //-------------------------------------------------
  155. public static void Shuffle<T>( List<T> list )
  156. {
  157. for ( int i = list.Count - 1; i > 0; i-- )
  158. {
  159. int r = UnityEngine.Random.Range( 0, i );
  160. T temp = list[i];
  161. list[i] = list[r];
  162. list[r] = temp;
  163. }
  164. }
  165. //-------------------------------------------------
  166. public static int RandomWithLookback( int min, int max, List<int> history, int historyCount )
  167. {
  168. int index = UnityEngine.Random.Range( min, max - history.Count );
  169. for ( int i = 0; i < history.Count; i++ )
  170. {
  171. if ( index >= history[i] )
  172. {
  173. index++;
  174. }
  175. }
  176. history.Add( index );
  177. if ( history.Count > historyCount )
  178. {
  179. history.RemoveRange( 0, history.Count - historyCount );
  180. }
  181. return index;
  182. }
  183. //-------------------------------------------------
  184. public static Transform FindChild( Transform parent, string name )
  185. {
  186. if ( parent.name == name )
  187. return parent;
  188. foreach ( Transform child in parent )
  189. {
  190. var found = FindChild( child, name );
  191. if ( found != null )
  192. return found;
  193. }
  194. return null;
  195. }
  196. //-------------------------------------------------
  197. public static bool IsNullOrEmpty<T>( T[] array )
  198. {
  199. if ( array == null )
  200. return true;
  201. if ( array.Length == 0 )
  202. return true;
  203. return false;
  204. }
  205. //-------------------------------------------------
  206. public static bool IsValidIndex<T>( T[] array, int i )
  207. {
  208. if ( array == null )
  209. return false;
  210. return ( i >= 0 ) && ( i < array.Length );
  211. }
  212. //-------------------------------------------------
  213. public static bool IsValidIndex<T>( List<T> list, int i )
  214. {
  215. if ( list == null || list.Count == 0 )
  216. return false;
  217. return ( i >= 0 ) && ( i < list.Count );
  218. }
  219. //-------------------------------------------------
  220. public static int FindOrAdd<T>( List<T> list, T item )
  221. {
  222. int index = list.IndexOf( item );
  223. if ( index == -1 )
  224. {
  225. list.Add( item );
  226. index = list.Count - 1;
  227. }
  228. return index;
  229. }
  230. //-------------------------------------------------
  231. public static List<T> FindAndRemove<T>( List<T> list, System.Predicate<T> match )
  232. {
  233. List<T> retVal = list.FindAll( match );
  234. list.RemoveAll( match );
  235. return retVal;
  236. }
  237. //-------------------------------------------------
  238. public static T FindOrAddComponent<T>( GameObject gameObject ) where T : Component
  239. {
  240. T component = gameObject.GetComponent<T>();
  241. if ( component )
  242. return component;
  243. return gameObject.AddComponent<T>();
  244. }
  245. //-------------------------------------------------
  246. public static void FastRemove<T>( List<T> list, int index )
  247. {
  248. list[index] = list[list.Count - 1];
  249. list.RemoveAt( list.Count - 1 );
  250. }
  251. //-------------------------------------------------
  252. public static void ReplaceGameObject<T, U>( T replace, U replaceWith )
  253. where T : MonoBehaviour
  254. where U : MonoBehaviour
  255. {
  256. replace.gameObject.SetActive( false );
  257. replaceWith.gameObject.SetActive( true );
  258. }
  259. //-------------------------------------------------
  260. public static void SwitchLayerRecursively( Transform transform, int fromLayer, int toLayer )
  261. {
  262. if ( transform.gameObject.layer == fromLayer )
  263. transform.gameObject.layer = toLayer;
  264. int childCount = transform.childCount;
  265. for ( int i = 0; i < childCount; i++ )
  266. {
  267. SwitchLayerRecursively( transform.GetChild( i ), fromLayer, toLayer );
  268. }
  269. }
  270. //-------------------------------------------------
  271. public static void DrawCross( Vector3 origin, Color crossColor, float size )
  272. {
  273. Vector3 line1Start = origin + ( Vector3.right * size );
  274. Vector3 line1End = origin - ( Vector3.right * size );
  275. Debug.DrawLine( line1Start, line1End, crossColor );
  276. Vector3 line2Start = origin + ( Vector3.up * size );
  277. Vector3 line2End = origin - ( Vector3.up * size );
  278. Debug.DrawLine( line2Start, line2End, crossColor );
  279. Vector3 line3Start = origin + ( Vector3.forward * size );
  280. Vector3 line3End = origin - ( Vector3.forward * size );
  281. Debug.DrawLine( line3Start, line3End, crossColor );
  282. }
  283. //-------------------------------------------------
  284. public static void ResetTransform( Transform t, bool resetScale = true )
  285. {
  286. t.localPosition = Vector3.zero;
  287. t.localRotation = Quaternion.identity;
  288. if ( resetScale )
  289. {
  290. t.localScale = new Vector3( 1f, 1f, 1f );
  291. }
  292. }
  293. //-------------------------------------------------
  294. public static Vector3 ClosestPointOnLine( Vector3 vA, Vector3 vB, Vector3 vPoint )
  295. {
  296. var vVector1 = vPoint - vA;
  297. var vVector2 = ( vB - vA ).normalized;
  298. var d = Vector3.Distance( vA, vB );
  299. var t = Vector3.Dot( vVector2, vVector1 );
  300. if ( t <= 0 )
  301. return vA;
  302. if ( t >= d )
  303. return vB;
  304. var vVector3 = vVector2 * t;
  305. var vClosestPoint = vA + vVector3;
  306. return vClosestPoint;
  307. }
  308. //-------------------------------------------------
  309. public static void AfterTimer( GameObject go, float _time, System.Action callback, bool trigger_if_destroyed_early = false )
  310. {
  311. AfterTimer_Component afterTimer_component = go.AddComponent<AfterTimer_Component>();
  312. afterTimer_component.Init( _time, callback, trigger_if_destroyed_early );
  313. }
  314. //-------------------------------------------------
  315. public static void SendPhysicsMessage( Collider collider, string message, SendMessageOptions sendMessageOptions )
  316. {
  317. Rigidbody rb = collider.attachedRigidbody;
  318. if ( rb && rb.gameObject != collider.gameObject )
  319. {
  320. rb.SendMessage( message, sendMessageOptions );
  321. }
  322. collider.SendMessage( message, sendMessageOptions );
  323. }
  324. //-------------------------------------------------
  325. public static void SendPhysicsMessage( Collider collider, string message, object arg, SendMessageOptions sendMessageOptions )
  326. {
  327. Rigidbody rb = collider.attachedRigidbody;
  328. if ( rb && rb.gameObject != collider.gameObject )
  329. {
  330. rb.SendMessage( message, arg, sendMessageOptions );
  331. }
  332. collider.SendMessage( message, arg, sendMessageOptions );
  333. }
  334. //-------------------------------------------------
  335. public static void IgnoreCollisions( GameObject goA, GameObject goB )
  336. {
  337. Collider[] goA_colliders = goA.GetComponentsInChildren<Collider>();
  338. Collider[] goB_colliders = goB.GetComponentsInChildren<Collider>();
  339. if ( goA_colliders.Length == 0 || goB_colliders.Length == 0 )
  340. {
  341. return;
  342. }
  343. foreach ( Collider cA in goA_colliders )
  344. {
  345. foreach ( Collider cB in goB_colliders )
  346. {
  347. if ( cA.enabled && cB.enabled )
  348. {
  349. Physics.IgnoreCollision( cA, cB, true );
  350. }
  351. }
  352. }
  353. }
  354. //-------------------------------------------------
  355. public static IEnumerator WrapCoroutine( IEnumerator coroutine, System.Action onCoroutineFinished )
  356. {
  357. while ( coroutine.MoveNext() )
  358. {
  359. yield return coroutine.Current;
  360. }
  361. onCoroutineFinished();
  362. }
  363. //-------------------------------------------------
  364. public static Color ColorWithAlpha( this Color color, float alpha )
  365. {
  366. color.a = alpha;
  367. return color;
  368. }
  369. //-------------------------------------------------
  370. // Exits the application if running standalone, or stops playback if running in the editor.
  371. //-------------------------------------------------
  372. public static void Quit()
  373. {
  374. #if UNITY_EDITOR
  375. UnityEditor.EditorApplication.isPlaying = false;
  376. #else
  377. // NOTE: The recommended call for exiting a Unity app is UnityEngine.Application.Quit(), but as
  378. // of 5.1.0f3 this was causing the application to crash. The following works without crashing:
  379. System.Diagnostics.Process.GetCurrentProcess().Kill();
  380. #endif
  381. }
  382. //-------------------------------------------------
  383. // Truncate floats to the specified # of decimal places when you want easier-to-read numbers without clamping to an int
  384. //-------------------------------------------------
  385. public static decimal FloatToDecimal( float value, int decimalPlaces = 2 )
  386. {
  387. return Math.Round( (decimal)value, decimalPlaces );
  388. }
  389. //-------------------------------------------------
  390. public static T Median<T>( this IEnumerable<T> source )
  391. {
  392. if ( source == null )
  393. {
  394. throw new ArgumentException( "Argument cannot be null.", "source" );
  395. }
  396. int count = source.Count();
  397. if ( count == 0 )
  398. {
  399. throw new InvalidOperationException( "Enumerable must contain at least one element." );
  400. }
  401. return source.OrderBy( x => x ).ElementAt( count / 2 );
  402. }
  403. //-------------------------------------------------
  404. public static void ForEach<T>( this IEnumerable<T> source, Action<T> action )
  405. {
  406. if ( source == null )
  407. {
  408. throw new ArgumentException( "Argument cannot be null.", "source" );
  409. }
  410. foreach ( T value in source )
  411. {
  412. action( value );
  413. }
  414. }
  415. //-------------------------------------------------
  416. // In some cases Unity/C# don't correctly interpret the newline control character (\n).
  417. // This function replaces every instance of "\\n" with the actual newline control character.
  418. //-------------------------------------------------
  419. public static string FixupNewlines( string text )
  420. {
  421. bool newLinesRemaining = true;
  422. while ( newLinesRemaining )
  423. {
  424. int CIndex = text.IndexOf( "\\n" );
  425. if ( CIndex == -1 )
  426. {
  427. newLinesRemaining = false;
  428. }
  429. else
  430. {
  431. text = text.Remove( CIndex - 1, 3 );
  432. text = text.Insert( CIndex - 1, "\n" );
  433. }
  434. }
  435. return text;
  436. }
  437. //-------------------------------------------------
  438. #if ( UNITY_5_4 )
  439. public static float PathLength( NavMeshPath path )
  440. #else
  441. public static float PathLength( UnityEngine.AI.NavMeshPath path )
  442. #endif
  443. {
  444. if ( path.corners.Length < 2 )
  445. return 0;
  446. Vector3 previousCorner = path.corners[0];
  447. float lengthSoFar = 0.0f;
  448. int i = 1;
  449. while ( i < path.corners.Length )
  450. {
  451. Vector3 currentCorner = path.corners[i];
  452. lengthSoFar += Vector3.Distance( previousCorner, currentCorner );
  453. previousCorner = currentCorner;
  454. i++;
  455. }
  456. return lengthSoFar;
  457. }
  458. //-------------------------------------------------
  459. public static bool HasCommandLineArgument( string argumentName )
  460. {
  461. string[] args = System.Environment.GetCommandLineArgs();
  462. for ( int i = 0; i < args.Length; i++ )
  463. {
  464. if ( args[i].Equals( argumentName ) )
  465. {
  466. return true;
  467. }
  468. }
  469. return false;
  470. }
  471. //-------------------------------------------------
  472. public static int GetCommandLineArgValue( string argumentName, int nDefaultValue )
  473. {
  474. string[] args = System.Environment.GetCommandLineArgs();
  475. for ( int i = 0; i < args.Length; i++ )
  476. {
  477. if ( args[i].Equals( argumentName ) )
  478. {
  479. if ( i == ( args.Length - 1 ) ) // Last arg, return default
  480. {
  481. return nDefaultValue;
  482. }
  483. return System.Int32.Parse( args[i + 1] );
  484. }
  485. }
  486. return nDefaultValue;
  487. }
  488. //-------------------------------------------------
  489. public static float GetCommandLineArgValue( string argumentName, float flDefaultValue )
  490. {
  491. string[] args = System.Environment.GetCommandLineArgs();
  492. for ( int i = 0; i < args.Length; i++ )
  493. {
  494. if ( args[i].Equals( argumentName ) )
  495. {
  496. if ( i == ( args.Length - 1 ) ) // Last arg, return default
  497. {
  498. return flDefaultValue;
  499. }
  500. return (float)Double.Parse( args[i + 1] );
  501. }
  502. }
  503. return flDefaultValue;
  504. }
  505. //-------------------------------------------------
  506. public static void SetActive( GameObject gameObject, bool active )
  507. {
  508. if ( gameObject != null )
  509. {
  510. gameObject.SetActive( active );
  511. }
  512. }
  513. //-------------------------------------------------
  514. // The version of Path.Combine() included with Unity can only combine two paths.
  515. // This version mimics the modern .NET version, which allows for any number of
  516. // paths to be combined.
  517. //-------------------------------------------------
  518. public static string CombinePaths( params string[] paths )
  519. {
  520. if ( paths.Length == 0 )
  521. {
  522. return "";
  523. }
  524. else
  525. {
  526. string combinedPath = paths[0];
  527. for ( int i = 1; i < paths.Length; i++ )
  528. {
  529. combinedPath = Path.Combine( combinedPath, paths[i] );
  530. }
  531. return combinedPath;
  532. }
  533. }
  534. }
  535. //-------------------------------------------------------------------------
  536. //Component used by the static AfterTimer function
  537. //-------------------------------------------------------------------------
  538. [System.Serializable]
  539. public class AfterTimer_Component : MonoBehaviour
  540. {
  541. private System.Action callback;
  542. private float triggerTime;
  543. private bool timerActive = false;
  544. private bool triggerOnEarlyDestroy = false;
  545. //-------------------------------------------------
  546. public void Init( float _time, System.Action _callback, bool earlydestroy )
  547. {
  548. triggerTime = _time;
  549. callback = _callback;
  550. triggerOnEarlyDestroy = earlydestroy;
  551. timerActive = true;
  552. StartCoroutine( Wait() );
  553. }
  554. //-------------------------------------------------
  555. private IEnumerator Wait()
  556. {
  557. yield return new WaitForSeconds( triggerTime );
  558. timerActive = false;
  559. callback.Invoke();
  560. Destroy( this );
  561. }
  562. //-------------------------------------------------
  563. void OnDestroy()
  564. {
  565. if ( timerActive )
  566. {
  567. //If the component or its GameObject get destroyed before the timer is complete, clean up
  568. StopCoroutine( Wait() );
  569. timerActive = false;
  570. if ( triggerOnEarlyDestroy )
  571. {
  572. callback.Invoke();
  573. }
  574. }
  575. }
  576. }
  577. }