2025-12-11 13:14:43 +01:00

491 lines
15 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.InputSystem;
using UnityEngine.UI;
using UnityEngine.XR.Interaction.Toolkit.Samples.StarterAssets;
using TMPro;
using LazyFollow = UnityEngine.XR.Interaction.Toolkit.UI.LazyFollow;
namespace UnityEngine.XR.Templates.MR
{
public struct Goal
{
public GoalManager.OnboardingGoals CurrentGoal;
public bool Completed;
public Goal(GoalManager.OnboardingGoals goal)
{
CurrentGoal = goal;
Completed = false;
}
}
public class GoalManager : MonoBehaviour
{
public enum OnboardingGoals
{
Empty,
FindSurfaces,
TapSurface,
}
Queue<Goal> m_OnboardingGoals;
Goal m_CurrentGoal;
bool m_AllGoalsFinished;
int m_SurfacesTapped;
int m_CurrentGoalIndex = 0;
[Serializable]
class Step
{
[SerializeField]
public GameObject stepObject;
[SerializeField]
public string buttonText;
public bool includeSkipButton;
}
[SerializeField]
List<Step> m_StepList = new List<Step>();
[SerializeField]
public TextMeshProUGUI m_StepButtonTextField;
[SerializeField]
public GameObject m_SkipButton;
[SerializeField]
GameObject m_LearnButton;
[SerializeField]
GameObject m_LearnModal;
[SerializeField]
Button m_LearnModalButton;
[SerializeField]
GameObject m_CoachingUIParent;
[SerializeField]
FadeMaterial m_FadeMaterial;
[SerializeField]
Toggle m_PassthroughToggle;
[SerializeField]
LazyFollow m_GoalPanelLazyFollow;
[SerializeField]
GameObject m_TapTooltip;
[SerializeField]
GameObject m_VideoPlayer;
[SerializeField]
Toggle m_VideoPlayerToggle;
[SerializeField]
ARFeatureController m_FeatureController;
[SerializeField]
ObjectSpawner m_ObjectSpawner;
const int k_NumberOfSurfacesTappedToCompleteGoal = 1;
Vector3 m_TargetOffset = new Vector3(-.5f, -.25f, 1.5f);
void Start()
{
m_OnboardingGoals = new Queue<Goal>();
var welcomeGoal = new Goal(OnboardingGoals.Empty);
var findSurfaceGoal = new Goal(OnboardingGoals.FindSurfaces);
var tapSurfaceGoal = new Goal(OnboardingGoals.TapSurface);
var endGoal = new Goal(OnboardingGoals.Empty);
m_OnboardingGoals.Enqueue(welcomeGoal);
m_OnboardingGoals.Enqueue(findSurfaceGoal);
m_OnboardingGoals.Enqueue(tapSurfaceGoal);
m_OnboardingGoals.Enqueue(endGoal);
m_CurrentGoal = m_OnboardingGoals.Dequeue();
if (m_TapTooltip != null)
m_TapTooltip.SetActive(false);
if (m_VideoPlayer != null)
{
m_VideoPlayer.SetActive(false);
if (m_VideoPlayerToggle != null)
m_VideoPlayerToggle.isOn = false;
}
if (m_FadeMaterial != null)
{
m_FadeMaterial.FadeSkybox(false);
if (m_PassthroughToggle != null)
m_PassthroughToggle.isOn = false;
}
if (m_LearnButton != null)
{
m_LearnButton.GetComponent<Button>().onClick.AddListener(OpenModal); ;
m_LearnButton.SetActive(false);
}
if (m_LearnModal != null)
{
m_LearnModal.transform.localScale = Vector3.zero;
}
if (m_LearnModalButton != null)
{
m_LearnModalButton.onClick.AddListener(CloseModal);
}
if (m_ObjectSpawner == null)
{
#if UNITY_2023_1_OR_NEWER
m_ObjectSpawner = FindAnyObjectByType<ObjectSpawner>();
#else
m_ObjectSpawner = FindObjectOfType<ObjectSpawner>();
#endif
}
if (m_FeatureController == null)
{
#if UNITY_2023_1_OR_NEWER
m_FeatureController = FindAnyObjectByType<ARFeatureController>();
#else
m_FeatureController = FindObjectOfType<ARFeatureController>();
#endif
}
}
void OpenModal()
{
if (m_LearnModal != null)
{
m_LearnModal.transform.localScale = Vector3.one;
}
}
void CloseModal()
{
if (m_LearnModal != null)
{
m_LearnModal.transform.localScale = Vector3.zero;
}
}
void Update()
{
if (!m_AllGoalsFinished)
{
ProcessGoals();
}
// Debug Input
#if UNITY_EDITOR
if (Keyboard.current.spaceKey.wasPressedThisFrame)
{
CompleteGoal();
}
#endif
}
void ProcessGoals()
{
if (!m_CurrentGoal.Completed)
{
switch (m_CurrentGoal.CurrentGoal)
{
case OnboardingGoals.Empty:
m_GoalPanelLazyFollow.positionFollowMode = LazyFollow.PositionFollowMode.Follow;
break;
case OnboardingGoals.FindSurfaces:
m_GoalPanelLazyFollow.positionFollowMode = LazyFollow.PositionFollowMode.Follow;
break;
case OnboardingGoals.TapSurface:
if (m_TapTooltip != null)
{
m_TapTooltip.SetActive(true);
}
m_GoalPanelLazyFollow.positionFollowMode = LazyFollow.PositionFollowMode.None;
break;
}
}
}
void CompleteGoal()
{
if (m_CurrentGoal.CurrentGoal == OnboardingGoals.TapSurface)
m_ObjectSpawner.objectSpawned -= OnObjectSpawned;
// disable tooltips before setting next goal
DisableTooltips();
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);
m_StepButtonTextField.text = m_StepList[m_CurrentGoalIndex].buttonText;
m_SkipButton.SetActive(m_StepList[m_CurrentGoalIndex].includeSkipButton);
}
else
{
m_AllGoalsFinished = true;
ForceEndAllGoals();
}
if (m_CurrentGoal.CurrentGoal == OnboardingGoals.FindSurfaces)
{
if (m_FadeMaterial != null)
m_FadeMaterial.FadeSkybox(true);
if (m_PassthroughToggle != null)
m_PassthroughToggle.isOn = true;
if (m_LearnButton != null)
{
m_LearnButton.SetActive(true);
}
StartCoroutine(TurnOnPlanes(true));
}
else if (m_CurrentGoal.CurrentGoal == OnboardingGoals.TapSurface)
{
if (m_LearnButton != null)
{
m_LearnButton.SetActive(false);
}
m_SurfacesTapped = 0;
m_ObjectSpawner.objectSpawned += OnObjectSpawned;
}
}
public IEnumerator TurnOnPlanes(bool visualize)
{
yield return new WaitForSeconds(1f);
if (m_FeatureController != null)
{
m_FeatureController.TogglePlaneVisualization(visualize);
m_FeatureController.TogglePlanes(true);
}
}
IEnumerator TurnOnARFeatures()
{
if (m_FeatureController == null)
yield return null;
yield return new WaitForSeconds(0.5f);
// We are checking the bounding box count here so that we disable plane visuals so there is no
// visual Z fighting. If the user has not defined any furniture in space setup or the platform
// does not support bounding boxes, we want to enable plane visuals, but disable bounding box visuals.
m_FeatureController.ToggleBoundingBoxes(true);
m_FeatureController.TogglePlanes(true);
// Quick hack for for async await race condition.
// TODO: -- Probably better to listen to trackable change events in the ARFeatureController and update accordingly there
yield return new WaitForSeconds(0.5f);
m_FeatureController.ToggleDebugInfo(false);
// If there are bounding boxes, we want to hide the planes so they don't cause z-fighting.
if (m_FeatureController.HasBoundingBoxes())
{
m_FeatureController.TogglePlaneVisualization(false);
m_FeatureController.ToggleBoundingBoxVisualization(true);
}
else
{
m_FeatureController.ToggleBoundingBoxVisualization(true);
}
m_FeatureController.occlusionManager.SetupManager();
}
void TurnOffARFeatureVisualization()
{
if (m_FeatureController == null)
return;
m_FeatureController.TogglePlaneVisualization(false);
m_FeatureController.ToggleBoundingBoxVisualization(false);
}
void DisableTooltips()
{
if (m_CurrentGoal.CurrentGoal == OnboardingGoals.TapSurface)
{
if (m_TapTooltip != null)
{
m_TapTooltip.SetActive(false);
}
}
}
public void ForceCompleteGoal()
{
CompleteGoal();
}
public void ForceEndAllGoals()
{
m_CoachingUIParent.transform.localScale = Vector3.zero;
TurnOnVideoPlayer();
if (m_VideoPlayerToggle != null)
m_VideoPlayerToggle.isOn = true;
if (m_FadeMaterial != null)
{
m_FadeMaterial.FadeSkybox(true);
if (m_PassthroughToggle != null)
m_PassthroughToggle.isOn = true;
}
if (m_LearnButton != null)
{
m_LearnButton.SetActive(false);
}
if (m_LearnModal != null)
{
m_LearnModal.transform.localScale = Vector3.zero;
}
StartCoroutine(TurnOnARFeatures());
}
public void ResetCoaching()
{
TurnOffARFeatureVisualization();
m_CoachingUIParent.transform.localScale = Vector3.one;
m_OnboardingGoals.Clear();
m_OnboardingGoals = new Queue<Goal>();
var welcomeGoal = new Goal(OnboardingGoals.Empty);
var findSurfaceGoal = new Goal(OnboardingGoals.FindSurfaces);
var tapSurfaceGoal = new Goal(OnboardingGoals.TapSurface);
var endGoal = new Goal(OnboardingGoals.Empty);
m_OnboardingGoals.Enqueue(welcomeGoal);
m_OnboardingGoals.Enqueue(findSurfaceGoal);
m_OnboardingGoals.Enqueue(tapSurfaceGoal);
m_OnboardingGoals.Enqueue(endGoal);
for (int i = 0; i < m_StepList.Count; i++)
{
if (i == 0)
{
m_StepList[i].stepObject.SetActive(true);
m_SkipButton.SetActive(m_StepList[i].includeSkipButton);
m_StepButtonTextField.text = m_StepList[i].buttonText;
}
else
{
m_StepList[i].stepObject.SetActive(false);
}
}
m_CurrentGoal = m_OnboardingGoals.Dequeue();
m_AllGoalsFinished = false;
if (m_TapTooltip != null)
m_TapTooltip.SetActive(false);
if (m_LearnButton != null)
{
m_LearnButton.SetActive(false);
}
if (m_LearnModal != null)
{
m_LearnModal.transform.localScale = Vector3.zero;
}
m_CurrentGoalIndex = 0;
}
void OnObjectSpawned(GameObject spawnedObject)
{
m_SurfacesTapped++;
if (m_CurrentGoal.CurrentGoal == OnboardingGoals.TapSurface && m_SurfacesTapped >= k_NumberOfSurfacesTappedToCompleteGoal)
{
CompleteGoal();
m_GoalPanelLazyFollow.positionFollowMode = LazyFollow.PositionFollowMode.Follow;
}
}
public void TooglePlayer(bool visibility)
{
if (visibility)
{
TurnOnVideoPlayer();
}
else
{
if (m_VideoPlayer.activeSelf)
{
m_VideoPlayer.SetActive(false);
if (m_VideoPlayerToggle.isOn)
m_VideoPlayerToggle.isOn = false;
}
}
}
void TurnOnVideoPlayer()
{
if (m_VideoPlayer.activeSelf)
return;
var follow = m_VideoPlayer.GetComponent<LazyFollow>();
if (follow != null)
follow.rotationFollowMode = LazyFollow.RotationFollowMode.None;
m_VideoPlayer.SetActive(false);
var target = Camera.main.transform;
var targetRotation = target.rotation;
var newTransform = target;
var targetEuler = targetRotation.eulerAngles;
targetRotation = Quaternion.Euler
(
0f,
targetEuler.y,
targetEuler.z
);
newTransform.rotation = targetRotation;
var targetPosition = target.position + newTransform.TransformVector(m_TargetOffset);
m_VideoPlayer.transform.position = targetPosition;
var forward = target.position - m_VideoPlayer.transform.position;
var targetPlayerRotation = forward.sqrMagnitude > float.Epsilon ? Quaternion.LookRotation(forward, Vector3.up) : Quaternion.identity;
targetPlayerRotation *= Quaternion.Euler(new Vector3(0f, 180f, 0f));
var targetPlayerEuler = targetPlayerRotation.eulerAngles;
var currentEuler = m_VideoPlayer.transform.rotation.eulerAngles;
targetPlayerRotation = Quaternion.Euler
(
currentEuler.x,
targetPlayerEuler.y,
currentEuler.z
);
m_VideoPlayer.transform.rotation = targetPlayerRotation;
m_VideoPlayer.SetActive(true);
if (follow != null)
follow.rotationFollowMode = LazyFollow.RotationFollowMode.LookAtWithWorldUp;
}
}
}