using UnityEngine; using System.Collections; using System; using UnityEngine.Events; using UnityEngine.XR.Interaction.Toolkit; using NaughtyAttributes; public class StepScript : MonoBehaviour { [Header("Step Info")] /// /// Add a description to the step to explain what is supposed to be /// [Tooltip("Add a description to the step to explain what is supposed to be")] [TextArea(3, 10)] public string description; /// /// If true, the game object is enabled at game start /// [Tooltip("If true, the game object is visible at game start")] public bool isEnabledBeforeStart = true; /// /// If true, the game object will stay enabled after the step is completed /// [Tooltip("If true, the game object will stay visible after the step is completed")] public bool isEnabledAfterEnd = true; /// /// Refresh rate of the step, used to update the step /// public float RefreshRate = 0.2f; [HorizontalLine(1, EColor.Blue)] /// /// If true, the step is completed at start, so no other completion event is needed /// [Tooltip("If true, the step is completed at start, so no other completion event is needed. It can still use pre and post callbacks")] public bool startCompleted = false; /// /// If true, the step is completed when the item is grabbed it needs the XRGrabInteractable component /// to detect when the item is grabbed. At least one completion event must be set to avoid an infinite loop /// [Tooltip("If true, the step is completed when the item is grabbed it needs the XRGrabInteractable component " + "to detect when the item is grabbed. At least one completion event must be set to avoid an infinite loop")] [InfoBox("Use this if no other complention events are added", EInfoBoxType.Warning)] [HideIf("startCompleted")] public bool completeOnItemGrabbed = true; /// /// These will be subscribed, so that they will act as completion events /// //[Tooltip("These will be subscribed, so that they will act as completion events. They can be used with the rab event.")] //[HideIf("startCompleted")] //public UnityEvent StepCompletionEvents; [HorizontalLine(1, EColor.Blue)] [Header("Callbacks")] /// /// If true, run the specified callbacks when the step starts /// [Tooltip("If true, run the specified callbacks when the step starts")] public bool useOnStart = false; /// /// Event called when the step starts /// [ShowIf("useOnStart")] [Tooltip("Event called when the step starts, before the update loop")] public UnityEvent OnStart; /// /// If true, run the specified callbacks when the step is updated /// [HideIf("startCompleted")] [Tooltip("This run the main callbacks that run until the step is complete by another event. The refresh rate is the same of the HapticManager")] public UnityEvent OnUpdate; /// /// If true, run the specified callbacks when the step is complete /// [Tooltip("If true, run the specified callbacks when the step is complete")] public bool useOnEnd = false; /// /// If true, run the specified callbacks when the step is complete /// /// [ShowIf("useOnEnd")] [Tooltip("If true, run the specified callbacks when the step is complete")] public UnityEvent OnEnd; /// /// If true, once the OnEnd callback are called, a delay is started, this allows to /// run another set of callback after these /// [Tooltip("If true, once the OnEnd callback are called, a delay is started, this allows to " + "run another set of callback after these")] public bool useDelayedEnd = false; /// /// Delay after the list ends, used to wait for delayed effects /// [ShowIf("useDelayedEnd")] [MinValue(0.0f), MaxValue(60)] [Tooltip("Delay after the list ends, used to wait for delayed effects, runned before the OnEndPostDelay")] public float endTimeDurationSeconds = 5f; /// /// Event called after the list ends, after a delay /// [ShowIf("useDelayedEnd")] [Tooltip("Event called after the list ends, after a delay")] public UnityEvent OnEndPostDelay; /// /// Flag to check if the step is complete /// private bool isStepComplete; /// /// Reference to the XRGrabInteractable component, used to detect when the item is grabbed /// private XRGrabInteractable grabInteractable; void Start() { if(startCompleted) { completeOnItemGrabbed = false; OnUpdate = null; } if (gameObject == null) { throw new Exception("This script cannot access a gameObject"); } else if (gameObject.transform == null) { throw new Exception("This script cannot access a gameObject transform"); } else if (completeOnItemGrabbed) { grabInteractable = gameObject.GetComponent(); if (grabInteractable == null) { throw new Exception("This script cannot access a XRGrabInteractable component"); } } gameObject.SetActive(isEnabledBeforeStart); } /// /// Run the step, this is called by the StepsListScript public IEnumerator StepRun() { Debug.Log("Step: " + name + " started"); isStepComplete = startCompleted; gameObject.SetActive(true); if (useOnStart) { OnStart.Invoke(); } if (!startCompleted) { grabInteractable = gameObject.GetComponent(); SubscribeToCompletionEvents(); do { OnUpdate.Invoke(); // Debug.Log("Step: " + name + " updated"); yield return new WaitForSeconds(RefreshRate); } while (!isStepComplete); UnSubscribeToCompletionEvents(); } if (useOnEnd) { OnEnd.Invoke(); } if (useDelayedEnd) { yield return new WaitForSeconds(endTimeDurationSeconds); OnEndPostDelay.Invoke(); } gameObject.SetActive(isEnabledAfterEnd); Debug.Log("Step complete"); } /// /// Callback for to set the step as complete from any event that calls it /// /// public void StepCompletionListener(SelectEnterEventArgs arg0) { isStepComplete = true; Debug.Log("Step completed by grabbing an item"); } /// /// Subscribe to the completion events /// private void SubscribeToCompletionEvents() { // subscribe to the grab event, if needed if (completeOnItemGrabbed) { grabInteractable.selectEntered.AddListener(StepCompletionListener); } } /// /// Unsubscribe to the completion events /// private void UnSubscribeToCompletionEvents() { if (completeOnItemGrabbed) { grabInteractable.selectEntered.RemoveListener(StepCompletionListener); } } }