This commit is contained in:
2025-01-17 13:10:42 +01:00
commit 4536213c91
15115 changed files with 1442174 additions and 0 deletions

View File

@@ -0,0 +1,121 @@
using System;
using UnityEngine.InputSystem.LowLevel;
////REVIEW: should we keep an explicit playback status? ATM calling ResumeHaptics() will re-issue last set motor speed regardless of pause state
namespace UnityEngine.InputSystem.Haptics
{
/// <summary>
/// Common implementation of dual motor rumbling.
/// </summary>
/// <remarks>
/// This struct is meant for use in devices that implement <see cref="IDualMotorRumble"/>.
/// </remarks>
internal struct DualMotorRumble
{
/// <summary>
/// Normalized [0..1] speed of the low-frequency (usually left) motor.
/// </summary>
/// <value>Speed of left motor.</value>
public float lowFrequencyMotorSpeed { get; private set; }
/// <summary>
/// Normalized [0..1] speed of the high-frequency (usually right) motor.
/// </summary>
/// <value>Speed of right motor.</value>
public float highFrequencyMotorSpeed { get; private set; }
/// <summary>
/// Whether either of the motors is currently set to non-zero speeds.
/// </summary>
/// <value>True if the motors are currently turned on.</value>
/// <remarks>
/// Does not take pausing into account, i.e. <see cref="lowFrequencyMotorSpeed"/> and/or
/// <see cref="highFrequencyMotorSpeed"/> may be non-zero but haptics on the device
/// may actually be paused with <see cref="PauseHaptics"/>.
/// </remarks>
public bool isRumbling =>
!Mathf.Approximately(lowFrequencyMotorSpeed, 0f)
|| !Mathf.Approximately(highFrequencyMotorSpeed, 0f);
/// <summary>
/// Reset motor speeds to zero but retain current values for <see cref="lowFrequencyMotorSpeed"/>
/// and <see cref="highFrequencyMotorSpeed"/>.
/// </summary>
/// <param name="device">Device to send command to.</param>
/// <exception cref="ArgumentNullException"><paramref name="device"/> is null.</exception>
public void PauseHaptics(InputDevice device)
{
if (device == null)
throw new ArgumentNullException("device");
if (!isRumbling)
return;
var command = DualMotorRumbleCommand.Create(0f, 0f);
device.ExecuteCommand(ref command);
}
/// <summary>
/// Resume haptics by setting motor speeds to the current values of <see cref="lowFrequencyMotorSpeed"/>
/// and <see cref="highFrequencyMotorSpeed"/>.
/// </summary>
/// <param name="device">Device to send command to.</param>
/// <exception cref="ArgumentNullException"><paramref name="device"/> is null.</exception>
public void ResumeHaptics(InputDevice device)
{
if (device == null)
throw new ArgumentNullException("device");
if (!isRumbling)
return;
SetMotorSpeeds(device, lowFrequencyMotorSpeed, highFrequencyMotorSpeed);
}
/// <summary>
/// Reset haptics by setting both <see cref="lowFrequencyMotorSpeed"/> and <see cref="highFrequencyMotorSpeed"/>
/// to zero.
/// </summary>
/// <param name="device">Device to send command to.</param>
/// <exception cref="ArgumentNullException"><paramref name="device"/> is null.</exception>
public void ResetHaptics(InputDevice device)
{
if (device == null)
throw new ArgumentNullException("device");
if (!isRumbling)
return;
SetMotorSpeeds(device, 0.0f, 0.0f);
}
/// <summary>
/// Set the speed of the low-frequency (usually left) and high-frequency (usually right) motor
/// on <paramref name="device"/>. Updates <see cref="lowFrequencyMotorSpeed"/> and
/// <see cref="highFrequencyMotorSpeed"/>.
/// </summary>
/// <param name="device">Device to send command to.</param>
/// <param name="lowFrequency">Speed of the low-frequency (left) motor. Normalized [0..1] value
/// with 1 indicating maximum speed and 0 indicating the motor is turned off. Will automatically
/// be clamped into range.</param>
/// <param name="highFrequency">Speed of the high-frequency (right) motor. Normalized [0..1] value
/// with 1 indicating maximum speed and 0 indicating the motor is turned off. Will automatically
/// be clamped into range.</param>
/// <remarks>
/// Sends <see cref="DualMotorRumbleCommand"/> to <paramref name="device"/>.
/// </remarks>
/// <exception cref="ArgumentNullException"><paramref name="device"/> is null.</exception>
public void SetMotorSpeeds(InputDevice device, float lowFrequency, float highFrequency)
{
if (device == null)
throw new ArgumentNullException("device");
lowFrequencyMotorSpeed = Mathf.Clamp(lowFrequency, 0.0f, 1.0f);
highFrequencyMotorSpeed = Mathf.Clamp(highFrequency, 0.0f, 1.0f);
var command = DualMotorRumbleCommand.Create(lowFrequencyMotorSpeed, highFrequencyMotorSpeed);
device.ExecuteCommand(ref command);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e4a1c749d40b4bf985d6b32d7db339bb
timeCreated: 1517012194

View File

@@ -0,0 +1,37 @@
using System.Runtime.InteropServices;
using UnityEngine.InputSystem.Utilities;
namespace UnityEngine.InputSystem.LowLevel
{
[StructLayout(LayoutKind.Explicit, Size = kSize)]
internal struct DualMotorRumbleCommand : IInputDeviceCommandInfo
{
public static FourCC Type { get { return new FourCC('R', 'M', 'B', 'L'); } }
internal const int kSize = InputDeviceCommand.kBaseCommandSize + sizeof(float) * 2;
[FieldOffset(0)]
public InputDeviceCommand baseCommand;
[FieldOffset(InputDeviceCommand.kBaseCommandSize)]
public float lowFrequencyMotorSpeed;
[FieldOffset(InputDeviceCommand.kBaseCommandSize + 4)]
public float highFrequencyMotorSpeed;
public FourCC typeStatic
{
get { return Type; }
}
public static DualMotorRumbleCommand Create(float lowFrequency, float highFrequency)
{
return new DualMotorRumbleCommand
{
baseCommand = new InputDeviceCommand(Type, kSize),
lowFrequencyMotorSpeed = lowFrequency,
highFrequencyMotorSpeed = highFrequency
};
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ee46ea662a1d4d2397d0b3a45baf67ef
timeCreated: 1517015642

View File

@@ -0,0 +1,30 @@
namespace UnityEngine.InputSystem.Haptics
{
/// <summary>
/// A simple haptics interface that allows to control two motors individually.
/// </summary>
/// <remarks>
/// Dual-motor control is most common on gamepads (see <see cref="Gamepad"/>) such as
/// Xbox and PlayStation controllers.
/// </remarks>
public interface IDualMotorRumble : IHaptics
{
/// <summary>
/// Set the motor speeds of the low-frequency (usually on the left) and high-frequency
/// (usually on the right) motors.
/// </summary>
/// <param name="lowFrequency">Speed of the low-frequency (left) motor. Normalized [0..1] value
/// with 1 indicating maximum speed and 0 indicating the motor is turned off. Will automatically
/// be clamped into range.</param>
/// <param name="highFrequency">Speed of the high-frequency (right) motor. Normalized [0..1] value
/// with 1 indicating maximum speed and 0 indicating the motor is turned off. Will automatically
/// be clamped into range.</param>
/// <remarks>
/// Note that hardware will put limits on the level of control you have over the motors.
/// Rumbling the motors at maximum speed for an extended period of time may cause them to turn
/// off for some time to prevent overheating. Also, how quickly the motors react and how often
/// the speed can be updated will depend on the hardware and drivers.
/// </remarks>
void SetMotorSpeeds(float lowFrequency, float highFrequency);
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6982a6ffcdbf47a6aa68fd4418622395
timeCreated: 1516644451

View File

@@ -0,0 +1,81 @@
////REVIEW: Devices usually will automatically shut down haptics if they haven't received a haptics command in some time.
//// How should we deal with that? Should haptics automatically refresh themselves periodically while they are set?
////REVIEW: Do we need a mute in addition to a pause?
namespace UnityEngine.InputSystem.Haptics
{
/// <summary>
/// Base interface for haptics on input devices.
/// </summary>
/// <remarks>
/// To support haptics, an <see cref="InputDevice"/> has to implement one or more
/// haptics interfaces.
/// </remarks>
/// <example>
/// <code>
/// class MyDevice : InputDevice, IDualMotorRumble
/// {
/// private DualMotorRumble m_Rumble;
///
/// public void SetMotorSpeeds(float lowFrequency, float highFrequency)
/// {
/// m_Rumble.SetMotorSpeeds(lowFrequency, highFrequency);
/// }
///
/// public void PauseHaptics()
/// {
/// m_Rumble.PauseHaptics();
/// }
///
/// public void ResumeHaptics()
/// {
/// m_Rumble.ResumeHaptics();
/// }
///
/// public void ResetHaptics()
/// {
/// m_Rumble.ResetHaptics();
/// }
/// }
/// </code>
/// </example>
/// <seealso cref="InputSystem.PauseHaptics"/>
/// <seealso cref="InputSystem.ResumeHaptics"/>
/// <seealso cref="InputSystem.ResetHaptics"/>
public interface IHaptics
{
/// <summary>
/// Pause haptics playback on the device.
/// </summary>
/// <remarks>
/// This should preserve current playback settings (such as motor speed levels
/// or effect playback positions) but shut down feedback effects on the device.
///
/// If proper resumption of effects is not possible, playback should be stopped
/// and <see cref="ResumeHaptics"/> is allowed to be a no-operation.
///
/// Note that haptics playback states are not required to survive domain reloads
/// in the editor.
/// </remarks>
/// <seealso cref="ResumeHaptics"/>
void PauseHaptics();
/// <summary>
/// Resume haptics playback on the device.
/// </summary>
/// <remarks>
/// Should be called after calling <see cref="PauseHaptics"/>. Otherwise does
/// nothing.
/// </remarks>
void ResumeHaptics();
/// <summary>
/// Reset haptics playback on the device to its default state.
/// </summary>
/// <remarks>
/// This will turn off all haptics effects that may be playing on the device.
/// </remarks>
void ResetHaptics();
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 179e4e4b8003406d97463b101f15eb8b
timeCreated: 1516752847