using System; using System.Collections.Generic; using UnityEngine.Serialization; using UnityEngine.XR.ARSubsystems; namespace UnityEngine.XR.ARFoundation { /// /// Manages anchors. /// /// /// Use this component to programmatically add, remove, or query for /// anchors. Anchors are Poses in the world /// which will be periodically updated by an AR device as its understanding /// of the world changes. /// Subscribe to changes (added, updated, and removed) via the /// event. /// /// [DefaultExecutionOrder(ARUpdateOrder.k_AnchorManager)] [DisallowMultipleComponent] [RequireComponent(typeof(ARSessionOrigin))] [HelpURL(HelpUrls.ApiWithNamespace + nameof(ARAnchorManager) + ".html")] public sealed class ARAnchorManager : ARTrackableManager< XRAnchorSubsystem, XRAnchorSubsystemDescriptor, XRAnchorSubsystem.Provider, XRAnchor, ARAnchor> { [SerializeField] [Tooltip("If not null, instantiates this prefab for each instantiated anchor.")] [FormerlySerializedAs("m_ReferencePointPrefab")] GameObject m_AnchorPrefab; /// /// This prefab will be instantiated for each . May be `null`. /// /// /// The purpose of this property is to extend the functionality of s. /// It is not the recommended way to instantiate content associated with an . /// See [Anchoring content](xref:arfoundation-anchor-manager#anchoring-content) for more details. /// public GameObject anchorPrefab { get => m_AnchorPrefab; set => m_AnchorPrefab = value; } /// /// Invoked once per frame to communicate changes to anchors, including /// new anchors, the update of existing anchors, and the removal /// of previously existing anchors. /// public event Action anchorsChanged; /// /// Attempts to add an with the given Pose. /// /// /// If /// is not null, a new instance of that prefab will be instantiated. Otherwise, a /// new GameObject will be created. In either case, the resulting /// GameObject will have an component on it. /// /// The pose, in Unity world space, of the . /// A new if successful, otherwise null. /// Thrown if this `MonoBehaviour` is not enabled. /// Thrown if the underlying subsystem is `null`. [Obsolete("Add an anchor using AddComponent<" + nameof(ARAnchor) + ">(). (2020-10-06)")] public ARAnchor AddAnchor(Pose pose) { if (!enabled) throw new InvalidOperationException("Cannot create an anchor from a disabled anchor manager."); if (subsystem == null) throw new InvalidOperationException("Anchor manager has no subsystem. Enable the manager first."); var sessionRelativePose = sessionOrigin.trackablesParent.InverseTransformPose(pose); // Add the anchor to the XRAnchorSubsystem if (subsystem.TryAddAnchor(sessionRelativePose, out var sessionRelativeData)) { return CreateTrackableImmediate(sessionRelativeData); } return null; } internal bool TryAddAnchor(ARAnchor anchor) { if (!CanBeAddedToSubsystem(anchor)) return false; var t = anchor.transform; var sessionRelativePose = sessionOrigin.trackablesParent.InverseTransformPose(new Pose(t.position, t.rotation)); // Add the anchor to the XRAnchorSubsystem if (subsystem.TryAddAnchor(sessionRelativePose, out var sessionRelativeData)) { CreateTrackableFromExisting(anchor, sessionRelativeData); return true; } return false; } /// /// Attempts to create a new anchor that is attached to an existing . /// /// The to which to attach. /// The initial Pose, in Unity world space, of the anchor. /// A new if successful, otherwise null. public ARAnchor AttachAnchor(ARPlane plane, Pose pose) { if (!enabled) throw new InvalidOperationException("Cannot create an anchor from a disabled anchor manager."); if (subsystem == null) throw new InvalidOperationException("Anchor manager has no subsystem. Enable the manager first."); if (plane == null) throw new ArgumentNullException(nameof(plane)); var sessionRelativePose = sessionOrigin.trackablesParent.InverseTransformPose(pose); if (subsystem.TryAttachAnchor(plane.trackableId, sessionRelativePose, out var sessionRelativeData)) { return CreateTrackableImmediate(sessionRelativeData); } return null; } /// /// Attempts to remove an . /// /// The anchor you wish to remove. /// /// True if the anchor was successfully removed. /// False usually means the anchor is not longer tracked by the system. /// [Obsolete("Call Destroy() on the " + nameof(ARAnchor) + " component to remove it. (2020-10-06)")] public bool RemoveAnchor(ARAnchor anchor) { if (!enabled) throw new InvalidOperationException("Cannot create an anchor from a disabled anchor manager."); if (subsystem == null) throw new InvalidOperationException("Anchor manager has no subsystem. Enable the manager first."); if (anchor == null) throw new ArgumentNullException(nameof(anchor)); if (subsystem.TryRemoveAnchor(anchor.trackableId)) { DestroyPendingTrackable(anchor.trackableId); return true; } return false; } internal bool TryRemoveAnchor(ARAnchor anchor) { if (anchor == null) throw new ArgumentNullException(nameof(anchor)); if (subsystem == null) return false; if (subsystem.TryRemoveAnchor(anchor.trackableId)) { if (m_PendingAdds.ContainsKey(anchor.trackableId)) { m_PendingAdds.Remove(anchor.trackableId); m_Trackables.Remove(anchor.trackableId); } anchor.pending = false; return true; } return false; } /// /// Gets the with given , /// or null if it does not exist. /// /// The of the to retrieve. /// The with or null if it does not exist. public ARAnchor GetAnchor(TrackableId trackableId) { if (m_Trackables.TryGetValue(trackableId, out var anchor)) return anchor; return null; } /// /// Get the prefab to instantiate for each . /// /// The prefab to instantiate for each . protected override GameObject GetPrefab() => m_AnchorPrefab; /// /// The name to assign to the `GameObject` instantiated for each . /// protected override string gameObjectName => "Anchor"; /// /// Invoked when the base class detects trackable changes. /// /// The list of added anchors. /// The list of updated anchors. /// The list of removed anchors. protected override void OnTrackablesChanged( List added, List updated, List removed) { if (anchorsChanged != null) { using (new ScopedProfiler("OnAnchorsChanged")) { anchorsChanged(new ARAnchorsChangedEventArgs( added, updated, removed)); } } } } }