#if AR_FOUNDATION_PRESENT using UnityEngine.InputSystem; using UnityEngine.XR.ARFoundation; using UnityEngine.XR.ARSubsystems; using UnityEngine.XR.Interaction.Toolkit.Inputs; using UnityEngine.XR.Interaction.Toolkit.Utilities.Internal; using UnityEngine.XR.Interaction.Toolkit.Samples.StarterAssets; namespace UnityEngine.XR.Interaction.Toolkit.Samples.ARStarterAssets { /// /// Spawns an object at an 's raycast hit position when a trigger is activated. /// public class ARInteractorSpawnTrigger : MonoBehaviour { /// /// The type of trigger to use to spawn an object. /// public enum SpawnTriggerType { /// /// Spawn an object when the associated with the interactor activates its selection /// state but no selection actually occurs. /// SelectAttempt, /// /// Spawn an object when an is performed. /// InputAction, } [SerializeField] [RequireInterface(typeof(IARInteractor))] [Tooltip("The AR Interactor that determines where to spawn the object.")] Object m_ARInteractorObject; /// /// The that determines where to spawn the object. /// public Object arInteractorObject { get => m_ARInteractorObject; set => m_ARInteractorObject = value; } [SerializeField] [Tooltip("The behavior to use to spawn objects.")] ObjectSpawner m_ObjectSpawner; /// /// The behavior to use to spawn objects. /// public ObjectSpawner objectSpawner { get => m_ObjectSpawner; set => m_ObjectSpawner = 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; /// /// Whether to require that the hits an with an alignment of /// in order to spawn anything. /// 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; /// /// The type of trigger to use to spawn an object. /// public SpawnTriggerType spawnTriggerType { get => m_SpawnTriggerType; set => m_SpawnTriggerType = value; } [SerializeField] [Tooltip("The action to use to trigger spawn. (Button Control)")] InputActionProperty m_SpawnAction = new(new InputAction(type: InputActionType.Button)); /// /// The Input System action to use to trigger spawn, if is set to . /// Must be an action with a button-like interaction where phase equals performed when pressed. /// Typically a Control or a Value type action with a Press or Sector interaction. /// public InputActionProperty spawnAction { get => m_SpawnAction; set { if (Application.isPlaying) m_SpawnAction.DisableDirectAction(); m_SpawnAction = value; if (Application.isPlaying && isActiveAndEnabled) m_SpawnAction.EnableDirectAction(); } } IARInteractor m_ARInteractor; XRBaseControllerInteractor m_ARInteractorAsControllerInteractor; bool m_EverHadSelection; /// /// See . /// void Start() { if (m_ObjectSpawner == null) #if UNITY_2023_1_OR_NEWER m_ObjectSpawner = FindAnyObjectByType(); #else m_ObjectSpawner = FindObjectOfType(); #endif m_ARInteractor = m_ARInteractorObject as IARInteractor; m_ARInteractorAsControllerInteractor = m_ARInteractorObject as XRBaseControllerInteractor; if (m_SpawnTriggerType == SpawnTriggerType.SelectAttempt && m_ARInteractorAsControllerInteractor == null) { Debug.LogError("Can only use SelectAttempt spawn trigger type with XRBaseControllerInteractor.", this); enabled = false; } } /// /// See . /// void OnEnable() { m_SpawnAction.EnableDirectAction(); } /// /// See . /// void OnDisable() { m_SpawnAction.DisableDirectAction(); } /// /// See . /// void Update() { var attemptSpawn = false; switch (m_SpawnTriggerType) { case SpawnTriggerType.SelectAttempt: var currentControllerState = m_ARInteractorAsControllerInteractor.xrController.currentControllerState; if (currentControllerState.selectInteractionState.activatedThisFrame) m_EverHadSelection = m_ARInteractorAsControllerInteractor.hasSelection; else if (currentControllerState.selectInteractionState.active) m_EverHadSelection |= m_ARInteractorAsControllerInteractor.hasSelection; else if (currentControllerState.selectInteractionState.deactivatedThisFrame) attemptSpawn = !m_ARInteractorAsControllerInteractor.hasSelection && !m_EverHadSelection; break; case SpawnTriggerType.InputAction: if (m_SpawnAction.action.WasPerformedThisFrame()) attemptSpawn = true; break; } if (attemptSpawn && m_ARInteractor.TryGetCurrentARRaycastHit(out var arRaycastHit)) { var arPlane = arRaycastHit.trackable as ARPlane; if (arPlane == null) return; if (m_RequireHorizontalUpSurface && arPlane.alignment != PlaneAlignment.HorizontalUp) return; m_ObjectSpawner.TrySpawnObject(arRaycastHit.pose.position, arPlane.normal); } } } } #endif