|
|
- // Shared Methods|Utilities|90060
- namespace VRTK
- {
- using UnityEngine;
- using UnityEngine.SceneManagement;
- #if UNITY_EDITOR
- using UnityEditor;
- #endif
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Reflection;
- #if UNITY_2017_2_OR_NEWER
- using UnityEngine.XR;
- #else
- using XRSettings = UnityEngine.VR.VRSettings;
- using XRStats = UnityEngine.VR.VRStats;
- #endif
-
- /// <summary>
- /// The Shared Methods script is a collection of reusable static methods that are used across a range of different scripts.
- /// </summary>
- public static class VRTK_SharedMethods
- {
- /// <summary>
- /// The GetBounds methods returns the bounds of the transform including all children in world space.
- /// </summary>
- /// <param name="transform"></param>
- /// <param name="excludeRotation">Resets the rotation of the transform temporarily to 0 to eliminate skewed bounds.</param>
- /// <param name="excludeTransform">Does not consider the stated object when calculating the bounds.</param>
- /// <returns>The bounds of the transform.</returns>
- public static Bounds GetBounds(Transform transform, Transform excludeRotation = null, Transform excludeTransform = null)
- {
- Quaternion oldRotation = Quaternion.identity;
- if (excludeRotation != null)
- {
- oldRotation = excludeRotation.rotation;
- excludeRotation.rotation = Quaternion.identity;
- }
-
- bool boundsInitialized = false;
- Bounds bounds = new Bounds(transform.position, Vector3.zero);
-
- Renderer[] renderers = transform.GetComponentsInChildren<Renderer>();
- for (int i = 0; i < renderers.Length; i++)
- {
- Renderer renderer = renderers[i];
- if (excludeTransform != null && renderer.transform.IsChildOf(excludeTransform))
- {
- continue;
- }
-
- // do late initialization in case initial transform does not contain any renderers
- if (!boundsInitialized)
- {
- bounds = new Bounds(renderer.transform.position, Vector3.zero);
- boundsInitialized = true;
- }
- bounds.Encapsulate(renderer.bounds);
- }
-
- if (bounds.size.magnitude == 0)
- {
- // do second pass as there were no renderers, this time with colliders
- BoxCollider[] colliders = transform.GetComponentsInChildren<BoxCollider>();
- for (int i = 0; i < colliders.Length; i++)
- {
- BoxCollider collider = colliders[i];
- if (excludeTransform != null && collider.transform.IsChildOf(excludeTransform))
- {
- continue;
- }
-
- // do late initialization in case initial transform does not contain any colliders
- if (!boundsInitialized)
- {
- bounds = new Bounds(collider.transform.position, Vector3.zero);
- boundsInitialized = true;
- }
- bounds.Encapsulate(collider.bounds);
- }
- }
-
- if (excludeRotation != null)
- {
- excludeRotation.rotation = oldRotation;
- }
-
- return bounds;
- }
-
- /// <summary>
- /// The IsLowest method checks to see if the given value is the lowest number in the given array of values.
- /// </summary>
- /// <param name="value">The value to check to see if it is lowest.</param>
- /// <param name="others">The array of values to check against.</param>
- /// <returns>Returns true if the value is lower than all numbers in the given array, returns false if it is not the lowest.</returns>
- public static bool IsLowest(float value, float[] others)
- {
- for (int i = 0; i < others.Length; i++)
- {
- if (others[i] <= value)
- {
- return false;
- }
- }
- return true;
- }
-
- /// <summary>
- /// The AddCameraFade method finds the headset camera and adds a headset fade script to it.
- /// </summary>
- /// <returns>The transform of the headset camera.</returns>
- public static Transform AddCameraFade()
- {
- Transform camera = VRTK_DeviceFinder.HeadsetCamera();
- VRTK_SDK_Bridge.AddHeadsetFade(camera);
- return camera;
- }
-
- /// <summary>
- /// The CreateColliders method attempts to add box colliders to all child objects in the given object that have a renderer but no collider.
- /// </summary>
- /// <param name="obj">The game object to attempt to add the colliders to.</param>
- public static void CreateColliders(GameObject obj)
- {
- Renderer[] renderers = obj.GetComponentsInChildren<Renderer>();
- for (int i = 0; i < renderers.Length; i++)
- {
- Renderer renderer = renderers[i];
- if (renderer.gameObject.GetComponent<Collider>() == null)
- {
- renderer.gameObject.AddComponent<BoxCollider>();
- }
- }
- }
-
- /// <summary>
- /// The ColliderExclude method reduces the colliders in the setA array by those matched in the setB array.
- /// </summary>
- /// <param name="setA">The array that contains all of the relevant colliders.</param>
- /// <param name="setB">The array that contains the colliders to remove from setA.</param>
- /// <returns>A Collider array that is a subset of setA that doesn't contain the colliders from setB.</returns>
- public static Collider[] ColliderExclude(Collider[] setA, Collider[] setB)
- {
- return setA.Except(setB).ToArray<Collider>();
- }
-
- /// <summary>
- /// The GetCollidersInGameObjects method iterates through a GameObject array and returns all of the unique found colliders for all GameObejcts.
- /// </summary>
- /// <param name="gameObjects">An array of GameObjects to get the colliders for.</param>
- /// <param name="searchChildren">If this is `true` then the given GameObjects will also have their child GameObjects searched for colliders.</param>
- /// <param name="includeInactive">If this is `true` then the inactive GameObjects in the array will also be checked for Colliders. Only relevant if `searchChildren` is `true`.</param>
- /// <returns>An array of Colliders that are found in the given GameObject array.</returns>
- public static Collider[] GetCollidersInGameObjects(GameObject[] gameObjects, bool searchChildren, bool includeInactive)
- {
- HashSet<Collider> foundColliders = new HashSet<Collider>();
- for (int i = 0; i < gameObjects.Length; i++)
- {
- Collider[] gameObjectColliders = (searchChildren ? gameObjects[i].GetComponentsInChildren<Collider>(includeInactive) : gameObjects[i].GetComponents<Collider>());
- for (int j = 0; j < gameObjectColliders.Length; j++)
- {
- foundColliders.Add(gameObjectColliders[j]);
- }
- }
- return foundColliders.ToArray();
- }
-
- /// <summary>
- /// The CloneComponent method takes a source component and copies it to the given destination game object.
- /// </summary>
- /// <param name="source">The component to copy.</param>
- /// <param name="destination">The game object to copy the component to.</param>
- /// <param name="copyProperties">Determines whether the properties of the component as well as the fields should be copied.</param>
- /// <returns>The component that has been cloned onto the given game object.</returns>
- public static Component CloneComponent(Component source, GameObject destination, bool copyProperties = false)
- {
- Component tmpComponent = destination.gameObject.AddComponent(source.GetType());
- if (copyProperties)
- {
- PropertyInfo[] foundProperties = source.GetType().GetProperties();
- for (int i = 0; i < foundProperties.Length; i++)
- {
- PropertyInfo foundProperty = foundProperties[i];
- if (foundProperty.CanWrite)
- {
- foundProperty.SetValue(tmpComponent, foundProperty.GetValue(source, null), null);
- }
- }
- }
-
- FieldInfo[] foundFields = source.GetType().GetFields();
- for (int i = 0; i < foundFields.Length; i++)
- {
- FieldInfo foundField = foundFields[i];
- foundField.SetValue(tmpComponent, foundField.GetValue(source));
- }
- return tmpComponent;
- }
-
- /// <summary>
- /// The ColorDarken method takes a given colour and darkens it by the given percentage.
- /// </summary>
- /// <param name="color">The source colour to apply the darken to.</param>
- /// <param name="percent">The percent to darken the colour by.</param>
- /// <returns>The new colour with the darken applied.</returns>
- public static Color ColorDarken(Color color, float percent)
- {
- return new Color(NumberPercent(color.r, percent), NumberPercent(color.g, percent), NumberPercent(color.b, percent), color.a);
- }
-
- /// <summary>
- /// The RoundFloat method is used to round a given float to the given decimal places.
- /// </summary>
- /// <param name="givenFloat">The float to round.</param>
- /// <param name="decimalPlaces">The number of decimal places to round to.</param>
- /// <param name="rawFidelity">If this is true then the decimal places must be given in the decimal multiplier, e.g. 10 for 1dp, 100 for 2dp, etc.</param>
- /// <returns>The rounded float.</returns>
- public static float RoundFloat(float givenFloat, int decimalPlaces, bool rawFidelity = false)
- {
- float roundBy = (rawFidelity ? decimalPlaces : Mathf.Pow(10.0f, decimalPlaces));
- return Mathf.Round(givenFloat * roundBy) / roundBy;
- }
-
- /// <summary>
- /// The IsEditTime method determines if the state of Unity is in the Unity Editor and the scene is not in play mode.
- /// </summary>
- /// <returns>Returns true if Unity is in the Unity Editor and not in play mode.</returns>
- public static bool IsEditTime()
- {
- #if UNITY_EDITOR
- return !EditorApplication.isPlayingOrWillChangePlaymode;
- #else
- return false;
- #endif
- }
-
- /// <summary>
- /// The Mod method is used to find the remainder of the sum a/b.
- /// </summary>
- /// <param name="a">The dividend value.</param>
- /// <param name="b">The divisor value.</param>
- /// <returns>The remainder value.</returns>
- public static float Mod(float a, float b)
- {
- return a - b * Mathf.Floor(a / b);
- }
-
- /// <summary>
- /// Finds the first GameObject with a given name and an ancestor that has a specific component.
- /// </summary>
- /// <remarks>
- /// This method returns active as well as inactive GameObjects in all scenes. It doesn't return assets.
- /// For performance reasons it is recommended to not use this function every frame. Cache the result in a member variable at startup instead.
- /// </remarks>
- /// <typeparam name="T">The component type that needs to be on an ancestor of the wanted GameObject. Must be a subclass of `Component`.</typeparam>
- /// <param name="gameObjectName">The name of the wanted GameObject. If it contains a '/' character, this method traverses the hierarchy like a path name, beginning on the game object that has a component of type `T`.</param>
- /// <param name="searchAllScenes">If this is true, all loaded scenes will be searched. If this is false, only the active scene will be searched.</param>
- /// <returns>The GameObject with name `gameObjectName` and an ancestor that has a `T`. If no such GameObject is found then `null` is returned.</returns>
- public static GameObject FindEvenInactiveGameObject<T>(string gameObjectName = null, bool searchAllScenes = false) where T : Component
- {
- if (string.IsNullOrEmpty(gameObjectName))
- {
- T foundComponent = FindEvenInactiveComponentsInValidScenes<T>(searchAllScenes, true).FirstOrDefault();
- return foundComponent == null ? null : foundComponent.gameObject;
- }
-
- return FindEvenInactiveComponentsInValidScenes<T>(searchAllScenes)
- .Select(component =>
- {
- Transform transform = component.gameObject.transform.Find(gameObjectName);
- return transform == null ? null : transform.gameObject;
- })
- .FirstOrDefault(gameObject => gameObject != null);
- }
-
- /// <summary>
- /// Finds all components of a given type.
- /// </summary>
- /// <remarks>
- /// This method returns components from active as well as inactive GameObjects in all scenes. It doesn't return assets.
- /// For performance reasons it is recommended to not use this function every frame. Cache the result in a member variable at startup instead.
- /// </remarks>
- /// <typeparam name="T">The component type to search for. Must be a subclass of `Component`.</typeparam>
- /// <param name="searchAllScenes">If this is true, all loaded scenes will be searched. If this is false, only the active scene will be searched.</param>
- /// <returns>All the found components. If no component is found an empty array is returned.</returns>
- public static T[] FindEvenInactiveComponents<T>(bool searchAllScenes = false) where T : Component
- {
- IEnumerable<T> results = FindEvenInactiveComponentsInValidScenes<T>(searchAllScenes);
- return results.ToArray();
- }
-
- /// <summary>
- /// Finds the first component of a given type.
- /// </summary>
- /// <remarks>
- /// This method returns components from active as well as inactive GameObjects in all scenes. It doesn't return assets.
- /// For performance reasons it is recommended to not use this function every frame. Cache the result in a member variable at startup instead.
- /// </remarks>
- /// <typeparam name="T">The component type to search for. Must be a subclass of `Component`.</typeparam>
- /// <param name="searchAllScenes">If this is true, all loaded scenes will be searched. If this is false, only the active scene will be searched.</param>
- /// <returns>The found component. If no component is found `null` is returned.</returns>
- public static T FindEvenInactiveComponent<T>(bool searchAllScenes = false) where T : Component
- {
- return FindEvenInactiveComponentsInValidScenes<T>(searchAllScenes, true).FirstOrDefault();
- }
-
- /// <summary>
- /// The GenerateVRTKObjectName method is used to create a standard name string for any VRTK generated object.
- /// </summary>
- /// <param name="autoGen">An additiona [AUTOGEN] prefix will be added if this is true.</param>
- /// <param name="replacements">A collection of parameters to add to the generated name.</param>
- /// <returns>The generated name string.</returns>
- public static string GenerateVRTKObjectName(bool autoGen, params object[] replacements)
- {
- string toFormat = "[VRTK]";
- if (autoGen)
- {
- toFormat += "[AUTOGEN]";
- }
- for (int i = 0; i < replacements.Length; i++)
- {
- toFormat += "[{" + i + "}]";
- }
- return string.Format(toFormat, replacements);
- }
-
- /// <summary>
- /// The GetGPUTimeLastFrame retrieves the time spent by the GPU last frame, in seconds, as reported by the VR SDK.
- /// </summary>
- /// <returns>The total GPU time utilized last frame as measured by the VR subsystem.</returns>
- public static float GetGPUTimeLastFrame()
- {
- #if UNITY_5_6_OR_NEWER
- float gpuTimeLastFrame;
- return (XRStats.TryGetGPUTimeLastFrame(out gpuTimeLastFrame) ? gpuTimeLastFrame : 0f);
- #else
- return XRStats.gpuTimeLastFrame;
- #endif
- }
-
- /// <summary>
- /// The Vector2ShallowCompare method compares two given Vector2 objects based on the given fidelity, which is the equivalent of comparing rounded Vector2 elements to determine if the Vector2 elements are equal.
- /// </summary>
- /// <param name="vectorA">The Vector2 to compare against.</param>
- /// <param name="vectorB">The Vector2 to compare with</param>
- /// <param name="compareFidelity">The number of decimal places to use when doing the comparison on the float elements within the Vector2.</param>
- /// <returns>Returns `true` if the given Vector2 objects match based on the given fidelity.</returns>
- public static bool Vector2ShallowCompare(Vector2 vectorA, Vector2 vectorB, int compareFidelity)
- {
- Vector2 distanceVector = vectorA - vectorB;
- return (Math.Round(Mathf.Abs(distanceVector.x), compareFidelity, MidpointRounding.AwayFromZero) < float.Epsilon &&
- Math.Round(Mathf.Abs(distanceVector.y), compareFidelity, MidpointRounding.AwayFromZero) < float.Epsilon);
- }
-
- /// <summary>
- /// The Vector3ShallowCompare method compares two given Vector3 objects based on the given threshold, which is the equavelent of checking the distance between two Vector3 objects are above the threshold distance.
- /// </summary>
- /// <param name="vectorA">The Vector3 to compare against.</param>
- /// <param name="vectorB">The Vector3 to compare with</param>
- /// <param name="threshold">The distance in which the two Vector3 objects can be within to be considered true</param>
- /// <returns>Returns `true` if the given Vector3 objects are within the given threshold distance.</returns>
- public static bool Vector3ShallowCompare(Vector3 vectorA, Vector3 vectorB, float threshold)
- {
- return (Vector3.Distance(vectorA, vectorB) < threshold);
- }
-
- /// <summary>
- /// The NumberPercent method is used to determine the percentage of a given value.
- /// </summary>
- /// <param name="value">The value to determine the percentage from</param>
- /// <param name="percent">The percentage to find within the given value.</param>
- /// <returns>A float containing the percentage value based on the given input.</returns>
- public static float NumberPercent(float value, float percent)
- {
- percent = Mathf.Clamp(percent, 0f, 100f);
- return (percent == 0f ? value : (value - (percent / 100f)));
- }
-
- /// <summary>
- /// The SetGlobalScale method is used to set a transform scale based on a global scale instead of a local scale.
- /// </summary>
- /// <param name="transform">The reference to the transform to scale.</param>
- /// <param name="globalScale">A Vector3 of a global scale to apply to the given transform.</param>
- public static void SetGlobalScale(this Transform transform, Vector3 globalScale)
- {
- transform.localScale = Vector3.one;
- transform.localScale = new Vector3(globalScale.x / transform.lossyScale.x, globalScale.y / transform.lossyScale.y, globalScale.z / transform.lossyScale.z);
- }
-
- /// <summary>
- /// The VectorHeading method calculates the current heading of the target position in relation to the origin position.
- /// </summary>
- /// <param name="originPosition">The point to use as the originating position for the heading calculation.</param>
- /// <param name="targetPosition">The point to use as the target position for the heading calculation.</param>
- /// <returns>A Vector3 containing the heading changes of the target position in relation to the origin position.</returns>
- public static Vector3 VectorHeading(Vector3 originPosition, Vector3 targetPosition)
- {
- return targetPosition - originPosition;
- }
-
- /// <summary>
- /// The VectorDirection method calculates the direction the target position is in relation to the origin position.
- /// </summary>
- /// <param name="originPosition">The point to use as the originating position for the direction calculation.</param>
- /// <param name="targetPosition">The point to use as the target position for the direction calculation.</param>
- /// <returns>A Vector3 containing the direction of the target position in relation to the origin position.</returns>
- public static Vector3 VectorDirection(Vector3 originPosition, Vector3 targetPosition)
- {
- Vector3 heading = VectorHeading(originPosition, targetPosition);
- return heading * DividerToMultiplier(heading.magnitude);
- }
-
- /// <summary>
- /// The DividerToMultiplier method takes a number to be used in a division and converts it to be used for multiplication. (e.g. 2 / 2 becomes 2 * 0.5)
- /// </summary>
- /// <param name="value">The number to convert into the multplier value.</param>
- /// <returns>The calculated number that can replace the divider number in a multiplication sum.</returns>
- public static float DividerToMultiplier(float value)
- {
- return (value != 0f ? 1f / value : 1f);
- }
-
- /// <summary>
- /// The NormalizeValue method takes a given value between a specified range and returns the normalized value between 0f and 1f.
- /// </summary>
- /// <param name="value">The actual value to normalize.</param>
- /// <param name="minValue">The minimum value the actual value can be.</param>
- /// <param name="maxValue">The maximum value the actual value can be.</param>
- /// <param name="threshold">The threshold to force to the minimum or maximum value if the normalized value is within the threhold limits.</param>
- /// <returns></returns>
- public static float NormalizeValue(float value, float minValue, float maxValue, float threshold = 0f)
- {
- float normalizedMax = maxValue - minValue;
- float normalizedValue = normalizedMax - (maxValue - value);
- float result = normalizedValue * DividerToMultiplier(normalizedMax); ;
- result = (result < threshold ? 0f : result);
- result = (result > 1f - threshold ? 1f : result);
- return Mathf.Clamp(result, 0f, 1f);
- }
-
- /// <summary>
- /// The AxisDirection method returns the relevant direction Vector3 based on the axis index in relation to x,y,z.
- /// </summary>
- /// <param name="axisIndex">The axis index of the axis. `0 = x` `1 = y` `2 = z`</param>
- /// <param name="givenTransform">An optional Transform to get the Axis Direction for. If this is `null` then the World directions will be used.</param>
- /// <returns>The direction Vector3 based on the given axis index.</returns>
- public static Vector3 AxisDirection(int axisIndex, Transform givenTransform = null)
- {
- Vector3[] worldDirections = (givenTransform != null ? new Vector3[] { givenTransform.right, givenTransform.up, givenTransform.forward } : new Vector3[] { Vector3.right, Vector3.up, Vector3.forward });
- return worldDirections[(int)Mathf.Clamp(axisIndex, 0f, worldDirections.Length)];
- }
-
- /// <summary>
- /// The AddListValue method adds the given value to the given list. If `preventDuplicates` is `true` then the given value will only be added if it doesn't already exist in the given list.
- /// </summary>
- /// <typeparam name="TValue">The datatype for the list value.</typeparam>
- /// <param name="list">The list to retrieve the value from.</param>
- /// <param name="value">The value to attempt to add to the list.</param>
- /// <param name="preventDuplicates">If this is `false` then the value provided will always be appended to the list. If this is `true` the value provided will only be added to the list if it doesn't already exist.</param>
- /// <returns>Returns `true` if the given value was successfully added to the list. Returns `false` if the given value already existed in the list and `preventDuplicates` is `true`.</returns>
- public static bool AddListValue<TValue>(List<TValue> list, TValue value, bool preventDuplicates = false)
- {
- if (list != null && (!preventDuplicates || !list.Contains(value)))
- {
- list.Add(value);
- return true;
- }
- return false;
- }
-
- /// <summary>
- /// The GetDictionaryValue method attempts to retrieve a value from a given dictionary for the given key. It removes the need for a double dictionary lookup to ensure the key is valid and has the option of also setting the missing key value to ensure the dictionary entry is valid.
- /// </summary>
- /// <typeparam name="TKey">The datatype for the dictionary key.</typeparam>
- /// <typeparam name="TValue">The datatype for the dictionary value.</typeparam>
- /// <param name="dictionary">The dictionary to retrieve the value from.</param>
- /// <param name="key">The key to retrieve the value for.</param>
- /// <param name="defaultValue">The value to utilise when either setting the missing key (if `setMissingKey` is `true`) or the default value to return when no key is found (if `setMissingKey` is `false`).</param>
- /// <param name="setMissingKey">If this is `true` and the given key is not present, then the dictionary value for the given key will be set to the `defaultValue` parameter. If this is `false` and the given key is not present then the `defaultValue` parameter will be returned as the value.</param>
- /// <returns>The found value for the given key in the given dictionary, or the default value if no key is found.</returns>
- public static TValue GetDictionaryValue<TKey, TValue>(Dictionary<TKey, TValue> dictionary, TKey key, TValue defaultValue = default(TValue), bool setMissingKey = false)
- {
- bool keyExists;
- return GetDictionaryValue(dictionary, key, out keyExists, defaultValue, setMissingKey);
- }
-
- /// <summary>
- /// The GetDictionaryValue method attempts to retrieve a value from a given dictionary for the given key. It removes the need for a double dictionary lookup to ensure the key is valid and has the option of also setting the missing key value to ensure the dictionary entry is valid.
- /// </summary>
- /// <typeparam name="TKey">The datatype for the dictionary key.</typeparam>
- /// <typeparam name="TValue">The datatype for the dictionary value.</typeparam>
- /// <param name="dictionary">The dictionary to retrieve the value from.</param>
- /// <param name="key">The key to retrieve the value for.</param>
- /// <param name="keyExists">Sets the given parameter to `true` if the key exists in the given dictionary or sets to `false` if the key didn't existing in the given dictionary.</param>
- /// <param name="defaultValue">The value to utilise when either setting the missing key (if `setMissingKey` is `true`) or the default value to return when no key is found (if `setMissingKey` is `false`).</param>
- /// <param name="setMissingKey">If this is `true` and the given key is not present, then the dictionary value for the given key will be set to the `defaultValue` parameter. If this is `false` and the given key is not present then the `defaultValue` parameter will be returned as the value.</param>
- /// <returns>The found value for the given key in the given dictionary, or the default value if no key is found.</returns>
- public static TValue GetDictionaryValue<TKey, TValue>(Dictionary<TKey, TValue> dictionary, TKey key, out bool keyExists, TValue defaultValue = default(TValue), bool setMissingKey = false)
- {
- keyExists = false;
- if (dictionary == null)
- {
- return defaultValue;
- }
-
- TValue outputValue;
- if (dictionary.TryGetValue(key, out outputValue))
- {
- keyExists = true;
- }
- else
- {
- if (setMissingKey)
- {
- dictionary.Add(key, defaultValue);
- }
- outputValue = defaultValue;
- }
- return outputValue;
- }
-
- /// <summary>
- /// The AddDictionaryValue method attempts to add a value for the given key in the given dictionary if the key does not already exist. If `overwriteExisting` is `true` then it always set the value even if they key exists.
- /// </summary>
- /// <typeparam name="TKey">The datatype for the dictionary key.</typeparam>
- /// <typeparam name="TValue">The datatype for the dictionary value.</typeparam>
- /// <param name="dictionary">The dictionary to set the value for.</param>
- /// <param name="key">The key to set the value for.</param>
- /// <param name="value">The value to set at the given key in the given dictionary.</param>
- /// <param name="overwriteExisting">If this is `true` then the value for the given key will always be set to the provided value. If this is `false` then the value for the given key will only be set if the given key is not found in the given dictionary.</param>
- /// <returns>Returns `true` if the given value was successfully added to the dictionary at the given key. Returns `false` if the given key already existed in the dictionary and `overwriteExisting` is `false`.</returns>
- public static bool AddDictionaryValue<TKey, TValue>(Dictionary<TKey, TValue> dictionary, TKey key, TValue value, bool overwriteExisting = false)
- {
- if (dictionary != null)
- {
- if (overwriteExisting)
- {
- dictionary[key] = value;
- return true;
- }
- else
- {
- bool keyExists;
- GetDictionaryValue(dictionary, key, out keyExists, value, true);
- return !keyExists;
- }
- }
- return false;
- }
-
- /// <summary>
- /// The GetTypeUnknownAssembly method is used to find a Type without knowing the exact assembly it is in.
- /// </summary>
- /// <param name="typeName">The name of the type to get.</param>
- /// <returns>The Type, or null if none is found.</returns>
- public static Type GetTypeUnknownAssembly(string typeName)
- {
- Type type = Type.GetType(typeName);
- if (type != null)
- {
- return type;
- }
- #if !UNITY_WSA
- Assembly[] foundAssemblies = AppDomain.CurrentDomain.GetAssemblies();
- for (int i = 0; i < foundAssemblies.Length; i++)
- {
- type = foundAssemblies[i].GetType(typeName);
- if (type != null)
- {
- return type;
- }
- }
- #endif
- return null;
- }
-
- /// <summary>
- /// The GetEyeTextureResolutionScale method returns the render scale for the resolution.
- /// </summary>
- /// <returns>Returns a float with the render scale for the resolution.</returns>
- public static float GetEyeTextureResolutionScale()
- {
- #if UNITY_2017_2_OR_NEWER
- return XRSettings.eyeTextureResolutionScale;
- #else
- return XRSettings.renderScale;
- #endif
- }
-
- /// <summary>
- /// The SetEyeTextureResolutionScale method sets the render scale for the resolution.
- /// </summary>
- /// <param name="value">The value to set the render scale to.</param>
- public static void SetEyeTextureResolutionScale(float value)
- {
- #if UNITY_2017_2_OR_NEWER
- XRSettings.eyeTextureResolutionScale = value;
- #else
- XRSettings.renderScale = value;
- #endif
- }
-
- /// <summary>
- /// The IsTypeSubclassOf checks if a given Type is a subclass of another given Type.
- /// </summary>
- /// <param name="givenType">The Type to check.</param>
- /// <param name="givenBaseType">The base Type to check.</param>
- /// <returns>Returns `true` if the given type is a subclass of the given base type.</returns>
- public static bool IsTypeSubclassOf(Type givenType, Type givenBaseType)
- {
- #if UNITY_WSA && !UNITY_EDITOR
- return (givenType.GetTypeInfo().IsSubclassOf(givenBaseType));
- #else
- return (givenType.IsSubclassOf(givenBaseType));
- #endif
- }
-
- /// <summary>
- /// The GetTypeCustomAttributes method gets the custom attributes of a given type.
- /// </summary>
- /// <param name="givenType">The type to get the custom attributes for.</param>
- /// <param name="attributeType">The attribute type.</param>
- /// <param name="inherit">Whether to inherit attributes.</param>
- /// <returns>Returns an object array of custom attributes.</returns>
- public static object[] GetTypeCustomAttributes(Type givenType, Type attributeType, bool inherit)
- {
- #if UNITY_WSA && !UNITY_EDITOR
- return ((object[])givenType.GetTypeInfo().GetCustomAttributes(attributeType, inherit));
- #else
- return (givenType.GetCustomAttributes(attributeType, inherit));
- #endif
- }
-
- /// <summary>
- /// The GetBaseType method returns the base Type for the given Type.
- /// </summary>
- /// <param name="givenType">The type to return the base Type for.</param>
- /// <returns>Returns the base Type.</returns>
- public static Type GetBaseType(Type givenType)
- {
- #if UNITY_WSA && !UNITY_EDITOR
- return (givenType.GetTypeInfo().BaseType);
- #else
- return (givenType.BaseType);
- #endif
- }
-
- /// <summary>
- /// The IsTypeAssignableFrom method determines if the given Type is assignable from the source Type.
- /// </summary>
- /// <param name="givenType">The Type to check on.</param>
- /// <param name="sourceType">The Type to check if the given Type is assignable from.</param>
- /// <returns>Returns `true` if the given Type is assignable from the source Type.</returns>
- public static bool IsTypeAssignableFrom(Type givenType, Type sourceType)
- {
- #if UNITY_WSA && !UNITY_EDITOR
- return (givenType.GetTypeInfo().IsAssignableFrom(sourceType.GetTypeInfo()));
- #else
- return (givenType.IsAssignableFrom(sourceType));
- #endif
- }
-
- /// <summary>
- /// The GetNestedType method returns the nested Type of the given Type.
- /// </summary>
- /// <param name="givenType">The Type to check on.</param>
- /// <param name="name">The name of the nested Type.</param>
- /// <returns>Returns the nested Type.</returns>
- public static Type GetNestedType(Type givenType, string name)
- {
- #if UNITY_WSA && !UNITY_EDITOR
- return (givenType.GetTypeInfo().GetDeclaredNestedType(name).GetType());
- #else
- return (givenType.GetNestedType(name));
- #endif
- }
-
- /// <summary>
- /// The GetPropertyFirstName method returns the string name of the first property on a given Type.
- /// </summary>
- /// <typeparam name="T">The type to check the first property on.</typeparam>
- /// <returns>Returns a string representation of the first property name for the given Type.</returns>
- public static string GetPropertyFirstName<T>()
- {
- #if UNITY_WSA && !UNITY_EDITOR
- return (typeof(T).GetTypeInfo().DeclaredProperties.First().Name);
- #else
- return (typeof(T).GetProperties()[0].Name);
- #endif
- }
-
- /// <summary>
- /// The GetCommandLineArguements method returns the command line arguements for the environment.
- /// </summary>
- /// <returns>Returns an array of command line arguements as strings.</returns>
- public static string[] GetCommandLineArguements()
- {
- #if UNITY_WSA && !UNITY_EDITOR
- return new string[0];
- #else
-
- return Environment.GetCommandLineArgs();
- #endif
- }
-
- /// <summary>
- /// The GetTypesOfType method returns an array of Types for the given Type.
- /// </summary>
- /// <param name="givenType">The Type to check on.</param>
- /// <returns>An array of Types found.</returns>
- public static Type[] GetTypesOfType(Type givenType)
- {
- #if UNITY_WSA && !UNITY_EDITOR
- return givenType.GetTypeInfo().Assembly.GetTypes();
- #else
-
- return givenType.Assembly.GetTypes();
- #endif
- }
-
- /// <summary>
- /// The GetExportedTypesOfType method returns an array of Exported Types for the given Type.
- /// </summary>
- /// <param name="givenType">The Type to check on.</param>
- /// <returns>An array of Exported Types found.</returns>
- public static Type[] GetExportedTypesOfType(Type givenType)
- {
- #if UNITY_WSA && !UNITY_EDITOR
- return givenType.GetTypeInfo().Assembly.GetExportedTypes();
- #else
-
- return givenType.Assembly.GetExportedTypes();
- #endif
- }
-
- /// <summary>
- /// The IsTypeAbstract method determines if a given Type is abstract.
- /// </summary>
- /// <param name="givenType">The Type to check on.</param>
- /// <returns>Returns `true` if the given type is abstract.</returns>
- public static bool IsTypeAbstract(Type givenType)
- {
- #if UNITY_WSA && !UNITY_EDITOR
- return givenType.GetTypeInfo().IsAbstract;
- #else
-
- return givenType.IsAbstract;
- #endif
- }
-
- #if UNITY_EDITOR
- public static BuildTargetGroup[] GetValidBuildTargetGroups()
- {
- return Enum.GetValues(typeof(BuildTargetGroup)).Cast<BuildTargetGroup>().Where(group =>
- {
- if (group == BuildTargetGroup.Unknown)
- {
- return false;
- }
-
- string targetGroupName = Enum.GetName(typeof(BuildTargetGroup), group);
- FieldInfo targetGroupFieldInfo = typeof(BuildTargetGroup).GetField(targetGroupName, BindingFlags.Public | BindingFlags.Static);
- bool validReturn = (targetGroupFieldInfo != null && targetGroupFieldInfo.GetCustomAttributes(typeof(ObsoleteAttribute), false).Length == 0);
- #if UNITY_WSA
- if (targetGroupName == "Metro" || targetGroupName == "WSA")
- {
- validReturn = (targetGroupFieldInfo != null);
- }
- #endif
- return validReturn;
- }).ToArray();
- }
- #endif
-
- /// <summary>
- /// The FindEvenInactiveComponentsInLoadedScenes method searches active and inactive game objects in all
- /// loaded scenes for components matching the type supplied.
- /// </summary>
- /// <param name="searchAllScenes">If true, will search all loaded scenes, otherwise just the active scene.</param>
- /// <param name="stopOnMatch">If true, will stop searching objects as soon as a match is found.</param>
- /// <returns></returns>
- private static IEnumerable<T> FindEvenInactiveComponentsInValidScenes<T>(bool searchAllScenes, bool stopOnMatch = false) where T : Component
- {
- IEnumerable<T> results;
- if (searchAllScenes)
- {
- List<T> allSceneResults = new List<T>();
- for (int sceneIndex = 0; sceneIndex < SceneManager.sceneCount; sceneIndex++)
- {
- allSceneResults.AddRange(FindEvenInactiveComponentsInScene<T>(SceneManager.GetSceneAt(sceneIndex), stopOnMatch));
- }
- results = allSceneResults;
- }
- else
- {
- results = FindEvenInactiveComponentsInScene<T>(SceneManager.GetActiveScene(), stopOnMatch);
- }
-
- return results;
- }
-
- /// <summary>
- /// The FIndEvenInactiveComponentsInScene method searches the specified scene for components matching the type supplied.
- /// </summary>
- /// <param name="scene">The scene to search. This scene must be valid, either loaded or loading.</param>
- /// <param name="stopOnMatch">If true, will stop searching objects as soon as a match is found.</param>
- /// <returns></returns>
- private static IEnumerable<T> FindEvenInactiveComponentsInScene<T>(Scene scene, bool stopOnMatch = false)
- {
- List<T> results = new List<T>();
- if(!scene.isLoaded)
- {
- return results;
- }
-
- foreach (GameObject rootObject in scene.GetRootGameObjects())
- {
- if (stopOnMatch)
- {
- T foundComponent = rootObject.GetComponentInChildren<T>(true);
- if (foundComponent != null)
- {
- results.Add(foundComponent);
- return results;
- }
- }
- else
- {
- results.AddRange(rootObject.GetComponentsInChildren<T>(true));
- }
- }
-
- return results;
- }
- }
- }
|