using System; using UnityEngine.InputSystem; using UnityEngine.XR.Interaction.Toolkit.Utilities; using UnityEngine.XR.Interaction.Toolkit.Utilities.Internal; namespace UnityEngine.XR.Interaction.Toolkit.Inputs.Readers { /// /// Base interface for all input value readers. /// /// /// This empty interface is needed to allow the RequireInterface attribute to be used. /// Generic attributes aren't supported until C# 11, so we can't use a typed interface with the RequireInterface attribute yet. /// public interface IXRInputValueReader { } /// /// Interface which allows for callers to read the current value from an input source. /// /// Type of the value to read, such as or . /// public interface IXRInputValueReader : IXRInputValueReader where TValue : struct { /// /// Read the current value from the input source. /// /// Returns the current value from the input source. May return default(TValue) if unused or no source is set. TValue ReadValue(); /// /// Try to read the current value from the input source. /// /// When this method returns, contains the current value from the input source. May return default(TValue) if unused or no source is set. /// Returns if the current value was able to be read (and for actions, also if in progress). /// /// You can use the return value of this method instead of only using in order to avoid doing /// any work when the input action is not in progress, such as when the control is not actuated. /// This can be useful for performance reasons. ///
/// If an input processor on an input action returns a different value from the default /// when the input action is not in progress, the returned by /// this method may not be default(TValue) as is typically the case for Try- methods. If you need /// to support processors that return a different value from the default when the control is not actuated, /// you should use instead of using the return value of this method to skip input handling. ///
bool TryReadValue(out TValue value); } /// /// Base abstract class for a serializable input value reader without typed code. /// /// public abstract class XRInputValueReader { /// /// The mode that determines from which input source the value is read from. /// /// public enum InputSourceMode { /// /// The input is explicitly not used. /// Set to this mode to avoid any performance cost when the input should be ignored. /// Unused, /// /// The input is read from an input action defined and serialized with this behavior. /// /// /// /// InputAction, /// /// The input is read from an input action defined in the project. /// This is the default mode. /// InputActionReference, /// /// The input is read from an object reference that implements . /// ObjectReference, /// /// The input is returned from manually set values, either in the Inspector window or at runtime through scripting. /// /// ManualValue, } [SerializeField] // ReSharper disable once InconsistentNaming -- treat like a private field private protected InputSourceMode m_InputSourceMode = InputSourceMode.InputActionReference; /// /// The mode that determines from which input source the value is read from. /// By default this is set to to read from an input action /// defined in the project. /// /// public InputSourceMode inputSourceMode { get => m_InputSourceMode; set => m_InputSourceMode = value; } [SerializeField] // ReSharper disable once InconsistentNaming -- treat like a private field private protected InputAction m_InputAction; /// /// The directly serialized embedded input action that is read /// when the mode is set to . /// public InputAction inputAction { get => m_InputAction; set => m_InputAction = value; } [SerializeField] InputActionReference m_InputActionReference; /// /// The reference to an input action that is read /// when the mode is set to . /// public InputActionReference inputActionReference { get => m_InputActionReference; set => m_InputActionReference = value; } readonly UnityObjectReferenceCache m_InputActionReferenceCache = new UnityObjectReferenceCache(); /// /// Initializes and returns an instance of . /// protected XRInputValueReader() { } /// /// Initializes and returns an instance of . /// /// The directly serialized embedded input action. /// The initial input source mode. protected XRInputValueReader(InputAction inputAction, InputSourceMode inputSourceMode) { m_InputAction = inputAction; m_InputSourceMode = inputSourceMode; } /// /// Enable the directly serialized embedded input action if the mode is set to . /// /// public void EnableDirectActionIfModeUsed() { if (m_InputSourceMode == InputSourceMode.InputAction) m_InputAction.Enable(); } /// /// Disable the directly serialized embedded input action if the mode is set to . /// /// public void DisableDirectActionIfModeUsed() { if (m_InputSourceMode == InputSourceMode.InputAction) m_InputAction.Disable(); } /// /// A fast but unsafe method for getting the without doing a Unity Object alive check after the first time. /// The purpose is to avoid the overhead of the Unity Object alive check when it is known that the reference is alive, /// but does not have the rigor of detecting when the asset is deleted after the first check, which should be very rare. /// This will handle the user modifying the field in the Inspector window by invalidating the cached version. /// /// The input action reference or actual . /// Returns if the reference is not null. Otherwise, returns . private protected bool TryGetInputActionReference(out InputActionReference reference) => m_InputActionReferenceCache.TryGet(m_InputActionReference, out reference); } /// /// Serializable typed input value reader that can read the current value from an input source. /// Behaviors can declare a field of this type to allow them to read input from an input action or any other source. /// /// Type of the value to read, such as or . [Serializable] public class XRInputValueReader : XRInputValueReader, IXRInputValueReader where TValue : struct { readonly struct BypassScope : IDisposable { readonly XRInputValueReader m_Reader; public BypassScope(XRInputValueReader reader) { m_Reader = reader; m_Reader.m_CallingBypass = true; } public void Dispose() { m_Reader.m_CallingBypass = false; } } [SerializeField] [RequireInterface(typeof(IXRInputValueReader))] Object m_ObjectReferenceObject; [SerializeField] TValue m_ManualValue; /// /// The manually set value that is returned /// when the mode is set to . /// public TValue manualValue { get => m_ManualValue; set => m_ManualValue = value; } /// /// A runtime bypass that can be used to override the input value returned by this class. /// public IXRInputValueReader bypass { get; set; } /// /// Whether this class is calling into the bypass. /// The purpose of this is to allow the bypass to read the raw value of this class using the public methods /// without recursively calling into the bypass again. /// bool m_CallingBypass; readonly UnityObjectReferenceCache, Object> m_ObjectReference = new UnityObjectReferenceCache, Object>(); /// /// Initializes and returns an instance of . /// public XRInputValueReader() { } /// /// Initializes and returns an instance of . /// /// The name of the directly serialized embedded input action. /// The initial input source mode. public XRInputValueReader(string name = null, InputSourceMode inputSourceMode = InputSourceMode.InputActionReference) : base(InputActionUtility.CreateValueAction(typeof(TValue), name), inputSourceMode) { } /// /// Gets the object reference that is used as the input source /// when the mode is set to . /// /// Returns the typed object reference, which may be . public IXRInputValueReader GetObjectReference() { return m_ObjectReference.Get(m_ObjectReferenceObject); } /// /// Sets the object reference that is used as the input source /// when the mode is set to . /// /// The typed object reference. /// /// If the argument is to be serialized, it must be a Unity type. /// public void SetObjectReference(IXRInputValueReader value) { m_ObjectReference.Set(ref m_ObjectReferenceObject, value); } /// public TValue ReadValue() { if (bypass != null && !m_CallingBypass) { using (new BypassScope(this)) { return bypass.ReadValue(); } } switch (m_InputSourceMode) { case InputSourceMode.Unused: default: return default; case InputSourceMode.InputAction: return ReadValue(m_InputAction); case InputSourceMode.InputActionReference: return TryGetInputActionReference(out var reference) ? ReadValue(reference.action) : default; case InputSourceMode.ObjectReference: { var objectReference = GetObjectReference(); return objectReference?.ReadValue() ?? default; } case InputSourceMode.ManualValue: return m_ManualValue; } } /// public bool TryReadValue(out TValue value) { if (bypass != null && !m_CallingBypass) { using (new BypassScope(this)) { return bypass.TryReadValue(out value); } } switch (inputSourceMode) { case InputSourceMode.Unused: default: value = default; return false; case InputSourceMode.InputAction: return TryReadValue(m_InputAction, out value); case InputSourceMode.InputActionReference: if (TryGetInputActionReference(out var reference)) return TryReadValue(reference.action, out value); value = default; return false; case InputSourceMode.ObjectReference: { var objectReference = GetObjectReference(); if (objectReference != null) return objectReference.TryReadValue(out value); value = default; return false; } case InputSourceMode.ManualValue: value = m_ManualValue; return true; } } static TValue ReadValue(InputAction action) { return action?.ReadValue() ?? default; } static bool TryReadValue(InputAction action, out TValue value) { if (action == null) { value = default; return false; } value = action.ReadValue(); return action.IsInProgress(); } } }