| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271 |
- 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>
- /// Sets the <see cref="spawnOptionIndex"/> so that a specific object will spawn. If the index is out
- /// of bounds of the list defined in <see cref="objectPrefabs"/>, the index will not be changed.
- /// </summary>
- /// <param name="index">Index of the object to be spawned.</param>
- /// <seealso cref="objectPrefabs"/>
- /// <seealso cref="spawnOptionIndex"/>
- public void SetSpawnObjectIndex(int index)
- {
- if (index < m_ObjectPrefabs.Count)
- m_SpawnOptionIndex = index;
- else
- Debug.LogWarning("Object index specified larger than number of Object Prefabs.", this);
- }
- /// <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)
- {
- Debug.LogWarning("Object spawn point out of view and OnlySpawnInView is set to true.", this);
- 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;
- }
- /// <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>
- /// <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 void SpawnObject(Vector3 spawnPoint, Vector3 spawnNormal)
- {
- if (!TrySpawnObject(spawnPoint, spawnNormal))
- Debug.LogWarning("Could not spawn object.", this);
- }
- }
- }
|