summaryrefslogtreecommitdiff
path: root/Assets/Samples/XR Interaction Toolkit/3.1.2/Starter Assets/DemoSceneAssets/Scripts/MultiAnchorTeleportReticle.cs
blob: 86643c763725dd11f73a0f726ef597f2fca1ed17 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
using UnityEngine.UI;
using UnityEngine.XR.Interaction.Toolkit.Interactables;
using UnityEngine.XR.Interaction.Toolkit.Interactables.Visuals;
using UnityEngine.XR.Interaction.Toolkit.Interactors.Visuals;
using UnityEngine.XR.Interaction.Toolkit.Locomotion.Teleportation;

namespace UnityEngine.XR.Interaction.Toolkit.Samples.StarterAssets
{
    /// <summary>
    /// A custom reticle for a <see cref="TeleportationMultiAnchorVolume"/> that displays its progress towards evaluating
    /// a destination anchor and an indicator pointing in the direction of the destination anchor.
    /// </summary>
    public class MultiAnchorTeleportReticle : MonoBehaviour, IXRInteractableCustomReticle
    {
        [SerializeField]
        [Tooltip("Filled image that displays the progress towards evaluating a destination anchor.")]
        Image m_TimerProgressFilledImage;

        /// <summary>
        /// <see cref="Image.Type.Filled"/> image that displays the progress towards evaluating a destination anchor.
        /// </summary>
        public Image timerProgressFilledImage
        {
            get => m_TimerProgressFilledImage;
            set => m_TimerProgressFilledImage = value;
        }

        [SerializeField]
        [Tooltip("Object that is rotated about its Z axis to point at the destination anchor.")]
        GameObject m_DestinationIndicator;

        /// <summary>
        /// Object that is rotated about its Z axis to point at the destination anchor.
        /// </summary>
        public GameObject destinationIndicator
        {
            get => m_DestinationIndicator;
            set => m_DestinationIndicator = value;
        }

        [SerializeField]
        [Tooltip("Object that is rotated about its Z axis to point at the potential destination while still evaluating.")]
        GameObject m_PotentialDestinationIndicator;

        /// <summary>
        /// Object that is rotated about its Z axis to point at the potential destination while still evaluating.
        /// </summary>
        public GameObject potentialDestinationIndicator
        {
            get => m_PotentialDestinationIndicator;
            set => m_PotentialDestinationIndicator = value;
        }

        [SerializeField]
        [Tooltip("The amount of time, in seconds, between updates to the indicator pointing at the potential destination.")]
        float m_PotentialIndicatorUpdateFrequency = 0.1f;

        /// <summary>
        /// The amount of time, in seconds, between updates to the indicator pointing at the potential destination.
        /// </summary>
        public float potentialIndicatorUpdateFrequency
        {
            get => m_PotentialIndicatorUpdateFrequency;
            set => m_PotentialIndicatorUpdateFrequency = value;
        }

        TeleportationMultiAnchorVolume m_AnchorVolume;
        float m_LastPotentialIndicatorUpdateTime;

        /// <inheritdoc/>
        public void OnReticleAttached(XRBaseInteractable interactable, IXRCustomReticleProvider reticleProvider)
        {
            m_AnchorVolume = interactable as TeleportationMultiAnchorVolume;
            m_PotentialDestinationIndicator.SetActive(false);
            m_DestinationIndicator.SetActive(false);
            m_TimerProgressFilledImage.type = Image.Type.Filled;
            m_TimerProgressFilledImage.fillAmount = 0f;
            if (m_AnchorVolume == null)
                return;

            m_AnchorVolume.destinationAnchorChanged += OnDestinationAnchorChanged;
        }

        /// <inheritdoc/>
        public void OnReticleDetaching()
        {
            if (m_AnchorVolume == null)
                return;

            m_AnchorVolume.destinationAnchorChanged -= OnDestinationAnchorChanged;
            m_AnchorVolume = null;
        }

        /// <summary>
        /// See <see cref="MonoBehaviour"/>.
        /// </summary>
        protected void Update()
        {
            if (m_AnchorVolume == null)
                return;

            var destinationAnchor = m_AnchorVolume.destinationAnchor;
            if (destinationAnchor != null)
            {
                PointAtTarget(m_DestinationIndicator.transform, destinationAnchor.position);
                return;
            }

            m_TimerProgressFilledImage.fillAmount = m_AnchorVolume.destinationEvaluationProgress;
            if (Time.time - m_LastPotentialIndicatorUpdateTime >= m_PotentialIndicatorUpdateFrequency)
                UpdatePotentialDestinationIndicator();
        }

        void UpdatePotentialDestinationIndicator()
        {
            m_LastPotentialIndicatorUpdateTime = Time.time;
            if (!m_AnchorVolume.destinationEvaluationSettings.Value.pollForDestinationChange)
            {
                m_PotentialDestinationIndicator.SetActive(false);
                return;
            }

            var potentialDestinationIndex = m_AnchorVolume.destinationEvaluationFilter.GetDestinationAnchorIndex(m_AnchorVolume);
            var anchors = m_AnchorVolume.anchorTransforms;
            if (potentialDestinationIndex < 0 || potentialDestinationIndex >= anchors.Count)
            {
                m_PotentialDestinationIndicator.SetActive(false);
                return;
            }

            var potentialDestination = anchors[potentialDestinationIndex];
            if (potentialDestination == null)
            {
                m_PotentialDestinationIndicator.SetActive(false);
                return;
            }

            m_PotentialDestinationIndicator.SetActive(true);
            PointAtTarget(m_PotentialDestinationIndicator.transform, potentialDestination.position);
        }

        void OnDestinationAnchorChanged(TeleportationMultiAnchorVolume anchorVolume)
        {
            var destinationAnchor = anchorVolume.destinationAnchor;
            if (destinationAnchor != null)
            {
                m_TimerProgressFilledImage.fillAmount = 1f;
                m_PotentialDestinationIndicator.SetActive(false);
                m_DestinationIndicator.SetActive(true);
                PointAtTarget(m_DestinationIndicator.transform, destinationAnchor.position);
            }
            else
            {
                m_TimerProgressFilledImage.fillAmount = 0f;
                m_DestinationIndicator.SetActive(false);
            }
        }

        static void PointAtTarget(Transform indicatorTransform, Vector3 targetPosition)
        {
            indicatorTransform.rotation = Quaternion.LookRotation(indicatorTransform.forward, targetPosition - indicatorTransform.position);
        }
    }
}