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();
}
}
}