Stratasys-450mc-VR/Assets/XRI_Examples/VRWalkthrough/Scripts/WalkthroughStep.cs

289 lines
8.6 KiB
C#

using System;
using System.Collections.Generic;
using UnityEngine.Events;
using UnityEngine.XR.Interaction.Toolkit;
namespace UnityEngine.XR.Content.Walkthrough
{
/// <summary>
/// Contains information needed to process one step of a walkthrough.
/// </summary>
public class WalkthroughStep : MonoBehaviour
{
/// Local method use only -- created here to reduce garbage collection. Collections must be cleared before use
static readonly List<WalkthroughTrigger> s_TriggersToRemove = new List<WalkthroughTrigger>();
[SerializeField]
[Tooltip("Camera target to reposition user.")]
GameObject m_CameraTarget;
[SerializeField]
[Tooltip("The Teleportation Provider used to reposition the user. Usually a component on the XR Origin.")]
TeleportationProvider m_TeleportationProvider;
[SerializeField]
[Tooltip("Optional audio source for voiceover")]
AudioSource m_AudioSource;
[SerializeField]
[Tooltip("Objects to enable when this step is active.")]
List<GameObject> m_Visuals = new List<GameObject>();
#pragma warning disable 649
[SerializeField]
[Tooltip("Actions to call when the step starts.")]
UnityEvent m_OnStepBegin;
[SerializeField]
[Tooltip("Actions to call when the step completes.")]
UnityEvent m_OnStepComplete;
[SerializeField]
[Tooltip("The purpose of this step.")]
string m_Description;
#pragma warning restore 649
[SerializeField]
[Tooltip("If true, this step cannot be skipped until completed at least once.")]
bool m_BlockUntilComplete;
[SerializeField]
[Tooltip("If true, this step will automatically progress when complete - unless explicitly skipped to.")]
bool m_AutoProgressOnComplete = true;
bool m_Started;
bool m_AutoProgressEnabled = true;
bool m_StepInvoked;
Action<bool> m_OnComplete;
GameObject m_Waypoint;
GameObject m_Link;
List<WalkthroughTrigger> m_Triggers = new List<WalkthroughTrigger>();
List<WalkthroughTrigger> m_RemainingTriggers = new List<WalkthroughTrigger>();
/// <summary>
/// The purpose of this step. Appends a (Complete) if complete and normally has triggers.
/// </summary>
public string description => $"{m_Description}{(completed && m_Triggers.Count > 0 ? " (Complete)" : "") }";
public GameObject waypoint
{
get => m_Waypoint;
set => m_Waypoint = value;
}
public GameObject link
{
get => m_Link;
set => m_Link = value;
}
/// <summary>
/// Ensures the step visuals are hidden until active and that all triggers are accounted for
/// </summary>
public void Initialize()
{
if (!m_Started)
SetVisualsState(false);
GetComponents(m_Triggers);
}
/// <summary>
/// Returns true if this step does not currently have any triggers remaining to fire
/// </summary>
public bool canProgress => (!m_BlockUntilComplete || (m_RemainingTriggers.Count == 0));
/// <summary>
/// Returns true if this step does not block, or has been completed at least once.
/// </summary>
public bool canSkip => (!m_BlockUntilComplete || completed);
/// <summary>
/// True if this step's triggers have been activated at least once
/// </summary>
public bool completed { get; private set; }
/// <summary>
/// Makes this step and its triggers the active focus of a walkthrough
/// </summary>
/// <param name="onComplete">Callback to fire when this step's triggers are complete</param>
/// <param name="allowAutoProgress">If this step is allow to auto-progress during this activation</param>
public void StartStep(Action<bool> onComplete, bool allowAutoProgress = true)
{
if (m_Started)
return;
// Autoprogression is enabled only if the step AND walkthrough allow it
m_AutoProgressEnabled = allowAutoProgress && m_AutoProgressOnComplete;
SetVisualsState(true);
SetAudioSource(true);
if (m_Waypoint != null)
{
m_Waypoint.SetActive(false);
}
if (m_CameraTarget != null && m_TeleportationProvider != null)
{
SetCameraPosition();
}
m_OnComplete = onComplete;
m_Started = true;
if (m_Triggers.Count == 0 && m_AutoProgressEnabled)
{
CompleteStep();
return;
}
foreach (var currentTrigger in m_Triggers)
{
if (currentTrigger.ResetTrigger())
m_RemainingTriggers.Add(currentTrigger);
}
if (m_RemainingTriggers.Count == 0)
{
CompleteStep();
return;
}
if (m_RemainingTriggers.Count > 0)
m_AutoProgressEnabled = m_AutoProgressOnComplete;
}
/// <summary>
/// Ends this step being the focus of the current walkthrough
/// </summary>
public void CancelStep()
{
SetVisualsState(false);
SetAudioSource(false);
if (m_Waypoint != null)
{
m_Waypoint.SetActive(true);
}
if (!m_Started)
return;
m_OnComplete = null;
m_Started = false;
m_RemainingTriggers.Clear();
}
void CompleteStep()
{
if (!m_Started)
return;
completed = true;
m_OnComplete?.Invoke(m_AutoProgressEnabled);
m_OnComplete = null;
m_Started = false;
// We disable visuals if the the next step is being activated
if (m_AutoProgressEnabled)
{
SetVisualsState(false);
SetAudioSource(false);
if (m_Waypoint != null)
{
m_Waypoint.SetActive(true);
}
}
m_RemainingTriggers.Clear();
}
void Update()
{
// If this step is running, check remaining triggers. Any triggers that are now met get removed.
// If there are no triggers left, then the step is complete.
if (!m_Started)
return;
if (m_RemainingTriggers.Count == 0)
return;
s_TriggersToRemove.Clear();
foreach (var currentTrigger in m_RemainingTriggers)
{
if (currentTrigger.Check())
s_TriggersToRemove.Add(currentTrigger);
}
foreach (var toRemove in s_TriggersToRemove)
{
m_RemainingTriggers.Remove(toRemove);
}
s_TriggersToRemove.Clear();
if (m_RemainingTriggers.Count == 0)
{
CompleteStep();
return;
}
}
void SetVisualsState(bool enabled)
{
if (m_Visuals != null)
{
foreach (var currentVisual in m_Visuals)
{
if (currentVisual != null)
currentVisual.SetActive(enabled);
}
}
if (m_StepInvoked == enabled)
return;
m_StepInvoked = enabled;
if (enabled && m_OnStepBegin != null)
m_OnStepBegin.Invoke();
if (!enabled && m_OnStepComplete != null)
m_OnStepComplete.Invoke();
}
void SetAudioSource(bool enabled)
{
if (m_AudioSource != null)
{
if (enabled)
{
m_AudioSource.Play();
}
else
{
m_AudioSource.Stop();
}
}
}
void SetCameraPosition()
{
TeleportRequest request = new TeleportRequest()
{
requestTime = Time.time,
matchOrientation = MatchOrientation.TargetUpAndForward,
destinationPosition = m_CameraTarget.transform.position,
destinationRotation = m_CameraTarget.transform.rotation
};
m_TeleportationProvider.QueueTeleportRequest(request);
}
}
}