course_heia_techimm_haptics.../Assets/Resources/Scripts/StepsLink/StepScript.cs
2025-07-12 18:59:18 +02:00

239 lines
No EOL
7.5 KiB
C#

using UnityEngine;
using System.Collections;
using System;
using UnityEngine.Events;
using UnityEngine.XR.Interaction.Toolkit;
using NaughtyAttributes;
public class StepScript : MonoBehaviour
{
[Header("Step Info")]
/// <summary>
/// Add a description to the step to explain what is supposed to be
/// </summary>
[Tooltip("Add a description to the step to explain what is supposed to be")]
[TextArea(3, 10)]
public string description;
/// <summary>
/// If true, the game object is enabled at game start
/// </summary>
[Tooltip("If true, the game object is visible at game start")]
public bool isEnabledBeforeStart = true;
/// <summary>
/// If true, the game object will stay enabled after the step is completed
/// </summary>
[Tooltip("If true, the game object will stay visible after the step is completed")]
public bool isEnabledAfterEnd = true;
/// <summary>
/// Refresh rate of the step, used to update the step
/// </summary>
public float RefreshRate = 0.2f;
[HorizontalLine(1, EColor.Blue)]
/// <summary>
/// If true, the step is completed at start, so no other completion event is needed
/// </summary>
[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;
/// <summary>
/// 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
/// </summary>
[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;
/// <summary>
/// These will be subscribed, so that they will act as completion events
/// </summary>
//[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")]
/// <summary>
/// If true, run the specified callbacks when the step starts
/// </summary>
[Tooltip("If true, run the specified callbacks when the step starts")]
public bool useOnStart = false;
/// <summary>
/// Event called when the step starts
/// </summary>
[ShowIf("useOnStart")]
[Tooltip("Event called when the step starts, before the update loop")]
public UnityEvent OnStart;
/// <summary>
/// If true, run the specified callbacks when the step is updated
/// </summary>
[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;
/// <summary>
/// If true, run the specified callbacks when the step is complete
/// </summary>
[Tooltip("If true, run the specified callbacks when the step is complete")]
public bool useOnEnd = false;
/// <summary>
/// If true, run the specified callbacks when the step is complete
/// </summary>
/// <returns></returns>
[ShowIf("useOnEnd")]
[Tooltip("If true, run the specified callbacks when the step is complete")]
public UnityEvent OnEnd;
/// <summary>
/// If true, once the OnEnd callback are called, a delay is started, this allows to
/// run another set of callback after these
/// </summary>
[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;
/// <summary>
/// Delay after the list ends, used to wait for delayed effects
/// </summary>
[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;
/// <summary>
/// Event called after the list ends, after a delay
/// </summary>
[ShowIf("useDelayedEnd")]
[Tooltip("Event called after the list ends, after a delay")]
public UnityEvent OnEndPostDelay;
/// <summary>
/// Flag to check if the step is complete
/// </summary>
private bool isStepComplete;
/// <summary>
/// Reference to the XRGrabInteractable component, used to detect when the item is grabbed
/// </summary>
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<XRGrabInteractable>();
if (grabInteractable == null)
{
throw new Exception("This script cannot access a XRGrabInteractable component");
}
}
gameObject.SetActive(isEnabledBeforeStart);
}
/// <summary>
/// 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<XRGrabInteractable>();
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");
}
/// <summary>
/// Callback for to set the step as complete from any event that calls it
/// </summary>
/// <param name="arg0"></param>
public void StepCompletionListener(SelectEnterEventArgs arg0)
{
isStepComplete = true;
Debug.Log("Step completed by grabbing an item");
}
/// <summary>
/// Subscribe to the completion events
/// </summary>
private void SubscribeToCompletionEvents()
{
// subscribe to the grab event, if needed
if (completeOnItemGrabbed)
{
grabInteractable.selectEntered.AddListener(StepCompletionListener);
}
}
/// <summary>
/// Unsubscribe to the completion events
/// </summary>
private void UnSubscribeToCompletionEvents()
{
if (completeOnItemGrabbed)
{
grabInteractable.selectEntered.RemoveListener(StepCompletionListener);
}
}
}