using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.XR.Interaction.Toolkit.Samples.StarterAssets;
///
/// Onboarding goal to be achieved as part of the .
///
public struct Goal
{
///
/// Goal state this goal represents.
///
public GoalManager.OnboardingGoals CurrentGoal;
///
/// This denotes whether a goal has been completed.
///
public bool Completed;
///
/// Creates a new Goal with the specified .
///
/// The state to assign to this Goal.
public Goal(GoalManager.OnboardingGoals goal)
{
CurrentGoal = goal;
Completed = false;
}
}
///
/// The GoalManager cycles through a list of Goals, each representing
/// an state to be completed by the user.
///
public class GoalManager : MonoBehaviour
{
///
/// State representation for the onboarding goals for the GoalManager.
///
public enum OnboardingGoals
{
///
/// Current empty scene
///
Empty,
///
/// Find/scan for AR surfaces
///
FindSurfaces,
///
/// Tap a surface to spawn an object
///
TapSurface,
///
/// Show movement hints
///
Hints,
///
/// Show scale and rotate hints
///
Scale
}
///
/// Individual step instructions to show as part of a goal.
///
[Serializable]
public class Step
{
///
/// The GameObject to enable and show the user in order to complete the goal.
///
[SerializeField]
public GameObject stepObject;
///
/// The text to display on the button shown in the step instructions.
///
[SerializeField]
public string buttonText;
///
/// This indicates whether to show an additional button to skip the current goal/step.
///
[SerializeField]
public bool includeSkipButton;
}
[Tooltip("List of Goals/Steps to complete as part of the user onboarding.")]
[SerializeField]
List m_StepList = new List();
///
/// List of Goals/Steps to complete as part of the user onboarding.
///
public List 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;
///
/// Object Spawner used to detect whether the spawning goal has been achieved.
///
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;
///
/// The greeting prompt Game Object to show when onboarding begins.
///
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;
///
/// The Options Button to enable once the greeting prompt is dismissed.
///
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;
///
/// The Create Button to enable once the greeting prompt is dismissed.
///
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;
///
/// The AR Template Menu Manager object to enable once the greeting prompt is dismissed.
///
public ARTemplateMenuManager menuManager
{
get => m_MenuManager;
set => m_MenuManager = value;
}
const int k_NumberOfSurfacesTappedToCompleteGoal = 1;
Queue 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;
}
}
///
/// Tells the Goal Manager to wait for a specific number of seconds before completing
/// the goal and showing the next card.
///
/// The number of seconds to wait before showing the card.
/// Returns an IEnumerator for the current coroutine running.
public IEnumerator WaitUntilNextCard(float seconds)
{
yield return new WaitForSeconds(seconds);
if (!Pointer.current.press.wasPressedThisFrame)
{
m_CurrentCoroutine = null;
CompleteGoal();
}
}
///
/// Forces the completion of the current goal and moves to the next.
///
public void ForceCompleteGoal()
{
CompleteGoal();
}
void OnObjectSpawned(GameObject spawnedObject)
{
m_SurfacesTapped++;
if (m_CurrentGoal.CurrentGoal == OnboardingGoals.TapSurface && m_SurfacesTapped >= k_NumberOfSurfacesTappedToCompleteGoal)
{
CompleteGoal();
}
}
///
/// Triggers a restart of the onboarding/coaching process.
///
public void StartCoaching()
{
if (m_OnboardingGoals != null)
{
m_OnboardingGoals.Clear();
}
m_OnboardingGoals = new Queue();
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);
}
}
}
}