diff options
Diffstat (limited to 'Assets/Samples/XR Interaction Toolkit/3.1.2/Starter Assets/Scripts/ObjectSpawner.cs')
-rw-r--r-- | Assets/Samples/XR Interaction Toolkit/3.1.2/Starter Assets/Scripts/ObjectSpawner.cs | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/Assets/Samples/XR Interaction Toolkit/3.1.2/Starter Assets/Scripts/ObjectSpawner.cs b/Assets/Samples/XR Interaction Toolkit/3.1.2/Starter Assets/Scripts/ObjectSpawner.cs new file mode 100644 index 0000000..3b278e0 --- /dev/null +++ b/Assets/Samples/XR Interaction Toolkit/3.1.2/Starter Assets/Scripts/ObjectSpawner.cs @@ -0,0 +1,237 @@ +using System; +using System.Collections.Generic; +using UnityEngine.XR.Interaction.Toolkit.Utilities; + +namespace UnityEngine.XR.Interaction.Toolkit.Samples.StarterAssets +{ + /// <summary> + /// Behavior with an API for spawning objects from a given set of prefabs. + /// </summary> + public class ObjectSpawner : MonoBehaviour + { + [SerializeField] + [Tooltip("The camera that objects will face when spawned. If not set, defaults to the main camera.")] + Camera m_CameraToFace; + + /// <summary> + /// The camera that objects will face when spawned. If not set, defaults to the <see cref="Camera.main"/> camera. + /// </summary> + public Camera cameraToFace + { + get + { + EnsureFacingCamera(); + return m_CameraToFace; + } + set => m_CameraToFace = value; + } + + [SerializeField] + [Tooltip("The list of prefabs available to spawn.")] + List<GameObject> m_ObjectPrefabs = new List<GameObject>(); + + /// <summary> + /// The list of prefabs available to spawn. + /// </summary> + public List<GameObject> objectPrefabs + { + get => m_ObjectPrefabs; + set => m_ObjectPrefabs = value; + } + + [SerializeField] + [Tooltip("Optional prefab to spawn for each spawned object. Use a prefab with the Destroy Self component to make " + + "sure the visualization only lives temporarily.")] + GameObject m_SpawnVisualizationPrefab; + + /// <summary> + /// Optional prefab to spawn for each spawned object. + /// </summary> + /// <remarks>Use a prefab with <see cref="DestroySelf"/> to make sure the visualization only lives temporarily.</remarks> + public GameObject spawnVisualizationPrefab + { + get => m_SpawnVisualizationPrefab; + set => m_SpawnVisualizationPrefab = value; + } + + [SerializeField] + [Tooltip("The index of the prefab to spawn. If outside the range of the list, this behavior will select " + + "a random object each time it spawns.")] + int m_SpawnOptionIndex = -1; + + /// <summary> + /// The index of the prefab to spawn. If outside the range of <see cref="objectPrefabs"/>, this behavior will + /// select a random object each time it spawns. + /// </summary> + /// <seealso cref="isSpawnOptionRandomized"/> + public int spawnOptionIndex + { + get => m_SpawnOptionIndex; + set => m_SpawnOptionIndex = value; + } + + /// <summary> + /// Whether this behavior will select a random object from <see cref="objectPrefabs"/> each time it spawns. + /// </summary> + /// <seealso cref="spawnOptionIndex"/> + /// <seealso cref="RandomizeSpawnOption"/> + public bool isSpawnOptionRandomized => m_SpawnOptionIndex < 0 || m_SpawnOptionIndex >= m_ObjectPrefabs.Count; + + [SerializeField] + [Tooltip("Whether to only spawn an object if the spawn point is within view of the camera.")] + bool m_OnlySpawnInView = true; + + /// <summary> + /// Whether to only spawn an object if the spawn point is within view of the <see cref="cameraToFace"/>. + /// </summary> + public bool onlySpawnInView + { + get => m_OnlySpawnInView; + set => m_OnlySpawnInView = value; + } + + [SerializeField] + [Tooltip("The size, in viewport units, of the periphery inside the viewport that will not be considered in view.")] + float m_ViewportPeriphery = 0.15f; + + /// <summary> + /// The size, in viewport units, of the periphery inside the viewport that will not be considered in view. + /// </summary> + public float viewportPeriphery + { + get => m_ViewportPeriphery; + set => m_ViewportPeriphery = value; + } + + [SerializeField] + [Tooltip("When enabled, the object will be rotated about the y-axis when spawned by Spawn Angle Range, " + + "in relation to the direction of the spawn point to the camera.")] + bool m_ApplyRandomAngleAtSpawn = true; + + /// <summary> + /// When enabled, the object will be rotated about the y-axis when spawned by <see cref="spawnAngleRange"/> + /// in relation to the direction of the spawn point to the camera. + /// </summary> + public bool applyRandomAngleAtSpawn + { + get => m_ApplyRandomAngleAtSpawn; + set => m_ApplyRandomAngleAtSpawn = value; + } + + [SerializeField] + [Tooltip("The range in degrees that the object will randomly be rotated about the y axis when spawned, " + + "in relation to the direction of the spawn point to the camera.")] + float m_SpawnAngleRange = 45f; + + /// <summary> + /// The range in degrees that the object will randomly be rotated about the y axis when spawned, in relation + /// to the direction of the spawn point to the camera. + /// </summary> + public float spawnAngleRange + { + get => m_SpawnAngleRange; + set => m_SpawnAngleRange = value; + } + + [SerializeField] + [Tooltip("Whether to spawn each object as a child of this object.")] + bool m_SpawnAsChildren; + + /// <summary> + /// Whether to spawn each object as a child of this object. + /// </summary> + public bool spawnAsChildren + { + get => m_SpawnAsChildren; + set => m_SpawnAsChildren = value; + } + + /// <summary> + /// Event invoked after an object is spawned. + /// </summary> + /// <seealso cref="TrySpawnObject"/> + public event Action<GameObject> objectSpawned; + + /// <summary> + /// See <see cref="MonoBehaviour"/>. + /// </summary> + void Awake() + { + EnsureFacingCamera(); + } + + void EnsureFacingCamera() + { + if (m_CameraToFace == null) + m_CameraToFace = Camera.main; + } + + /// <summary> + /// Sets this behavior to select a random object from <see cref="objectPrefabs"/> each time it spawns. + /// </summary> + /// <seealso cref="spawnOptionIndex"/> + /// <seealso cref="isSpawnOptionRandomized"/> + public void RandomizeSpawnOption() + { + m_SpawnOptionIndex = -1; + } + + /// <summary> + /// Attempts to spawn an object from <see cref="objectPrefabs"/> at the given position. The object will have a + /// yaw rotation that faces <see cref="cameraToFace"/>, plus or minus a random angle within <see cref="spawnAngleRange"/>. + /// </summary> + /// <param name="spawnPoint">The world space position at which to spawn the object.</param> + /// <param name="spawnNormal">The world space normal of the spawn surface.</param> + /// <returns>Returns <see langword="true"/> if the spawner successfully spawned an object. Otherwise returns + /// <see langword="false"/>, for instance if the spawn point is out of view of the camera.</returns> + /// <remarks> + /// The object selected to spawn is based on <see cref="spawnOptionIndex"/>. If the index is outside + /// the range of <see cref="objectPrefabs"/>, this method will select a random prefab from the list to spawn. + /// Otherwise, it will spawn the prefab at the index. + /// </remarks> + /// <seealso cref="objectSpawned"/> + public bool TrySpawnObject(Vector3 spawnPoint, Vector3 spawnNormal) + { + if (m_OnlySpawnInView) + { + var inViewMin = m_ViewportPeriphery; + var inViewMax = 1f - m_ViewportPeriphery; + var pointInViewportSpace = cameraToFace.WorldToViewportPoint(spawnPoint); + if (pointInViewportSpace.z < 0f || pointInViewportSpace.x > inViewMax || pointInViewportSpace.x < inViewMin || + pointInViewportSpace.y > inViewMax || pointInViewportSpace.y < inViewMin) + { + return false; + } + } + + var objectIndex = isSpawnOptionRandomized ? Random.Range(0, m_ObjectPrefabs.Count) : m_SpawnOptionIndex; + var newObject = Instantiate(m_ObjectPrefabs[objectIndex]); + if (m_SpawnAsChildren) + newObject.transform.parent = transform; + + newObject.transform.position = spawnPoint; + EnsureFacingCamera(); + + var facePosition = m_CameraToFace.transform.position; + var forward = facePosition - spawnPoint; + BurstMathUtility.ProjectOnPlane(forward, spawnNormal, out var projectedForward); + newObject.transform.rotation = Quaternion.LookRotation(projectedForward, spawnNormal); + + if (m_ApplyRandomAngleAtSpawn) + { + var randomRotation = Random.Range(-m_SpawnAngleRange, m_SpawnAngleRange); + newObject.transform.Rotate(Vector3.up, randomRotation); + } + + if (m_SpawnVisualizationPrefab != null) + { + var visualizationTrans = Instantiate(m_SpawnVisualizationPrefab).transform; + visualizationTrans.position = spawnPoint; + visualizationTrans.rotation = newObject.transform.rotation; + } + + objectSpawned?.Invoke(newObject); + return true; + } + } +} |