VR4Medical/ICI/Library/PackageCache/com.unity.xr.interaction.toolkit@42ef3600567b/Runtime/Feedback/SimpleAudioFeedback.cs
2025-07-29 13:45:50 +03:00

370 lines
12 KiB
C#

using System.Diagnostics;
using UnityEngine.XR.Interaction.Toolkit.Interactables;
using UnityEngine.XR.Interaction.Toolkit.Interactors;
using UnityEngine.XR.Interaction.Toolkit.Utilities;
using UnityEngine.XR.Interaction.Toolkit.Utilities.Internal;
namespace UnityEngine.XR.Interaction.Toolkit.Feedback
{
/// <summary>
/// Component that responds to select and hover events by playing audio clips.
/// </summary>
/// <seealso cref="SimpleHapticFeedback"/>
[AddComponentMenu("XR/Feedback/Simple Audio Feedback", 11)]
[HelpURL(XRHelpURLConstants.k_SimpleAudioFeedback)]
public class SimpleAudioFeedback : MonoBehaviour
{
[SerializeField]
[RequireInterface(typeof(IXRInteractor))]
Object m_InteractorSourceObject;
[SerializeField]
AudioSource m_AudioSource;
/// <summary>
/// The Audio Source component to use to play audio clips.
/// </summary>
public AudioSource audioSource
{
get => m_AudioSource;
set => m_AudioSource = value;
}
[SerializeField]
bool m_PlaySelectEntered;
/// <summary>
/// Whether to play a sound when the interactor starts selecting an interactable.
/// </summary>
public bool playSelectEntered
{
get => m_PlaySelectEntered;
set => m_PlaySelectEntered = value;
}
[SerializeField]
AudioClip m_SelectEnteredClip;
/// <summary>
/// The audio clip to play when the interactor starts selecting an interactable.
/// </summary>
public AudioClip selectEnteredClip
{
get => m_SelectEnteredClip;
set => m_SelectEnteredClip = value;
}
[SerializeField]
bool m_PlaySelectExited;
/// <summary>
/// Whether to play a sound when the interactor stops selecting an interactable without being canceled.
/// </summary>
public bool playSelectExited
{
get => m_PlaySelectExited;
set => m_PlaySelectExited = value;
}
[SerializeField]
AudioClip m_SelectExitedClip;
/// <summary>
/// The audio clip to play when the interactor stops selecting an interactable without being canceled.
/// </summary>
public AudioClip selectExitedClip
{
get => m_SelectExitedClip;
set => m_SelectExitedClip = value;
}
[SerializeField]
bool m_PlaySelectCanceled;
/// <summary>
/// Whether to play a sound when the interactor stops selecting an interactable due to being canceled.
/// </summary>
public bool playSelectCanceled
{
get => m_PlaySelectCanceled;
set => m_PlaySelectCanceled = value;
}
[SerializeField]
AudioClip m_SelectCanceledClip;
/// <summary>
/// The audio clip to play when the interactor stops selecting an interactable due to being canceled.
/// </summary>
public AudioClip selectCanceledClip
{
get => m_SelectCanceledClip;
set => m_SelectCanceledClip = value;
}
[SerializeField]
bool m_PlayHoverEntered;
/// <summary>
/// Whether to play a sound when the interactor starts hovering over an interactable.
/// </summary>
public bool playHoverEntered
{
get => m_PlayHoverEntered;
set => m_PlayHoverEntered = value;
}
[SerializeField]
AudioClip m_HoverEnteredClip;
/// <summary>
/// The audio clip to play when the interactor starts hovering over an interactable.
/// </summary>
public AudioClip hoverEnteredClip
{
get => m_HoverEnteredClip;
set => m_HoverEnteredClip = value;
}
[SerializeField]
bool m_PlayHoverExited;
/// <summary>
/// Whether to play a sound when the interactor stops hovering over an interactable without being canceled.
/// </summary>
public bool playHoverExited
{
get => m_PlayHoverExited;
set => m_PlayHoverExited = value;
}
[SerializeField]
AudioClip m_HoverExitedClip;
/// <summary>
/// The audio clip to play when the interactor stops hovering over an interactable without being canceled.
/// </summary>
public AudioClip hoverExitedClip
{
get => m_HoverExitedClip;
set => m_HoverExitedClip = value;
}
[SerializeField]
bool m_PlayHoverCanceled;
/// <summary>
/// Whether to play a sound when the interactor stops hovering over an interactable due to being canceled.
/// </summary>
public bool playHoverCanceled
{
get => m_PlayHoverCanceled;
set => m_PlayHoverCanceled = value;
}
[SerializeField]
AudioClip m_HoverCanceledClip;
/// <summary>
/// The audio clip to play when the interactor stops hovering over an interactable due to being canceled.
/// </summary>
public AudioClip hoverCanceledClip
{
get => m_HoverCanceledClip;
set => m_HoverCanceledClip = value;
}
[SerializeField]
bool m_AllowHoverAudioWhileSelecting;
/// <summary>
/// Whether to allow hover audio to play while the interactor is selecting an interactable.
/// </summary>
public bool allowHoverAudioWhileSelecting
{
get => m_AllowHoverAudioWhileSelecting;
set => m_AllowHoverAudioWhileSelecting = value;
}
readonly UnityObjectReferenceCache<IXRInteractor, Object> m_InteractorSource = new UnityObjectReferenceCache<IXRInteractor, Object>();
/// <summary>
/// See <see cref="MonoBehaviour"/>.
/// </summary>
[Conditional("UNITY_EDITOR")]
protected void Reset()
{
#if UNITY_EDITOR
m_InteractorSourceObject = GetComponentInParent<IXRInteractor>(true) as Object;
m_AudioSource = GetComponent<AudioSource>();
#endif
}
/// <summary>
/// See <see cref="MonoBehaviour"/>.
/// </summary>
protected void Awake()
{
if (m_InteractorSourceObject == null)
m_InteractorSourceObject = GetComponentInParent<IXRInteractor>(true) as Object;
if (m_PlaySelectEntered || m_PlaySelectExited || m_PlaySelectCanceled ||
m_PlayHoverEntered || m_PlayHoverExited || m_PlayHoverCanceled)
{
if (m_AudioSource == null)
CreateAudioSource();
}
}
/// <summary>
/// See <see cref="MonoBehaviour"/>.
/// </summary>
protected void OnEnable()
{
Subscribe(GetInteractorSource());
}
/// <summary>
/// See <see cref="MonoBehaviour"/>.
/// </summary>
protected void OnDisable()
{
Unsubscribe(GetInteractorSource());
}
/// <summary>
/// Gets the interactor this behavior should subscribe to for events.
/// </summary>
/// <returns>Returns the interactor this behavior should subscribe to for events.</returns>
/// <seealso cref="SetInteractorSource"/>
public IXRInteractor GetInteractorSource()
{
return m_InteractorSource.Get(m_InteractorSourceObject);
}
/// <summary>
/// Sets the interactor this behavior should subscribe to for events.
/// </summary>
/// <param name="interactor">The interactor this behavior should subscribe to for events.</param>
/// <remarks>
/// This also sets the serialized field to the given interactor as a Unity Object.
/// </remarks>
/// <seealso cref="GetInteractorSource"/>
public void SetInteractorSource(IXRInteractor interactor)
{
if (Application.isPlaying && isActiveAndEnabled)
Unsubscribe(m_InteractorSource.Get(m_InteractorSourceObject));
m_InteractorSource.Set(ref m_InteractorSourceObject, interactor);
if (Application.isPlaying && isActiveAndEnabled)
Subscribe(interactor);
}
/// <summary>
/// Play the given audio clip.
/// </summary>
/// <param name="clip">The audio clip to play.</param>
protected void PlayAudio(AudioClip clip)
{
if (clip == null)
return;
if (m_AudioSource == null)
CreateAudioSource();
m_AudioSource.PlayOneShot(clip);
}
void CreateAudioSource()
{
if (!TryGetComponent(out m_AudioSource))
m_AudioSource = gameObject.AddComponent<AudioSource>();
m_AudioSource.loop = false;
m_AudioSource.playOnAwake = false;
}
void Subscribe(IXRInteractor interactor)
{
if (interactor == null || (interactor is Object interactorObject && interactorObject == null))
return;
if (interactor is IXRSelectInteractor selectInteractor)
{
selectInteractor.selectEntered.AddListener(OnSelectEntered);
selectInteractor.selectExited.AddListener(OnSelectExited);
}
if (interactor is IXRHoverInteractor hoverInteractor)
{
hoverInteractor.hoverEntered.AddListener(OnHoverEntered);
hoverInteractor.hoverExited.AddListener(OnHoverExited);
}
}
void Unsubscribe(IXRInteractor interactor)
{
if (interactor == null || (interactor is Object interactorObject && interactorObject == null))
return;
if (interactor is IXRSelectInteractor selectInteractor)
{
selectInteractor.selectEntered.RemoveListener(OnSelectEntered);
selectInteractor.selectExited.RemoveListener(OnSelectExited);
}
if (interactor is IXRHoverInteractor hoverInteractor)
{
hoverInteractor.hoverEntered.RemoveListener(OnHoverEntered);
hoverInteractor.hoverExited.RemoveListener(OnHoverExited);
}
}
void OnSelectEntered(SelectEnterEventArgs args)
{
if (m_PlaySelectEntered)
PlayAudio(m_SelectEnteredClip);
}
void OnSelectExited(SelectExitEventArgs args)
{
if (m_PlaySelectCanceled && args.isCanceled)
PlayAudio(m_SelectCanceledClip);
if (m_PlaySelectExited && !args.isCanceled)
PlayAudio(m_SelectExitedClip);
}
void OnHoverEntered(HoverEnterEventArgs args)
{
if (m_PlayHoverEntered && IsHoverAudioAllowed(args.interactorObject, args.interactableObject))
PlayAudio(m_HoverEnteredClip);
}
void OnHoverExited(HoverExitEventArgs args)
{
if (!IsHoverAudioAllowed(args.interactorObject, args.interactableObject))
return;
if (m_PlayHoverCanceled && args.isCanceled)
PlayAudio(m_HoverCanceledClip);
if (m_PlayHoverExited && !args.isCanceled)
PlayAudio(m_HoverExitedClip);
}
bool IsHoverAudioAllowed(IXRInteractor interactor, IXRInteractable interactable)
{
return m_AllowHoverAudioWhileSelecting || !IsSelecting(interactor, interactable);
}
static bool IsSelecting(IXRInteractor interactor, IXRInteractable interactable)
{
return interactor is IXRSelectInteractor selectInteractor &&
interactable is IXRSelectInteractable selectable &&
selectInteractor.IsSelecting(selectable);
}
}
}