#if AR_FOUNDATION_PRESENT using UnityEngine.XR.ARSubsystems; using UnityEngine.XR.ARFoundation; using UnityEngine.XR.Interaction.Toolkit.Interactors; using UnityEngine.XR.Interaction.Toolkit.Samples.StarterAssets; namespace UnityEngine.XR.Interaction.Toolkit.Samples.ARStarterAssets { /// /// Spawns an object on physics trigger enter with an , at the point of contact on the plane. /// [RequireComponent(typeof(Rigidbody))] public class ARContactSpawnTrigger : MonoBehaviour { [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("If enabled, spawning will be blocked if the active interactor in the associated XR Interaction Group is hovering or selecting an interactable object.")] bool m_BlockSpawnDuringInteraction; /// /// If enabled, spawning will be blocked if the active interactor in the associated is hovering or selecting an interactable object. /// public bool blockSpawnDuringInteraction { get => m_BlockSpawnDuringInteraction; set => m_BlockSpawnDuringInteraction = value; } [SerializeField] [Tooltip("XR Interaction Group associated with this contact spawn trigger.")] XRInteractionGroup m_InteractionGroup; /// /// XR Interaction Group associated with this contact spawn trigger. Spawning will be blocked if an interactor from this XR Interaction Group is /// hovering or selecting and interactable and is enabled. /// /// If , this scripts attempts to find an component on the parent. public XRInteractionGroup interactionGroup { get => m_InteractionGroup; set => m_InteractionGroup = value; } [SerializeField] [Tooltip("Whether to require that the AR Plane has an alignment of horizontal up to spawn on it.")] bool m_RequireHorizontalUpSurface; /// /// Whether to require that the has an alignment of to spawn on it. /// public bool requireHorizontalUpSurface { get => m_RequireHorizontalUpSurface; set => m_RequireHorizontalUpSurface = value; } /// /// See . /// void Start() { if (m_InteractionGroup == null) { m_InteractionGroup = GetComponentInParent(); if (m_BlockSpawnDuringInteraction && m_InteractionGroup == null) Debug.LogWarning("Interaction group could be found. Spawning objects will not be blocked during hover or select interaction.", this); } if (m_ObjectSpawner == null) { #if UNITY_2023_1_OR_NEWER m_ObjectSpawner = FindAnyObjectByType(); #else m_ObjectSpawner = FindObjectOfType(); #endif if (m_ObjectSpawner == null) Debug.LogWarning("Object spawner could not be found. AR Contact Spawn Trigger will be unable to spawn objects.", this); } } /// /// See . /// void OnTriggerEnter(Collider other) { if ((blockSpawnDuringInteraction && IsInteractionBlockingSpawn()) || !TryGetSpawnSurfaceData(other, out var surfacePosition, out var surfaceNormal)) return; var infinitePlane = new Plane(surfaceNormal, surfacePosition); var contactPoint = infinitePlane.ClosestPointOnPlane(transform.position); m_ObjectSpawner.TrySpawnObject(contactPoint, surfaceNormal); } /// /// Tries to get the surface position and normal from an object to potentially spawn on. /// /// The collider of the object to potentially spawn on. /// The potential world position of the spawn surface. /// The potential normal of the spawn surface. /// Returns if is a valid spawn surface, /// otherwise returns . public bool TryGetSpawnSurfaceData(Collider objectCollider, out Vector3 surfacePosition, out Vector3 surfaceNormal) { surfacePosition = default; surfaceNormal = default; var arPlane = objectCollider.GetComponent(); if (arPlane == null) return false; if (m_RequireHorizontalUpSurface && arPlane.alignment != PlaneAlignment.HorizontalUp) return false; surfaceNormal = arPlane.normal; surfacePosition = arPlane.center; return true; } bool IsInteractionBlockingSpawn() { if (m_InteractionGroup != null && m_InteractionGroup.activeInteractor != null) { var hoverInteractor = (IXRHoverInteractor)m_InteractionGroup.activeInteractor; var selectInteractor = (IXRSelectInteractor)m_InteractionGroup.activeInteractor; var isHovering = (hoverInteractor != null) && hoverInteractor.hasHover; var isSelecting = (selectInteractor != null) && selectInteractor.hasSelection; return isHovering || isSelecting; } return false; } } } #endif