Assignment for RMIT Mixed Reality in 2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

838 lines
42 KiB

// 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;
}
}
}