diff --git a/Packages/com.unity.inputsystem/InputSystem/State/InputStateHistory.cs b/Packages/com.unity.inputsystem/InputSystem/State/InputStateHistory.cs
index e98443475f..1cd3f4a620 100644
--- a/Packages/com.unity.inputsystem/InputSystem/State/InputStateHistory.cs
+++ b/Packages/com.unity.inputsystem/InputSystem/State/InputStateHistory.cs
@@ -29,7 +29,7 @@ namespace UnityEngine.InputSystem.LowLevel
///
/// The class listens to changes on the given controls by adding change monitors ()
/// to each control.
- ///
+ ///
///
///
/// // Track all stick controls in the system.
@@ -54,7 +54,7 @@ namespace UnityEngine.InputSystem.LowLevel
/// history.Dispose();
///
///
- ///
+ ///
public class InputStateHistory : IDisposable, IEnumerable, IInputStateChangeMonitor
{
private const int kDefaultHistorySize = 128;
@@ -62,30 +62,34 @@ public class InputStateHistory : IDisposable, IEnumerable
/// Total number of state records currently captured in the history.
///
- /// Number of records in the collection.
///
+ /// Number of records in the collection.
+ ///
/// This will always be at most .
+ /// To record a change use .
///
- ///
- ///
public int Count => m_RecordCount;
///
/// Current version stamp. Every time a record is stored in the history,
/// this is incremented by one.
///
- /// Version stamp that indicates the number of mutations.
- ///
+ ///
+ /// Version stamp that indicates the number of mutations.
+ /// To record a change use .
+ ///
public uint version => m_CurrentVersion;
///
/// Maximum number of records that can be recorded in the history.
///
- /// Upper limit on number of records.
/// is negative.
///
+ /// Upper limit on number of records.
/// A fixed size memory block of unmanaged memory will be allocated to store history
- /// records. This property determines TODO
+ /// records.
+ /// When the history is full, it will start overwriting the oldest
+ /// entry each time a new history record is received.
///
public int historyDepth
{
@@ -100,6 +104,15 @@ public int historyDepth
}
}
+ ///
+ /// Size of additional data storage to allocate per record.
+ ///
+ /// is negative.
+ ///
+ /// Additional custom data can be stored per record up to the size of this value.
+ /// To retrieve a pointer to this memory use
+ /// Used by
+ ///
public int extraMemoryPerRecord
{
get => m_ExtraMemoryPerRecord;
@@ -113,6 +126,14 @@ public int extraMemoryPerRecord
}
}
+ ///
+ /// Specify which player loop positions the state history will be monitored for.
+ ///
+ /// When an invalid mask is provided (e.g. ).
+ ///
+ /// The state history will only be monitored for the specified player loop positions.
+ /// is excluded from this list
+ ///
public InputUpdateType updateMask
{
get => m_UpdateMask ?? InputSystem.s_Manager.updateMask & ~InputUpdateType.Editor;
@@ -124,8 +145,22 @@ public InputUpdateType updateMask
}
}
+ ///
+ /// List of input controls the state history will be recording for.
+ ///
+ ///
+ /// The list of input controls the state history will be recording for is specified on construction of the
+ ///
public ReadOnlyArray controls => new ReadOnlyArray(m_Controls, 0, m_ControlCount);
+ ///
+ /// Returns an entry in the state history at the given index.
+ ///
+ /// Index into the array.
+ ///
+ /// Returns a entry from the state history at the given index.
+ ///
+ /// is less than 0 or greater than .
public unsafe Record this[int index]
{
get
@@ -148,9 +183,34 @@ public unsafe Record this[int index]
}
}
+ ///
+ /// Optional delegate to perform when a record is added to the history array.
+ ///
+ ///
+ /// Can be used to fill in the extra memory with custom data using
+ ///
public Action onRecordAdded { get; set; }
+
+ ///
+ /// Optional delegate to decide whether the state change should be stored in the history.
+ ///
+ ///
+ /// Can be used to filter out some events to focus on recording the ones you are most interested in.
+ ///
+ /// If the callback returns true, a record will be added to the history
+ /// If the callback returns false, the event will be ignored and not recorded.
+ ///
public Func onShouldRecordStateChange { get; set; }
+ ///
+ /// Creates a new InputStateHistory class to record all control state changes.
+ ///
+ /// Maximum size of control state in the record entries. Controls with larger state will not be recorded.
+ ///
+ /// Creates a new InputStateHistory to record a history of control state changes.
+ ///
+ /// New controls are automatically added into the state history if their state is smaller than the threshold.
+ ///
public InputStateHistory(int maxStateSizeInBytes)
{
if (maxStateSizeInBytes <= 0)
@@ -160,6 +220,19 @@ public InputStateHistory(int maxStateSizeInBytes)
m_StateSizeInBytes = maxStateSizeInBytes.AlignToMultipleOf(4);
}
+ ///
+ /// Creates a new InputStateHistory class to record state changes for a specified control.
+ ///
+ /// Control path to identify which controls to monitor.
+ ///
+ /// Creates a new InputStateHistory to record a history of state changes for the specified controls.
+ ///
+ ///
+ ///
+ /// // Track all stick controls in the system.
+ /// var history = new InputStateHistory("*/<Stick>");
+ ///
+ ///
public InputStateHistory(string path)
{
using (var controls = InputSystem.FindControls(path))
@@ -169,6 +242,13 @@ public InputStateHistory(string path)
}
}
+ ///
+ /// Creates a new InputStateHistory class to record state changes for a specified control.
+ ///
+ /// Control to monitor.
+ ///
+ /// Creates a new InputStateHistory to record a history of state changes for the specified control.
+ ///
public InputStateHistory(InputControl control)
{
if (control == null)
@@ -178,6 +258,13 @@ public InputStateHistory(InputControl control)
m_ControlCount = 1;
}
+ ///
+ /// Creates a new InputStateHistory class to record state changes for a specified controls.
+ ///
+ /// Controls to monitor.
+ ///
+ /// Creates a new InputStateHistory to record a history of state changes for the specified controls.
+ ///
public InputStateHistory(IEnumerable controls)
{
if (controls != null)
@@ -187,11 +274,22 @@ public InputStateHistory(IEnumerable controls)
}
}
+ ///
+ /// InputStateHistory destructor.
+ ///
~InputStateHistory()
{
Dispose();
}
+ ///
+ /// Clear the history record.
+ ///
+ ///
+ /// Clear the history record. Resetting the list to empty.
+ ///
+ /// This won't clear controls that have been added on the fly.
+ ///
public void Clear()
{
m_HeadIndex = 0;
@@ -201,6 +299,15 @@ public void Clear()
// NOTE: Won't clear controls that have been added on the fly.
}
+ ///
+ /// Add a record to the input state history.
+ ///
+ /// Record to add.
+ /// The newly added record from the history array (as a copy is made).
+ ///
+ /// Add a record to the input state history.
+ /// Allocates an entry in the history array and returns this copy of the original data passed to the function.
+ ///
public unsafe Record AddRecord(Record record)
{
var recordPtr = AllocateRecord(out var index);
@@ -209,6 +316,21 @@ public unsafe Record AddRecord(Record record)
return newRecord;
}
+ ///
+ /// Start recording state history for the specified controls.
+ ///
+ ///
+ /// Start recording state history for the controls specified in the constructor.
+ ///
+ ///
+ ///
+ /// using (var allTouchTaps = new InputStateHistory("<Touchscreen>/touch*/tap"))
+ /// {
+ /// allTouchTaps.StartRecording();
+ /// allTouchTaps.StopRecording();
+ /// }
+ ///
+ ///
public void StartRecording()
{
// We defer allocation until we actually get values on a control.
@@ -217,12 +339,38 @@ public void StartRecording()
InputState.AddChangeMonitor(control, this);
}
+ ///
+ /// Stop recording state history for the specified controls.
+ ///
+ ///
+ /// Stop recording state history for the controls specified in the constructor.
+ ///
+ ///
+ ///
+ /// using (var allTouchTaps = new InputStateHistory("<Touchscreen>/touch*/tap"))
+ /// {
+ /// allTouchTaps.StartRecording();
+ /// allTouchTaps.StopRecording();
+ /// }
+ ///
+ ///
public void StopRecording()
{
foreach (var control in controls)
InputState.RemoveChangeMonitor(control, this);
}
+ ///
+ /// Record a state change for a specific control.
+ ///
+ /// The control to record the state change for.
+ /// The current event data to record.
+ /// The newly added record.
+ ///
+ /// Record a state change for a specific control.
+ /// Will call the delegate after adding the record.
+ /// Note this does not call the delegate.
+ ///
public unsafe Record RecordStateChange(InputControl control, InputEventPtr eventPtr)
{
if (eventPtr.IsA())
@@ -236,6 +384,18 @@ public unsafe Record RecordStateChange(InputControl control, InputEventPtr event
return RecordStateChange(control, statePtr, eventPtr.time);
}
+ ///
+ /// Record a state change for a specific control.
+ ///
+ /// The control to record the state change for.
+ /// The current state data to record.
+ /// Time stamp to apply (overriding the event timestamp)
+ /// The newly added record.
+ ///
+ /// Record a state change for a specific control.
+ /// Will call the delegate after adding the record.
+ /// Note this does not call the delegate.
+ ///
public unsafe Record RecordStateChange(InputControl control, void* statePtr, double time)
{
var controlIndex = ArrayHelpers.IndexOfReference(m_Controls, control, m_ControlCount);
@@ -277,16 +437,37 @@ public unsafe Record RecordStateChange(InputControl control, void* statePtr, dou
return record;
}
+ ///
+ /// Enumerate all state history records.
+ ///
+ /// An enumerator going over the state history records.
+ ///
+ /// Enumerate all state history records.
+ ///
+ ///
public IEnumerator GetEnumerator()
{
return new Enumerator(this);
}
+ ///
+ /// Enumerate all state history records.
+ ///
+ /// An enumerator going over the state history records.
+ ///
+ /// Enumerate all state history records.
+ ///
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
+ ///
+ /// Dispose of the state history records.
+ ///
+ ///
+ /// Stops recording and cleans up the state history
+ ///
public void Dispose()
{
StopRecording();
@@ -294,6 +475,12 @@ public void Dispose()
GC.SuppressFinalize(this);
}
+ ///
+ /// Destroy the state history records.
+ ///
+ ///
+ /// Deletes the state history records.
+ ///
protected void Destroy()
{
if (m_RecordBuffer.IsCreated)
@@ -325,6 +512,15 @@ private void Allocate()
NativeArrayOptions.UninitializedMemory);
}
+ ///
+ /// Remap a records internal index to an index from the start of the recording in the circular buffer.
+ ///
+ ///
+ /// Remap a records internal index, which is relative to the start of the record buffer,
+ /// to an index relative to the start of the recording in the circular buffer.
+ ///
+ /// Record index (from the start of the record array).
+ /// An index relative to the start of the recording in the circular buffer.
protected internal int RecordIndexToUserIndex(int index)
{
if (index < m_HeadIndex)
@@ -332,11 +528,30 @@ protected internal int RecordIndexToUserIndex(int index)
return index - m_HeadIndex;
}
+ ///
+ /// Remap an index from the start of the recording in the circular buffer to a records internal index.
+ ///
+ ///
+ /// Remap an index relative to the start of the recording in the circular buffer,
+ /// to a records internal index, which is relative to the start of the record buffer.
+ ///
+ /// An index relative to the start of the recording in the circular buffer.
+ /// Record index (from the start of the record array).
protected internal int UserIndexToRecordIndex(int index)
{
return (m_HeadIndex + index) % m_HistoryDepth;
}
+ ///
+ /// Retrieve a record from the input state history.
+ ///
+ ///
+ /// Retrieve a record from the input state history by Record index.
+ ///
+ /// Record index into the input state history records buffer.
+ /// The record header for the specified index
+ /// When the buffer is no longer valid as it has been disposed.
+ /// If the index is out of range of the history depth.
protected internal unsafe RecordHeader* GetRecord(int index)
{
if (!m_RecordBuffer.IsCreated)
@@ -346,11 +561,27 @@ protected internal int UserIndexToRecordIndex(int index)
return GetRecordUnchecked(index);
}
+ ///
+ /// Retrieve a record from the input state history, without any bounds check.
+ ///
+ ///
+ /// Retrieve a record from the input state history by record index, without any bounds check
+ ///
+ /// Record index into the input state history records buffer.
+ /// The record header for the specified index
internal unsafe RecordHeader* GetRecordUnchecked(int index)
{
return (RecordHeader*)((byte*)m_RecordBuffer.GetUnsafePtr() + index * bytesPerRecord);
}
+ ///
+ /// Allocate a new record in the input state history.
+ ///
+ ///
+ /// Allocate a new record in the input state history.
+ ///
+ /// The index of the newly created record
+ /// The header of the newly created record
protected internal unsafe RecordHeader* AllocateRecord(out int index)
{
if (!m_RecordBuffer.IsCreated)
@@ -371,6 +602,13 @@ protected internal int UserIndexToRecordIndex(int index)
return (RecordHeader*)((byte*)m_RecordBuffer.GetUnsafePtr() + bytesPerRecord * index);
}
+ ///
+ /// Returns value from the control in the specified record header.
+ ///
+ /// The record header to query.
+ /// The type of the value being read
+ /// The value from the record.
+ /// When the record is no longer value or the specified type is not present.
protected unsafe TValue ReadValue(RecordHeader* data)
where TValue : struct
{
@@ -387,6 +625,15 @@ protected unsafe TValue ReadValue(RecordHeader* data)
return controlOfType.ReadValueFromState(statePtr);
}
+ ///
+ /// Read the control's final, processed value from the given state and return the value as an object.
+ ///
+ /// The record header to query.
+ /// The value of the control associated with the record header.
+ ///
+ /// This method allocates GC memory and should not be used during normal gameplay operation.
+ ///
+ /// When the specified value is not present.
protected unsafe object ReadValueAsObject(RecordHeader* data)
{
// Get control. If we only have a single one, the index isn't stored on the data.
@@ -399,6 +646,18 @@ protected unsafe object ReadValueAsObject(RecordHeader* data)
return control.ReadValueFromStateAsObject(statePtr);
}
+ ///
+ /// Delegate to list to control state change notifications.
+ ///
+ /// Control that is being monitored by the state change monitor and that had its state memory changed.
+ /// Time on the timeline at which the control state change was received.
+ /// If the state change was initiated by a state event (either a
+ /// or ), this is the pointer to that event. Otherwise, it is pointer that is still
+ /// , but refers a "dummy" event that is not a or .
+ /// Index of the monitor as passed to
+ ///
+ /// Records a state change after checking the and the callback.
+ ///
unsafe void IInputStateChangeMonitor.NotifyControlStateChanged(InputControl control, double time,
InputEventPtr eventPtr, long monitorIndex)
{
@@ -416,6 +675,15 @@ unsafe void IInputStateChangeMonitor.NotifyControlStateChanged(InputControl cont
}
// Unused.
+ ///
+ /// Called when a timeout set on a state change monitor has expired.
+ ///
+ /// Control on which the timeout expired.
+ /// Input time at which the timer expired. This is the time at which an is being
+ /// run whose is past the time of expiration.
+ /// Index of the monitor as given to .
+ /// Index of the timer as given to .
+ ///
void IInputStateChangeMonitor.NotifyTimerExpired(InputControl control, double time, long monitorIndex,
int timerIndex)
{
@@ -473,16 +741,50 @@ public void Dispose()
}
}
+ /// State change record header
+ ///
+ /// Input State change record header containing the timestamp and other common record data.
+ /// Stored in the .
+ ///
+ ///
[StructLayout(LayoutKind.Explicit)]
protected internal unsafe struct RecordHeader
{
+ ///
+ /// The time stamp of the input state record.
+ ///
+ ///
+ /// The time stamp of the input state record in the owning container.
+ ///
+ ///
[FieldOffset(0)] public double time;
+
+ ///
+ /// The version of the input state record.
+ ///
+ ///
+ /// Current version stamp. See .
+ ///
[FieldOffset(8)] public uint version;
+
+ ///
+ /// The index of the record.
+ ///
+ ///
+ /// The index of the record relative to the start of the buffer.
+ /// See to remap this record index to a user index.
+ ///
[FieldOffset(12)] public int controlIndex;
[FieldOffset(12)] private fixed byte m_StateWithoutControlIndex[1];
[FieldOffset(16)] private fixed byte m_StateWithControlIndex[1];
+ ///
+ /// The state data including the control index.
+ ///
+ ///
+ /// The state data including the control index.
+ ///
public byte* statePtrWithControlIndex
{
get
@@ -492,6 +794,12 @@ public byte* statePtrWithControlIndex
}
}
+ ///
+ /// The state data excluding the control index.
+ ///
+ ///
+ /// The state data excluding the control index.
+ ///
public byte* statePtrWithoutControlIndex
{
get
@@ -501,10 +809,26 @@ public byte* statePtrWithoutControlIndex
}
}
+ ///
+ /// Size of the state data including the control index.
+ ///
+ ///
+ /// Size of the data including the control index.
+ ///
public const int kSizeWithControlIndex = 16;
+
+ ///
+ /// Size of the state data excluding the control index.
+ ///
+ ///
+ /// Size of the data excluding the control index.
+ ///
public const int kSizeWithoutControlIndex = 12;
}
+ /// State change record
+ /// Input State change record stored in the .
+ ///
public unsafe struct Record : IEquatable
{
// We store an index rather than a direct pointer to make this struct safer to use.
@@ -516,10 +840,31 @@ public unsafe struct Record : IEquatable
internal int recordIndex => m_IndexPlusOne - 1;
internal uint version => m_Version;
+ ///
+ /// Identifies if the record is valid.
+ ///
+ /// True if the record is a valid entry. False if invalid.
+ ///
+ /// When the history is cleared with the entries become invalid.
+ ///
public bool valid => m_Owner != default && m_IndexPlusOne != default && header->version == m_Version;
+ ///
+ /// Identifies the owning container for the record.
+ ///
+ /// The owning container for the record.
+ ///
+ /// Identifies the owning container for the record.
+ ///
public InputStateHistory owner => m_Owner;
+ ///
+ /// The index of the input state record in the owning container.
+ ///
+ ///
+ /// The index of the input state record in the owning container.
+ ///
+ /// When the record is no longer value.
public int index
{
get
@@ -529,6 +874,14 @@ public int index
}
}
+ ///
+ /// The time stamp of the input state record.
+ ///
+ ///
+ /// The time stamp of the input state record in the owning container.
+ ///
+ ///
+ /// When the record is no longer value.
public double time
{
get
@@ -538,6 +891,13 @@ public double time
}
}
+ ///
+ /// The control associated with the input state record.
+ ///
+ ///
+ /// The control associated with the input state record.
+ ///
+ /// When the record is no longer value.
public InputControl control
{
get
@@ -550,6 +910,13 @@ public InputControl control
}
}
+ ///
+ /// The next input state record in the owning container.
+ ///
+ ///
+ /// The next input state record in the owning container.
+ ///
+ /// When the record is no longer value.
public Record next
{
get
@@ -563,6 +930,13 @@ public Record next
}
}
+ ///
+ /// The previous input state record in the owning container.
+ ///
+ ///
+ /// The previous input state record in the owning container.
+ ///
+ /// When the record is no longer value.
public Record previous
{
get
@@ -583,6 +957,12 @@ internal Record(InputStateHistory owner, int index, RecordHeader* header)
m_Version = header->version;
}
+ ///
+ /// Returns value from the control in the record.
+ ///
+ /// The type of the value being read
+ /// Returns the value from the record.
+ /// When the record is no longer value or the specified type is not present.
public TValue ReadValue()
where TValue : struct
{
@@ -590,12 +970,27 @@ public TValue ReadValue()
return m_Owner.ReadValue(header);
}
+ ///
+ /// Read the control's final, processed value from the given state and return the value as an object.
+ ///
+ /// The value of the control associated with the record.
+ ///
+ /// This method allocates GC memory and should not be used during normal gameplay operation.
+ ///
+ /// When the specified value is not present.
public object ReadValueAsObject()
{
CheckValid();
return m_Owner.ReadValueAsObject(header);
}
+ ///
+ /// Read the state memory for the record.
+ ///
+ /// The state memory for the record.
+ ///
+ /// Read the state memory for the record.
+ ///
public void* GetUnsafeMemoryPtr()
{
CheckValid();
@@ -609,6 +1004,14 @@ public object ReadValueAsObject()
return header->statePtrWithControlIndex;
}
+ ///
+ /// Read the extra memory for the record.
+ ///
+ /// The extra memory for the record.
+ ///
+ /// Additional date can be stored in a record in the extra memory section.
+ ///
+ ///
public void* GetUnsafeExtraMemoryPtr()
{
CheckValid();
@@ -622,6 +1025,13 @@ public object ReadValueAsObject()
return (byte*)header + m_Owner.bytesPerRecord - m_Owner.extraMemoryPerRecord;
}
+ /// Copy data from one record to another.
+ /// Source record to copy from.
+ ///
+ /// Copy data from one record to another.
+ ///
+ /// When the source record history is not valid.
+ /// When the control is not tracked by the owning container.
public void CopyFrom(Record record)
{
if (!record.valid)
@@ -686,16 +1096,27 @@ internal void CheckValid()
throw new InvalidOperationException("Record is no longer valid");
}
+ /// Compare two records.
+ /// Compare two records.
+ /// The record to compare with.
+ /// True if the records are the same, False if they differ.
public bool Equals(Record other)
{
return ReferenceEquals(m_Owner, other.m_Owner) && m_IndexPlusOne == other.m_IndexPlusOne && m_Version == other.m_Version;
}
+ /// Compare two records.
+ /// Compare two records.
+ /// The record to compare with.
+ /// True if the records are the same, False if they differ.
public override bool Equals(object obj)
{
return obj is Record other && Equals(other);
}
+ /// Return the hash code of the record.
+ /// Return the hash code of the record.
+ /// The hash code of the record.
public override int GetHashCode()
{
unchecked
@@ -707,6 +1128,9 @@ public override int GetHashCode()
}
}
+ /// Return the string representation of the record.
+ /// Includes the control, value and time of the record (or <Invalid> if not valid).
+ /// The string representation of the record.
public override string ToString()
{
if (!valid)
@@ -720,10 +1144,27 @@ public override string ToString()
///
/// Records value changes of a given control over time.
///
- ///
+ /// The type of the record being stored
+ ///
+ /// This class makes it easy to track input values over time. It will automatically retain input state up to a given
+ /// maximum history depth (). When the history is full, it will start overwriting the oldest
+ /// entry each time a new history record is received.
+ ///
+ /// The class listens to changes on the given controls by adding change monitors ()
+ /// to each control.
+ ///
public class InputStateHistory : InputStateHistory, IReadOnlyList.Record>
where TValue : struct
{
+ ///
+ /// Creates a new InputStateHistory class to record all control state changes.
+ ///
+ /// Maximum size of control state in the record entries. Controls with larger state will not be recorded.
+ ///
+ /// Creates a new InputStateHistory to record a history of control state changes.
+ ///
+ /// New controls are automatically added into the state history if there state is smaller than the threshold.
+ ///
public InputStateHistory(int? maxStateSizeInBytes = null)
// Using the size of the value here isn't quite correct but the value is used as an upper
// bound on stored state size for which the size of the value should be a reasonable guess.
@@ -733,11 +1174,31 @@ public InputStateHistory(int? maxStateSizeInBytes = null)
throw new ArgumentException("Max state size cannot be smaller than sizeof(TValue)", nameof(maxStateSizeInBytes));
}
+ ///
+ /// Creates a new InputStateHistory class to record state changes for a specified control.
+ ///
+ /// Control to monitor.
+ ///
+ /// Creates a new InputStateHistory to record a history of state changes for the specified control.
+ ///
public InputStateHistory(InputControl control)
: base(control)
{
}
+ ///
+ /// Creates a new InputStateHistory class to record state changes for a specified control.
+ ///
+ /// Control path to identify which controls to monitor.
+ ///
+ /// Creates a new InputStateHistory to record a history of state changes for the specified controls.
+ ///
+ ///
+ ///
+ /// // Track all stick controls in the system.
+ /// var history = new InputStateHistory<Vector2>("*/<Stick>");
+ ///
+ ///
public InputStateHistory(string path)
: base(path)
{
@@ -748,11 +1209,23 @@ public InputStateHistory(string path)
$"Control '{control}' matched by '{path}' has value type '{TypeHelpers.GetNiceTypeName(control.valueType)}' which is incompatible with '{TypeHelpers.GetNiceTypeName(typeof(TValue))}'");
}
+ ///
+ /// InputStateHistory destructor.
+ ///
~InputStateHistory()
{
Destroy();
}
+ ///
+ /// Add a record to the input state history.
+ ///
+ /// Record to add.
+ /// The newly added record from the history array (as a copy is made).
+ ///
+ /// Add a record to the input state history.
+ /// Allocates an entry in the history array and returns this copy of the original data passed to the function.
+ ///
public unsafe Record AddRecord(Record record)
{
var recordPtr = AllocateRecord(out var index);
@@ -761,6 +1234,26 @@ public unsafe Record AddRecord(Record record)
return newRecord;
}
+ ///
+ /// Record a state change for a specific control.
+ ///
+ /// The control to record the state change for.
+ /// The value to record.
+ /// Time stamp to apply (overriding the event timestamp)
+ /// The newly added record.
+ ///
+ /// Record a state change for a specific control.
+ /// Will call the delegate after adding the record.
+ /// Note this does not call the delegate.
+ ///
+ ///
+ ///
+ /// using (var allTouchTaps = new InputStateHistory<float>(Gamepad.current.leftTrigger))
+ /// {
+ /// history.RecordStateChange(Gamepad.current.leftTrigger, 0.234f);
+ /// }
+ ///
+ ///
public unsafe Record RecordStateChange(InputControl control, TValue value, double time = -1)
{
using (StateEvent.From(control.device, out var eventPtr))
@@ -774,16 +1267,39 @@ public unsafe Record RecordStateChange(InputControl control, TValue valu
}
}
+ ///
+ /// Enumerate all state history records.
+ ///
+ /// An enumerator going over the state history records.
+ ///
+ /// Enumerate all state history records.
+ ///
+ ///
public new IEnumerator GetEnumerator()
{
return new Enumerator(this);
}
+ ///
+ /// Enumerate all state history records.
+ ///
+ /// An enumerator going over the state history records.
+ ///
+ /// Enumerate all state history records.
+ ///
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
+ ///
+ /// Returns an entry in the state history at the given index.
+ ///
+ /// Index into the array.
+ ///
+ /// Returns a entry from the state history at the given index.
+ ///
+ /// is less than 0 or greater than .
public new unsafe Record this[int index]
{
get
@@ -838,6 +1354,9 @@ public void Dispose()
}
}
+ /// State change record
+ /// Input State change record stored in the
+ ///
public new unsafe struct Record : IEquatable
{
private readonly InputStateHistory m_Owner;
@@ -847,10 +1366,31 @@ public void Dispose()
internal RecordHeader* header => m_Owner.GetRecord(recordIndex);
internal int recordIndex => m_IndexPlusOne - 1;
+ ///
+ /// Identifies if the record is valid.
+ ///
+ /// True if the record is a valid entry. False if invalid.
+ ///
+ /// When the history is cleared with the entries become invalid.
+ ///
public bool valid => m_Owner != default && m_IndexPlusOne != default && header->version == m_Version;
+ ///
+ /// Identifies the owning container for the record.
+ ///
+ /// The owning container for the record.
+ ///
+ /// Identifies the owning container for the record.
+ ///
public InputStateHistory owner => m_Owner;
+ ///
+ /// The index of the input state record in the owning container.
+ ///
+ ///
+ /// The index of the input state record in the owning container.
+ ///
+ /// When the record is no longer value.
public int index
{
get
@@ -860,6 +1400,14 @@ public int index
}
}
+ ///
+ /// The time stamp of the input state record.
+ ///
+ ///
+ /// The time stamp of the input state record in the owning container.
+ ///
+ ///
+ /// When the record is no longer value.
public double time
{
get
@@ -869,6 +1417,13 @@ public double time
}
}
+ ///
+ /// The control associated with the input state record.
+ ///
+ ///
+ /// The control associated with the input state record.
+ ///
+ /// When the record is no longer value.
public InputControl control
{
get
@@ -881,6 +1436,13 @@ public InputControl control
}
}
+ ///
+ /// The next input state record in the owning container.
+ ///
+ ///
+ /// The next input state record in the owning container.
+ ///
+ /// When the record is no longer value.
public Record next
{
get
@@ -894,6 +1456,13 @@ public Record next
}
}
+ ///
+ /// The previous input state record in the owning container.
+ ///
+ ///
+ /// The previous input state record in the owning container.
+ ///
+ /// When the record is no longer value.
public Record previous
{
get
@@ -921,12 +1490,24 @@ internal Record(InputStateHistory owner, int index)
m_Version = default;
}
+ ///
+ /// Returns value from the control in the Record.
+ ///
+ /// Returns value from the Record.
+ /// When the record is no longer value or the specified type is not present.
public TValue ReadValue()
{
CheckValid();
return m_Owner.ReadValue(header);
}
+ ///
+ /// Read the state memory for the record.
+ ///
+ /// The state memory for the record.
+ ///
+ /// Read the state memory for the record.
+ ///
public void* GetUnsafeMemoryPtr()
{
CheckValid();
@@ -940,6 +1521,14 @@ public TValue ReadValue()
return header->statePtrWithControlIndex;
}
+ ///
+ /// Read the extra memory for the record.
+ ///
+ /// The extra memory for the record.
+ ///
+ /// Additional date can be stored in a record in the extra memory section.
+ ///
+ ///
public void* GetUnsafeExtraMemoryPtr()
{
CheckValid();
@@ -953,6 +1542,13 @@ public TValue ReadValue()
return (byte*)header + m_Owner.bytesPerRecord - m_Owner.extraMemoryPerRecord;
}
+ /// Copy data from one record to another.
+ /// Source Record to copy from.
+ ///
+ /// Copy data from one record to another.
+ ///
+ /// When the source record history is not valid.
+ /// When the control is not tracked by the owning container.
public void CopyFrom(Record record)
{
CheckValid();
@@ -971,16 +1567,27 @@ private void CheckValid()
throw new InvalidOperationException("Record is no longer valid");
}
+ /// Compare two records.
+ /// Compare two records.
+ /// The record to compare with.
+ /// True if the records are the same, False if they differ.
public bool Equals(Record other)
{
return ReferenceEquals(m_Owner, other.m_Owner) && m_IndexPlusOne == other.m_IndexPlusOne && m_Version == other.m_Version;
}
+ /// Compare two records.
+ /// Compare two records.
+ /// The record to compare with.
+ /// True if the records are the same, False if they differ.
public override bool Equals(object obj)
{
return obj is Record other && Equals(other);
}
+ /// Return the hash code of the record.
+ /// Return the hash code of the record.
+ /// The hash code of the record.
public override int GetHashCode()
{
unchecked
@@ -992,6 +1599,9 @@ public override int GetHashCode()
}
}
+ /// Return the string representation of the record.
+ /// Includes the control, value and time of the record (or <Invalid> if not valid).
+ /// The string representation of the record.
public override string ToString()
{
if (!valid)