A game about forced loneliness, made by TACStudios
at master 17 kB view raw
1using System; 2using UnityEngine.InputSystem.LowLevel; 3 4namespace UnityEngine.InputSystem 5{ 6 /// <summary> 7 /// Information passed to <see cref="IInputInteraction">interactions</see> 8 /// when their associated controls trigger. 9 /// </summary> 10 /// <seealso cref="IInputInteraction.Process"/> 11 public struct InputInteractionContext 12 { 13 /// <summary> 14 /// The action associated with the binding. 15 /// </summary> 16 /// <remarks> 17 /// If the binding is not associated with an action, this is <c>null</c>. 18 /// </remarks> 19 /// <seealso cref="InputBinding.action"/> 20 public InputAction action => m_State.GetActionOrNull(ref m_TriggerState); 21 22 /// <summary> 23 /// The bound control that changed its state to trigger the binding associated 24 /// with the interaction. 25 /// </summary> 26 /// <remarks> 27 /// In case the binding associated with the interaction is a composite, this is 28 /// one of the controls that are part of the composite. 29 /// </remarks> 30 /// <seealso cref="InputBinding.path"/> 31 public InputControl control => m_State.GetControl(ref m_TriggerState); 32 33 /// <summary> 34 /// The phase the interaction is currently in. 35 /// </summary> 36 /// <remarks> 37 /// Each interaction on a binding has its own phase independent of the action the binding is applied to. 38 /// If an interaction gets to "drive" an action at a particular point in time, its phase will determine 39 /// the phase of the action. 40 /// </remarks> 41 /// <seealso cref="InputAction.phase"/> 42 /// <seealso cref="Started"/> 43 /// <seealso cref="Waiting"/> 44 /// <seealso cref="Performed"/> 45 /// <seealso cref="Canceled"/> 46 public InputActionPhase phase => m_TriggerState.phase; 47 48 /// <summary> 49 /// Time stamp of the input event that caused <see cref="control"/> to trigger a change in the 50 /// state of <see cref="action"/>. 51 /// </summary> 52 /// <seealso cref="InputEvent.time"/> 53 public double time => m_TriggerState.time; 54 55 /// <summary> 56 /// Timestamp of the <see cref="InputEvent"/> that caused the interaction to transition 57 /// to <see cref="InputActionPhase.Started"/>. 58 /// </summary> 59 /// <seealso cref="InputEvent.time"/> 60 public double startTime => m_TriggerState.startTime; 61 62 /// <summary> 63 /// Whether the interaction's <see cref="IInputInteraction.Process"/> method has been called because 64 /// a timer set by <see cref="SetTimeout"/> has expired. 65 /// </summary> 66 /// <seealso cref="SetTimeout"/> 67 public bool timerHasExpired 68 { 69 get => (m_Flags & Flags.TimerHasExpired) != 0; 70 internal set 71 { 72 if (value) 73 m_Flags |= Flags.TimerHasExpired; 74 else 75 m_Flags &= ~Flags.TimerHasExpired; 76 } 77 } 78 79 /// <summary> 80 /// True if the interaction is waiting for input 81 /// </summary> 82 /// <remarks> 83 /// By default, an interaction will return this this phase after every time it has been performed 84 /// (<see cref="InputActionPhase.Performed"/>). This can be changed by using <see cref="PerformedAndStayStarted"/> 85 /// or <see cref="PerformedAndStayPerformed"/>. 86 /// </remarks> 87 /// <seealso cref="InputActionPhase.Waiting"/> 88 public bool isWaiting => phase == InputActionPhase.Waiting; 89 90 /// <summary> 91 /// True if the interaction has been started. 92 /// </summary> 93 /// <seealso cref="InputActionPhase.Started"/> 94 /// <seealso cref="Started"/> 95 public bool isStarted => phase == InputActionPhase.Started; 96 97 /// <summary> 98 /// Compute the current level of control actuation. 99 /// </summary> 100 /// <returns>The current level of control actuation (usually [0..1]) or -1 if the control is actuated 101 /// but does not support computing magnitudes.</returns> 102 /// <seealso cref="ControlIsActuated"/> 103 /// <seealso cref="InputControl.EvaluateMagnitude()"/> 104 public float ComputeMagnitude() 105 { 106 return m_TriggerState.magnitude; 107 } 108 109 /// <summary> 110 /// Return true if the control that triggered the interaction has been actuated beyond the given threshold. 111 /// </summary> 112 /// <param name="threshold">Threshold that must be reached for the control to be considered actuated. If this is zero, 113 /// the threshold must be exceeded. If it is any positive value, the value must be at least matched.</param> 114 /// <returns>True if the trigger control is actuated.</returns> 115 /// <seealso cref="InputControlExtensions.IsActuated"/> 116 /// <seealso cref="ComputeMagnitude"/> 117 public bool ControlIsActuated(float threshold = 0) 118 { 119 return InputActionState.IsActuated(ref m_TriggerState, threshold); 120 } 121 122 /// <summary> 123 /// Mark the interaction has having begun. 124 /// </summary> 125 /// <remarks> 126 /// This affects the current interaction only. There might be multiple interactions on a binding 127 /// and arbitrary many interactions might concurrently be in started state. However, only one interaction 128 /// (usually the one that starts first) is allowed to drive the action's state as a whole. If an interaction 129 /// that is currently driving an action is canceled, however, the next interaction in the list that has 130 /// been started will take over and continue driving the action. 131 /// 132 /// <example> 133 /// <code> 134 /// public class MyInteraction : IInputInteraction&lt;float&gt; 135 /// { 136 /// public void Process(ref IInputInteractionContext context) 137 /// { 138 /// if (context.isWaiting &amp;&amp; context.ControlIsActuated()) 139 /// { 140 /// // We've waited for input and got it. Start the interaction. 141 /// context.Started(); 142 /// } 143 /// else if (context.isStarted &amp;&amp; !context.ControlIsActuated()) 144 /// { 145 /// // Interaction has been completed. 146 /// context.Performed(); 147 /// } 148 /// } 149 /// 150 /// public void Reset() 151 /// { 152 /// // No reset code needed. We're not keeping any state locally in the interaction. 153 /// } 154 /// } 155 /// </code> 156 /// </example> 157 /// </remarks> 158 public void Started() 159 { 160 m_TriggerState.startTime = time; 161 m_State.ChangePhaseOfInteraction(InputActionPhase.Started, ref m_TriggerState); 162 } 163 164 /// <summary> 165 /// Marks the interaction as being performed and then transitions back to <see cref="InputActionPhase.Waiting"/> 166 /// to wait for input. This behavior is desirable for interaction events that are instant and reflect 167 /// a transitional interaction pattern such as <see cref="Interactions.PressInteraction"/> or <see cref="Interactions.TapInteraction"/>. 168 /// </summary> 169 /// <remarks> 170 /// Note that this affects the current interaction only. There might be multiple interactions on a binding 171 /// and arbitrary many interactions might concurrently be in started state. However, only one interaction 172 /// (usually the one that starts first) is allowed to drive the action's state as a whole. If an interaction 173 /// that is currently driving an action is canceled, however, the next interaction in the list that has 174 /// been started will take over and continue driving the action. 175 /// </remarks> 176 public void Performed() 177 { 178 if (m_TriggerState.phase == InputActionPhase.Waiting) 179 m_TriggerState.startTime = time; 180 m_State.ChangePhaseOfInteraction(InputActionPhase.Performed, ref m_TriggerState); 181 } 182 183 /// <summary> 184 /// Marks the interaction as being performed and then transitions into I <see cref="InputActionPhase.Started"/> 185 /// to wait for an initial trigger condition to be true before being performed again. This behavior 186 /// may be desirable for interaction events that reflect transitional interaction patterns but should 187 /// be considered as started until a cancellation condition is true, such as releasing a button. 188 /// </summary> 189 public void PerformedAndStayStarted() 190 { 191 if (m_TriggerState.phase == InputActionPhase.Waiting) 192 m_TriggerState.startTime = time; 193 m_State.ChangePhaseOfInteraction(InputActionPhase.Performed, ref m_TriggerState, 194 phaseAfterPerformed: InputActionPhase.Started); 195 } 196 197 /// <summary> 198 /// Marks the interaction as being performed and then stays in that state waiting for an input to 199 /// cancel the interactions active state. This behavior is desirable for interaction events that 200 /// are active for a duration until a cancellation condition is true, such as <see cref="Interactions.HoldInteraction"/> or <see cref="Interactions.TapInteraction"/> where releasing 201 /// the associated button cancels the interaction.. 202 /// </summary> 203 public void PerformedAndStayPerformed() 204 { 205 if (m_TriggerState.phase == InputActionPhase.Waiting) 206 m_TriggerState.startTime = time; 207 m_State.ChangePhaseOfInteraction(InputActionPhase.Performed, ref m_TriggerState, 208 phaseAfterPerformed: InputActionPhase.Performed); 209 } 210 211 /// <summary> 212 /// Marks the interaction as being interrupted or aborted. This is relevant to signal that the interaction 213 /// pattern was not completed, for example, the user pressed and then released a button before the minimum 214 /// time required for a <see cref="Interactions.HoldInteraction"/> to complete. 215 /// </summary> 216 /// <remarks> 217 /// This is used by most existing interactions to cancel the transitions in the interaction state machine 218 /// when a condition required to proceed turned false or other indirect requirements were not met, such as 219 /// time-based conditions. 220 /// </remarks> 221 public void Canceled() 222 { 223 if (m_TriggerState.phase != InputActionPhase.Canceled) 224 m_State.ChangePhaseOfInteraction(InputActionPhase.Canceled, ref m_TriggerState); 225 } 226 227 /// <summary> 228 /// Put the interaction back into <see cref="InputActionPhase.Waiting"/> state. 229 /// </summary> 230 /// <seealso cref="InputAction.phase"/> 231 /// <seealso cref="InputActionPhase"/> 232 /// <seealso cref="Started"/> 233 /// <seealso cref="Performed"/> 234 /// <seealso cref="Canceled"/> 235 public void Waiting() 236 { 237 if (m_TriggerState.phase != InputActionPhase.Waiting) 238 m_State.ChangePhaseOfInteraction(InputActionPhase.Waiting, ref m_TriggerState); 239 } 240 241 /// <summary> 242 /// Start a timeout that triggers within <paramref name="seconds"/>. 243 /// </summary> 244 /// <param name="seconds">Number of seconds before the timeout is triggered.</param> 245 /// <remarks> 246 /// An interaction might wait a set amount of time for something to happen and then 247 /// do something depending on whether it did or did not happen. By calling this method, 248 /// a timeout is installed such that in the input update that the timer expires in, the 249 /// interaction's <see cref="IInputInteraction.Process"/> method is called with <see cref="timerHasExpired"/> 250 /// being true. 251 /// 252 /// Changing the phase of the interaction while a timeout is running will implicitly cancel 253 /// the timeout. 254 /// 255 /// <example> 256 /// <code> 257 /// // Let's say we're writing a Process() method for an interaction that, 258 /// // after a control has been actuated, waits for 1 second for it to be 259 /// // released again. If that happens, the interaction performs. If not, 260 /// // it cancels. 261 /// public void Process(ref InputInteractionContext context) 262 /// { 263 /// // timerHasExpired will be true if we get called when our timeout 264 /// // has expired. 265 /// if (context.timerHasExpired) 266 /// { 267 /// // The user did not release the control quickly enough. 268 /// // Our interaction is not successful, so cancel. 269 /// context.Canceled(); 270 /// return; 271 /// } 272 /// 273 /// if (context.ControlIsActuated()) 274 /// { 275 /// if (!context.isStarted) 276 /// { 277 /// // The control has been actuated. We want to give the user a max 278 /// // of 1 second to release it. So we start the interaction now and then 279 /// // set the timeout. 280 /// context.Started(); 281 /// context.SetTimeout(1); 282 /// } 283 /// } 284 /// else 285 /// { 286 /// // Control has been released. If we're currently waiting for a release, 287 /// // it has come in time before out timeout expired. In other words, the 288 /// // interaction has been successfully performed. We call Performed() 289 /// // which implicitly removes our ongoing timeout. 290 /// if (context.isStarted) 291 /// context.Performed(); 292 /// } 293 /// } 294 /// </code> 295 /// </example> 296 /// </remarks> 297 /// <seealso cref="timerHasExpired"/> 298 public void SetTimeout(float seconds) 299 { 300 m_State.StartTimeout(seconds, ref m_TriggerState); 301 } 302 303 /// <summary> 304 /// Override the default timeout value used by <see cref="InputAction.GetTimeoutCompletionPercentage"/>. 305 /// </summary> 306 /// <param name="seconds">Amount of total successive timeouts TODO</param> 307 /// <exception cref="ArgumentException"></exception> 308 /// <remarks> 309 /// By default, timeout completion will be entirely determine by the timeout that is currently 310 /// running, if any. However, some interactions (such as <see cref="Interactions.MultiTapInteraction"/>) 311 /// will have to run multiple timeouts in succession. Thus, completion of a single timeout is not 312 /// the same as completion of the interaction. 313 /// 314 /// You can use this method to account for this. 315 /// 316 /// Whenever a timeout completes, the timeout duration will automatically be accumulated towards 317 /// the total timeout completion time. 318 /// 319 /// <example> 320 /// <code> 321 /// // Let's say we're starting our first timeout and we know that we will run three timeouts 322 /// // in succession of 2 seconds each. By calling SetTotalTimeoutCompletionTime(), we can account for this. 323 /// SetTotalTimeoutCompletionTime(3 * 2); 324 /// 325 /// // Start the first timeout. When this timeout expires, it will automatically 326 /// // count one second towards the total timeout completion time. 327 /// SetTimeout(2); 328 /// </code> 329 /// </example> 330 /// </remarks> 331 /// <seealso cref="InputAction.GetTimeoutCompletionPercentage"/> 332 public void SetTotalTimeoutCompletionTime(float seconds) 333 { 334 if (seconds <= 0) 335 throw new ArgumentException("Seconds must be a positive value", nameof(seconds)); 336 337 m_State.SetTotalTimeoutCompletionTime(seconds, ref m_TriggerState); 338 } 339 340 /// <summary> 341 /// Read the value of the binding that triggered processing of the interaction. 342 /// </summary> 343 /// <typeparam name="TValue">Type of value to read from the binding. Must match the value type of the control 344 /// or composite in effect for the binding.</typeparam> 345 /// <returns>Value read from the binding.</returns> 346 public TValue ReadValue<TValue>() 347 where TValue : struct 348 { 349 return m_State.ReadValue<TValue>(m_TriggerState.bindingIndex, m_TriggerState.controlIndex); 350 } 351 352 internal InputActionState m_State; 353 internal Flags m_Flags; 354 internal InputActionState.TriggerState m_TriggerState; 355 356 internal int mapIndex => m_TriggerState.mapIndex; 357 358 internal int controlIndex => m_TriggerState.controlIndex; 359 360 internal int bindingIndex => m_TriggerState.bindingIndex; 361 362 internal int interactionIndex => m_TriggerState.interactionIndex; 363 364 [Flags] 365 internal enum Flags 366 { 367 TimerHasExpired = 1 << 1 368 } 369 } 370}