| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328 |
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine.InputSystem;
- using UnityEngine.XR.Interaction.Toolkit.Samples.StarterAssets;
- namespace UnityEngine.XR.Templates.AR
- {
- /// <summary>
- /// Onboarding goal to be achieved as part of the <see cref="GoalManager"/>.
- /// </summary>
- public struct Goal
- {
- /// <summary>
- /// Goal state this goal represents.
- /// </summary>
- public GoalManager.OnboardingGoals CurrentGoal;
- /// <summary>
- /// This denotes whether a goal has been completed.
- /// </summary>
- public bool Completed;
- /// <summary>
- /// Creates a new Goal with the specified <see cref="GoalManager.OnboardingGoals"/>.
- /// </summary>
- /// <param name="goal">The <see cref="GoalManager.OnboardingGoals"/> state to assign to this Goal.</param>
- public Goal(GoalManager.OnboardingGoals goal)
- {
- CurrentGoal = goal;
- Completed = false;
- }
- }
- /// <summary>
- /// The GoalManager cycles through a list of Goals, each representing
- /// an <see cref="GoalManager.OnboardingGoals"/> state to be completed by the user.
- /// </summary>
- public class GoalManager : MonoBehaviour
- {
- /// <summary>
- /// State representation for the onboarding goals for the GoalManager.
- /// </summary>
- public enum OnboardingGoals
- {
- /// <summary>
- /// Current empty scene
- /// </summary>
- Empty,
- /// <summary>
- /// Find/scan for AR surfaces
- /// </summary>
- FindSurfaces,
- /// <summary>
- /// Tap a surface to spawn an object
- /// </summary>
- TapSurface,
- /// <summary>
- /// Show movement hints
- /// </summary>
- Hints,
- /// <summary>
- /// Show scale and rotate hints
- /// </summary>
- Scale
- }
- /// <summary>
- /// Individual step instructions to show as part of a goal.
- /// </summary>
- [Serializable]
- public class Step
- {
- /// <summary>
- /// The GameObject to enable and show the user in order to complete the goal.
- /// </summary>
- [SerializeField]
- public GameObject stepObject;
- /// <summary>
- /// The text to display on the button shown in the step instructions.
- /// </summary>
- [SerializeField]
- public string buttonText;
- /// <summary>
- /// This indicates whether to show an additional button to skip the current goal/step.
- /// </summary>
- [SerializeField]
- public bool includeSkipButton;
- }
- [Tooltip("List of Goals/Steps to complete as part of the user onboarding.")]
- [SerializeField]
- List<Step> m_StepList = new List<Step>();
- /// <summary>
- /// List of Goals/Steps to complete as part of the user onboarding.
- /// </summary>
- public List<Step> stepList
- {
- get => m_StepList;
- set => m_StepList = value;
- }
- [Tooltip("Object Spawner used to detect whether the spawning goal has been achieved.")]
- [SerializeField]
- ObjectSpawner m_ObjectSpawner;
- /// <summary>
- /// Object Spawner used to detect whether the spawning goal has been achieved.
- /// </summary>
- public ObjectSpawner objectSpawner
- {
- get => m_ObjectSpawner;
- set => m_ObjectSpawner = value;
- }
- [Tooltip("The greeting prompt Game Object to show when onboarding begins.")]
- [SerializeField]
- GameObject m_GreetingPrompt;
- /// <summary>
- /// The greeting prompt Game Object to show when onboarding begins.
- /// </summary>
- public GameObject greetingPrompt
- {
- get => m_GreetingPrompt;
- set => m_GreetingPrompt = value;
- }
- [Tooltip("The Options Button to enable once the greeting prompt is dismissed.")]
- [SerializeField]
- GameObject m_OptionsButton;
- /// <summary>
- /// The Options Button to enable once the greeting prompt is dismissed.
- /// </summary>
- public GameObject optionsButton
- {
- get => m_OptionsButton;
- set => m_OptionsButton = value;
- }
- [Tooltip("The Create Button to enable once the greeting prompt is dismissed.")]
- [SerializeField]
- GameObject m_CreateButton;
- /// <summary>
- /// The Create Button to enable once the greeting prompt is dismissed.
- /// </summary>
- public GameObject createButton
- {
- get => m_CreateButton;
- set => m_CreateButton = value;
- }
- [Tooltip("The AR Template Menu Manager object to enable once the greeting prompt is dismissed.")]
- [SerializeField]
- ARTemplateMenuManager m_MenuManager;
- /// <summary>
- /// The AR Template Menu Manager object to enable once the greeting prompt is dismissed.
- /// </summary>
- public ARTemplateMenuManager menuManager
- {
- get => m_MenuManager;
- set => m_MenuManager = value;
- }
- const int k_NumberOfSurfacesTappedToCompleteGoal = 1;
- Queue<Goal> m_OnboardingGoals;
- Coroutine m_CurrentCoroutine;
- Goal m_CurrentGoal;
- bool m_AllGoalsFinished;
- int m_SurfacesTapped;
- int m_CurrentGoalIndex = 0;
- void Update()
- {
- if (Pointer.current != null && Pointer.current.press.wasPressedThisFrame && !m_AllGoalsFinished && (m_CurrentGoal.CurrentGoal == OnboardingGoals.FindSurfaces || m_CurrentGoal.CurrentGoal == OnboardingGoals.Hints || m_CurrentGoal.CurrentGoal == OnboardingGoals.Scale))
- {
- if (m_CurrentCoroutine != null)
- {
- StopCoroutine(m_CurrentCoroutine);
- }
- CompleteGoal();
- }
- }
- void CompleteGoal()
- {
- if (m_CurrentGoal.CurrentGoal == OnboardingGoals.TapSurface)
- m_ObjectSpawner.objectSpawned -= OnObjectSpawned;
- m_CurrentGoal.Completed = true;
- m_CurrentGoalIndex++;
- if (m_OnboardingGoals.Count > 0)
- {
- m_CurrentGoal = m_OnboardingGoals.Dequeue();
- m_StepList[m_CurrentGoalIndex - 1].stepObject.SetActive(false);
- m_StepList[m_CurrentGoalIndex].stepObject.SetActive(true);
- }
- else
- {
- m_StepList[m_CurrentGoalIndex - 1].stepObject.SetActive(false);
- m_AllGoalsFinished = true;
- return;
- }
- PreprocessGoal();
- }
- void PreprocessGoal()
- {
- if (m_CurrentGoal.CurrentGoal == OnboardingGoals.FindSurfaces)
- {
- m_CurrentCoroutine = StartCoroutine(WaitUntilNextCard(5f));
- }
- else if (m_CurrentGoal.CurrentGoal == OnboardingGoals.Hints)
- {
- m_CurrentCoroutine = StartCoroutine(WaitUntilNextCard(6f));
- }
- else if (m_CurrentGoal.CurrentGoal == OnboardingGoals.Scale)
- {
- m_CurrentCoroutine = StartCoroutine(WaitUntilNextCard(8f));
- }
- else if (m_CurrentGoal.CurrentGoal == OnboardingGoals.TapSurface)
- {
- m_SurfacesTapped = 0;
- m_ObjectSpawner.objectSpawned += OnObjectSpawned;
- }
- }
- /// <summary>
- /// Tells the Goal Manager to wait for a specific number of seconds before completing
- /// the goal and showing the next card.
- /// </summary>
- /// <param name="seconds">The number of seconds to wait before showing the card.</param>
- /// <returns>Returns an IEnumerator for the current coroutine running.</returns>
- public IEnumerator WaitUntilNextCard(float seconds)
- {
- yield return new WaitForSeconds(seconds);
- if (!Pointer.current.press.wasPressedThisFrame)
- {
- m_CurrentCoroutine = null;
- CompleteGoal();
- }
- }
- /// <summary>
- /// Forces the completion of the current goal and moves to the next.
- /// </summary>
- public void ForceCompleteGoal()
- {
- CompleteGoal();
- }
- void OnObjectSpawned(GameObject spawnedObject)
- {
- m_SurfacesTapped++;
- if (m_CurrentGoal.CurrentGoal == OnboardingGoals.TapSurface && m_SurfacesTapped >= k_NumberOfSurfacesTappedToCompleteGoal)
- {
- CompleteGoal();
- }
- }
- /// <summary>
- /// Triggers a restart of the onboarding/coaching process.
- /// </summary>
- public void StartCoaching()
- {
- if (m_OnboardingGoals != null)
- {
- m_OnboardingGoals.Clear();
- }
- m_OnboardingGoals = new Queue<Goal>();
- if (!m_AllGoalsFinished)
- {
- var findSurfaceGoal = new Goal(OnboardingGoals.FindSurfaces);
- m_OnboardingGoals.Enqueue(findSurfaceGoal);
- }
- int startingStep = m_AllGoalsFinished ? 1 : 0;
- var tapSurfaceGoal = new Goal(OnboardingGoals.TapSurface);
- var translateHintsGoal = new Goal(OnboardingGoals.Hints);
- var scaleHintsGoal = new Goal(OnboardingGoals.Scale);
- var rotateHintsGoal = new Goal(OnboardingGoals.Hints);
- m_OnboardingGoals.Enqueue(tapSurfaceGoal);
- m_OnboardingGoals.Enqueue(translateHintsGoal);
- m_OnboardingGoals.Enqueue(scaleHintsGoal);
- m_OnboardingGoals.Enqueue(rotateHintsGoal);
- m_CurrentGoal = m_OnboardingGoals.Dequeue();
- m_AllGoalsFinished = false;
- m_CurrentGoalIndex = startingStep;
- m_GreetingPrompt.SetActive(false);
- m_OptionsButton.SetActive(true);
- m_CreateButton.SetActive(true);
- m_MenuManager.enabled = true;
- for (int i = startingStep; i < m_StepList.Count; i++)
- {
- if (i == startingStep)
- {
- m_StepList[i].stepObject.SetActive(true);
- PreprocessGoal();
- }
- else
- {
- m_StepList[i].stepObject.SetActive(false);
- }
- }
- }
- }
- }
|