1
0
mirror of https://projects.caleb-brown.dev/UDRI-XRT/UDRIGEEKCup2024.git synced 2025-01-22 15:18:25 -05:00
UDRIGEEKCup2024/Assets/MobileARTemplateAssets/Scripts/GoalManager.cs
2024-04-05 11:33:14 -04:00

327 lines
9.4 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.XR.Interaction.Toolkit.Samples.StarterAssets;
/// <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);
}
}
}
}