| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207 |
- #if AR_FOUNDATION_PRESENT
- using UnityEngine.Events;
- using UnityEngine.EventSystems;
- using UnityEngine.XR.ARFoundation;
- using UnityEngine.XR.ARSubsystems;
- using UnityEngine.XR.Interaction.Toolkit.Inputs.Readers;
- using UnityEngine.XR.Interaction.Toolkit.Interactors;
- namespace UnityEngine.XR.Interaction.Toolkit.Samples.ARStarterAssets
- {
- /// <summary>
- /// Spawns an object at an <see cref="IARInteractor"/>'s raycast hit position when a trigger is activated.
- /// </summary>
- public class ARInteractorSpawnTrigger : MonoBehaviour
- {
- /// <summary>
- /// The type of trigger to use to spawn an object.
- /// </summary>
- public enum SpawnTriggerType
- {
- /// <summary>
- /// Spawn an object when the interactor activates its select input
- /// but no selection actually occurs.
- /// </summary>
- SelectAttempt,
- /// <summary>
- /// Spawn an object when an input is performed.
- /// </summary>
- InputAction,
- }
- [SerializeField]
- [Tooltip("The AR ray interactor that determines where to spawn the object.")]
- XRRayInteractor m_ARInteractor;
- /// <summary>
- /// The AR ray interactor that determines where to spawn the object.
- /// </summary>
- public XRRayInteractor arInteractor
- {
- get => m_ARInteractor;
- set => m_ARInteractor = value;
- }
- [SerializeField]
- [Tooltip("Whether to require that the AR Interactor hits an AR Plane with a horizontal up alignment in order to spawn anything.")]
- bool m_RequireHorizontalUpSurface;
- /// <summary>
- /// Whether to require that the <see cref="IARInteractor"/> hits an <see cref="ARPlane"/> with an alignment of
- /// <see cref="PlaneAlignment.HorizontalUp"/> in order to spawn anything.
- /// </summary>
- public bool requireHorizontalUpSurface
- {
- get => m_RequireHorizontalUpSurface;
- set => m_RequireHorizontalUpSurface = value;
- }
- [SerializeField]
- [Tooltip("The type of trigger to use to spawn an object, either when the Interactor's select action occurs or " +
- "when a button input is performed.")]
- SpawnTriggerType m_SpawnTriggerType;
- /// <summary>
- /// The type of trigger to use to spawn an object.
- /// </summary>
- public SpawnTriggerType spawnTriggerType
- {
- get => m_SpawnTriggerType;
- set => m_SpawnTriggerType = value;
- }
- [SerializeField]
- XRInputButtonReader m_SpawnObjectInput = new XRInputButtonReader("Spawn Object");
- /// <summary>
- /// The input used to trigger spawn, if <see cref="spawnTriggerType"/> is set to <see cref="SpawnTriggerType.InputAction"/>.
- /// </summary>
- public XRInputButtonReader spawnObjectInput
- {
- get => m_SpawnObjectInput;
- set => XRInputReaderUtility.SetInputProperty(ref m_SpawnObjectInput, value, this);
- }
- [SerializeField]
- [Tooltip("When enabled, spawn will not be triggered if an object is currently selected.")]
- bool m_BlockSpawnWhenInteractorHasSelection = true;
- /// <summary>
- /// When enabled, spawn will not be triggered if an object is currently selected.
- /// </summary>
- public bool blockSpawnWhenInteractorHasSelection
- {
- get => m_BlockSpawnWhenInteractorHasSelection;
- set => m_BlockSpawnWhenInteractorHasSelection = value;
- }
- /// <summary>
- /// Calls the methods in its invocation list when an object is spawned.
- /// </summary>
- /// <remarks>
- /// The first event parameter corresponds to the spawn position in world space
- /// and the second event parameter corresponds to the vector normal to the surface.
- /// </remarks>
- public UnityEvent<Vector3, Vector3> objectSpawnTriggered
- {
- get => m_ObjectSpawnTriggered;
- set => m_ObjectSpawnTriggered = value;
- }
- [Header("Events")]
- [SerializeField]
- [Tooltip("Calls the methods in its invocation list when an object is spawned.")]
- UnityEvent<Vector3, Vector3> m_ObjectSpawnTriggered = new UnityEvent<Vector3, Vector3>();
- bool m_AttemptSpawn;
- bool m_EverHadSelection;
- /// <summary>
- /// See <see cref="MonoBehaviour"/>.
- /// </summary>
- void OnEnable()
- {
- m_SpawnObjectInput.EnableDirectActionIfModeUsed();
- }
- /// <summary>
- /// See <see cref="MonoBehaviour"/>.
- /// </summary>
- void OnDisable()
- {
- m_SpawnObjectInput.DisableDirectActionIfModeUsed();
- }
- /// <summary>
- /// See <see cref="MonoBehaviour"/>.
- /// </summary>
- void Start()
- {
- if (m_ARInteractor == null)
- {
- Debug.LogError("Missing AR Interactor reference, disabling component.", this);
- enabled = false;
- }
- }
- /// <summary>
- /// See <see cref="MonoBehaviour"/>.
- /// </summary>
- void Update()
- {
- // Wait a frame after the Spawn Object input is triggered to actually cast against AR planes and spawn
- // in order to ensure the touchscreen gestures have finished processing to allow the ray pose driver
- // to update the pose based on the touch position of the gestures.
- if (m_AttemptSpawn)
- {
- m_AttemptSpawn = false;
- // Cancel the spawn if the select was delayed until the frame after the spawn trigger.
- // This can happen if the select action uses a different input source than the spawn trigger.
- if (m_ARInteractor.hasSelection)
- return;
- // Don't spawn the object if the tap was over screen space UI.
- var isPointerOverUI = EventSystem.current != null && EventSystem.current.IsPointerOverGameObject(-1);
- if (!isPointerOverUI && m_ARInteractor.TryGetCurrentARRaycastHit(out var arRaycastHit))
- {
- if (!(arRaycastHit.trackable is ARPlane arPlane))
- return;
- if (m_RequireHorizontalUpSurface && arPlane.alignment != PlaneAlignment.HorizontalUp)
- return;
- m_ObjectSpawnTriggered.Invoke(arRaycastHit.pose.position, arPlane.normal);
- }
- return;
- }
- var selectState = m_ARInteractor.logicalSelectState;
- if (m_BlockSpawnWhenInteractorHasSelection)
- {
- if (selectState.wasPerformedThisFrame)
- m_EverHadSelection = m_ARInteractor.hasSelection;
- else if (selectState.active)
- m_EverHadSelection |= m_ARInteractor.hasSelection;
- }
- m_AttemptSpawn = false;
- switch (m_SpawnTriggerType)
- {
- case SpawnTriggerType.SelectAttempt:
- if (selectState.wasCompletedThisFrame)
- m_AttemptSpawn = !m_ARInteractor.hasSelection && !m_EverHadSelection;
- break;
- case SpawnTriggerType.InputAction:
- if (m_SpawnObjectInput.ReadWasPerformedThisFrame())
- m_AttemptSpawn = !m_ARInteractor.hasSelection && !m_EverHadSelection;
- break;
- }
- }
- }
- }
- #endif
|