Tilt Five™ Unity API  1.4.1
Input.cs
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2020-2023 Tilt Five, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 using System;
17 using UnityEngine;
18 
19 #if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
20 using UnityEngine.InputSystem;
21 #endif
22 
23 using TiltFive.Logging;
24 
25 namespace TiltFive
26 {
30  public static class Input
31  {
32  #region Public Enums
33 
34  public enum WandButton : UInt32
35  {
36  T5 = 1 << 0,
37  One = 1 << 1,
38  Two = 1 << 2,
39  Three = 1 << 7,
40  Y = 1 << 3,
41  B = 1 << 4,
42  A = 1 << 5,
43  X = 1 << 6,
44  [Obsolete("WandButton.System is deprecated, please use Wandbutton.T5 instead.")]
45  System = T5,
46  [Obsolete("WandButton.Z is deprecated, please use Wandbutton.Three instead.")]
47  Z = Three,
48  }
49 
50  #endregion Public Enums
51 
52 
53  #region Private Fields
54 
55 #if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
56  internal static GlassesDevice[] glassesDevices = new GlassesDevice[PlayerSettings.MAX_SUPPORTED_PLAYERS];
57  internal static WandDevice[,] wandDevices = new WandDevice[PlayerSettings.MAX_SUPPORTED_PLAYERS, 2];
58 #endif
59 
60  #endregion
61 
62 
63  #region Public Functions
64 
74  public static bool GetButton(WandButton button,
75  ControllerIndex controllerIndex = ControllerIndex.Right, PlayerIndex playerIndex = PlayerIndex.One)
76  {
77  return TryGetButton(button, out var pressed, controllerIndex, playerIndex) && pressed;
78  }
79 
89  public static bool TryGetButton(WandButton button, out bool pressed,
90  ControllerIndex controllerIndex = ControllerIndex.Right, PlayerIndex playerIndex = PlayerIndex.One)
91  {
92  if(!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle))
93  {
94  pressed = false;
95  return false;
96  }
97 
98  return Wand.TryGetButton(button, out pressed, glassesHandle, controllerIndex);
99  }
100 
109  public static bool GetButtonDown(WandButton button,
110  ControllerIndex controllerIndex = ControllerIndex.Right, PlayerIndex playerIndex = PlayerIndex.One)
111  {
112  return TryGetButtonDown(button, out var buttonDown, controllerIndex, playerIndex) && buttonDown;
113  }
114 
124  public static bool TryGetButtonDown(WandButton button, out bool buttonDown,
125  ControllerIndex controllerIndex = ControllerIndex.Right, PlayerIndex playerIndex = PlayerIndex.One)
126  {
127  // The specified player's glasses aren't even connected, let alone the wand. No way to get a rising edge here.
128  if(!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle))
129  {
130  buttonDown = false;
131  return false;
132  }
133 
134  return Wand.TryGetButtonDown(button, out buttonDown, glassesHandle, controllerIndex);
135  }
136 
144  public static bool GetButtonUp(WandButton button,
145  ControllerIndex controllerIndex = ControllerIndex.Right, PlayerIndex playerIndex = PlayerIndex.One)
146  {
147  return TryGetButtonUp(button, out var buttonUp, controllerIndex, playerIndex) && buttonUp;
148  }
149 
160  public static bool TryGetButtonUp(WandButton button, out bool buttonUp,
161  ControllerIndex controllerIndex = ControllerIndex.Right, PlayerIndex playerIndex = PlayerIndex.One)
162  {
163  // TODO: Tweak the Wand.cs TryGetButtonDown to check if the glasses disconnected this frame.
164  // If it did, and the button was held last frame, then buttonUp can be true.
165  if(!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle))
166  {
167  buttonUp = false;
168  return false;
169  }
170 
171  return Wand.TryGetButtonUp(button, out buttonUp, glassesHandle, controllerIndex);
172  }
173 
180  public static Vector2 GetStickTilt(ControllerIndex controllerIndex = ControllerIndex.Right, PlayerIndex playerIndex = PlayerIndex.One)
181  {
182  if(TryGetStickTilt(out var stickTilt, controllerIndex, playerIndex))
183  {
184  return stickTilt;
185  }
186  return Vector2.zero;
187  }
188 
196  public static bool TryGetStickTilt(out Vector2 stickTilt,
197  ControllerIndex controllerIndex = ControllerIndex.Right, PlayerIndex playerIndex = PlayerIndex.One)
198  {
199  if(!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle))
200  {
201  stickTilt = Vector2.zero;
202  return false;
203  }
204  return Wand.TryGetStickTilt(out stickTilt, glassesHandle, controllerIndex);
205  }
206 
214  public static float GetTrigger(ControllerIndex controllerIndex = ControllerIndex.Right, PlayerIndex playerIndex = PlayerIndex.One)
215  {
216  if(TryGetTrigger(out var triggerDisplacement, controllerIndex, playerIndex))
217  {
218  return triggerDisplacement;
219  }
220  return 0f;
221  }
222 
231  public static bool TryGetTrigger(out float triggerDisplacement,
232  ControllerIndex controllerIndex = ControllerIndex.Right, PlayerIndex playerIndex = PlayerIndex.One)
233  {
234  if (!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle))
235  {
236  triggerDisplacement = 0f;
237  return false;
238  }
239  return Wand.TryGetTrigger(out triggerDisplacement, glassesHandle, controllerIndex);
240  }
241 
247  public static bool GetWandAvailability(ControllerIndex controllerIndex = ControllerIndex.Right)
248  {
249  return Wand.TryCheckConnected(out var connected, PlayerIndex.One, controllerIndex) && connected;
250  }
251 
252  public static void Update()
253  {
254  // Deleting this function would be appropriate, but since it's public,
255  // there's a small chance it could break someone's code...
256  // ...not that I can think of a use-case that involves calling TiltFive.Input.Update()
257  }
258 
259  #endregion Public Functions
260 
261 
262  #region Internal Functions
263 
264  internal static bool GetButton(this T5_ControllerState controllerState, WandButton button)
265  {
266  var buttonsState = controllerState.ButtonsState;
267 
268  switch (button)
269  {
270  case WandButton.T5:
271  return buttonsState.T5;
272  case WandButton.One:
273  return buttonsState.One;
274  case WandButton.Two:
275  return buttonsState.Two;
276  case WandButton.Y:
277  return buttonsState.Y;
278  case WandButton.B:
279  return buttonsState.B;
280  case WandButton.A:
281  return buttonsState.A;
282  case WandButton.X:
283  return buttonsState.X;
284  case WandButton.Three:
285  return buttonsState.Three;
286  default:
287  throw new ArgumentException("Invalid WandButton argument - enum value does not exist");
288  }
289 
290  }
291 
292  internal static bool TryGetButton(this T5_ControllerState controllerState, WandButton button)
293  {
294  return controllerState.ButtonsValid && controllerState.GetButton(button);
295  }
296 
297  internal static Vector2 TryGetStick(this T5_ControllerState controllerState)
298  {
299  return controllerState.AnalogValid ? controllerState.Stick : Vector2.zero;
300  }
301 
302  internal static float TryGetTrigger(this T5_ControllerState controllerState)
303  {
304  return controllerState.AnalogValid ? controllerState.Trigger : 0f;
305  }
306 
307 #if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
308  internal static GlassesDevice GetGlassesDevice(PlayerIndex playerIndex)
309  {
310  return glassesDevices[(int)playerIndex - 1];
311  }
312 
313  internal static WandDevice GetWandDevice(PlayerIndex playerIndex, ControllerIndex controllerIndex)
314  {
315  return wandDevices[(int)playerIndex - 1, (int)controllerIndex];
316  }
317 #endif
318 
319  #endregion Internal Functions
320 
321 
322  #region Private Functions
323 
324  static Input()
325  {
326  Wand.Scan();
327  }
328 
329 #if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
330  internal static void AddGlassesDevice(PlayerIndex playerIndex)
331  {
332  var glassesDevices = Input.glassesDevices;
333  int i = (int)playerIndex - 1;
334 
335  // Create a GlassesDevice if necessary
336  if (glassesDevices[i] == null)
337  {
338  var preexistingGlassesDevice = InputSystem.GetDevice<GlassesDevice>($"Player{playerIndex}");
339  if (preexistingGlassesDevice != null)
340  {
341  glassesDevices[i] = preexistingGlassesDevice;
342  glassesDevices[i].PlayerIndex = playerIndex;
343  }
344  else
345  {
346  glassesDevices[i] = InputSystem.AddDevice<GlassesDevice>($"T5 Glasses - Player {playerIndex}");
347  glassesDevices[i].PlayerIndex = playerIndex;
348  InputSystem.AddDeviceUsage(glassesDevices[i], $"Player{playerIndex}");
349  }
350 
351  }
352  else if (!glassesDevices[i].added)
353  {
354  InputSystem.AddDevice(glassesDevices[i]);
355  }
356  }
357 
358  internal static void AddWandDevice(PlayerIndex playerIndex, ControllerIndex controllerIndex)
359  {
360  var wandDevices = Input.wandDevices;
361  int i = (int)playerIndex - 1;
362  int j = (int)controllerIndex;
363 
364  // Unfortunately, the enum ControllerIndex.Primary still exists, and Unity seems to have a habit
365  // of substituting its display name when we're trying to get the display name for ControllerIndex.Right.
366  // TODO: Localize
367  var handednessLabel = controllerIndex == ControllerIndex.Right ? "Right" : "Left";
368 
369  // If we already know about a wandDevice corresponding to our input parameters, add it to the input system if it isn't already added
370  if (wandDevices[i, j] != null && !wandDevices[i, j].added)
371  {
372  var wandDevice = wandDevices[i, j];
373  InputSystem.AddDevice(wandDevice);
374  wandDevice.ControllerIndex = controllerIndex;
375  InputSystem.QueueConfigChangeEvent(wandDevice);
376  InputSystem.AddDevice(wandDevice);
377  }
378  else
379  {
380  // Otherwise, ask the input system if it has a matching wandDevice.
381  // This corner case (in which a matching wandDevice exists, but the static field in Player.cs is empty)
382  // can occur when reloading scripts. Unity's input system keeps the device alive, but this class suffers amnesia.
383  WandDevice currentWandDevice = InputSystem.GetDevice<WandDevice>($"Player{playerIndex}-{handednessLabel}Hand");
384 
385  // If the input system does have a matching wandDevice, just remember it and we're done
386  if (currentWandDevice != null)
387  {
388  wandDevices[i, j] = currentWandDevice;
389  }
390  // Otherwise, add a brand new wandDevice to the input system and remember it.
391  else
392  {
393  wandDevices[i, j] = InputSystem.AddDevice<WandDevice>($"T5 Wand - P{(int)playerIndex} {handednessLabel}");
394  }
395 
396  wandDevices[i, j].playerIndex = playerIndex;
397  wandDevices[i, j].ControllerIndex = controllerIndex;
398  InputSystem.AddDeviceUsage(wandDevices[i, j], $"Player{playerIndex}");
399  InputSystem.AddDeviceUsage(wandDevices[i, j], $"Player{playerIndex}-{handednessLabel}Hand");
400  InputSystem.AddDeviceUsage(wandDevices[i, j], wandDevices[i, j].ControllerIndex == ControllerIndex.Left ? CommonUsages.LeftHand : CommonUsages.RightHand);
401  InputSystem.QueueConfigChangeEvent(wandDevices[i, j]);
402  }
403  }
404 
405  internal static void RemoveGlassesDevice(PlayerIndex playerIndex)
406  {
407  var glassesDevices = Input.glassesDevices;
408  int i = (int)playerIndex - 1;
409 
410  // Destroy a GlassesDevice if it exists
411  if (glassesDevices[i] != null)
412  {
413  var preexistingGlassesDevice = InputSystem.GetDevice<GlassesDevice>($"Player{playerIndex}");
414  if (preexistingGlassesDevice != null)
415  {
416  InputSystem.RemoveDevice(preexistingGlassesDevice);
417  glassesDevices[i] = null;
418  }
419  }
420  }
421 
422  internal static void RemoveWandDevice(PlayerIndex playerIndex, ControllerIndex controllerIndex)
423  {
424  var wandDevices = Input.wandDevices;
425  int i = (int)playerIndex - 1;
426  int j = (int)controllerIndex;
427 
428  // Unfortunately, the enum ControllerIndex.Primary still exists, and Unity seems to have a habit
429  // of substituting its display name when we're trying to get the display name for ControllerIndex.Right.
430  // TODO: Localize
431  var handednessLabel = controllerIndex == ControllerIndex.Right ? "Right" : "Left";
432 
433  // If we already know about a wandDevice corresponding to our input parameters, Remove the Device and let the system know if it's disappearance
434  if (wandDevices[i, j] != null && !wandDevices[i, j].added)
435  {
436  var wandDevice = wandDevices[i, j];
437  InputSystem.RemoveDevice(wandDevice);
438  InputSystem.QueueConfigChangeEvent(wandDevice);
439  wandDevices[i, j] = null;
440  }
441  else
442  {
443  // Otherwise, ask the input system if it has a matching wandDevice.
444  // This corner case (in which a matching wandDevice exists, but the static field in Player.cs is empty)
445  // can occur when reloading scripts. Unity's input system keeps the device alive, but this class suffers amnesia.
446  WandDevice currentWandDevice = InputSystem.GetDevice<WandDevice>($"Player{playerIndex}-{handednessLabel}Hand");
447 
448  // If the input system does have a matching wandDevice, just destroy it
449  if (currentWandDevice != null)
450  {
451  InputSystem.RemoveDevice(currentWandDevice);
452  InputSystem.QueueConfigChangeEvent(currentWandDevice);
453  wandDevices[i, j] = null;
454  }
455  }
456  }
457 #endif
458 
459  #endregion Private Functions
460  }
461 
462 }
TiltFive.Input.WandButton WandButton
Definition: Wand.cs:28
Provides access to Wand inputs
Definition: Input.cs:31
static bool TryGetButtonDown(WandButton button, out bool buttonDown, ControllerIndex controllerIndex=ControllerIndex.Right, PlayerIndex playerIndex=PlayerIndex.One)
Whether the indicated wand button was pressed during this frame. Fails if the wand is unavailable.
Definition: Input.cs:124
static bool TryGetTrigger(out float triggerDisplacement, ControllerIndex controllerIndex=ControllerIndex.Right, PlayerIndex playerIndex=PlayerIndex.One)
Gets the degree to which the trigger is depressed, from 0.0 (released) to 1.0 (fully depressed)....
Definition: Input.cs:231
static bool TryGetButtonUp(WandButton button, out bool buttonUp, ControllerIndex controllerIndex=ControllerIndex.Right, PlayerIndex playerIndex=PlayerIndex.One)
Whether the indicated wand button was released during this frame. Fails if the wand is unavailable.
Definition: Input.cs:160
static Vector2 GetStickTilt(ControllerIndex controllerIndex=ControllerIndex.Right, PlayerIndex playerIndex=PlayerIndex.One)
Gets the direction and magnitude of the stick's tilt for the indicated wand.
Definition: Input.cs:180
static Input()
Definition: Input.cs:324
static bool GetButtonDown(WandButton button, ControllerIndex controllerIndex=ControllerIndex.Right, PlayerIndex playerIndex=PlayerIndex.One)
Whether the indicated wand button was pressed during this frame.
Definition: Input.cs:109
static bool TryGetStickTilt(out Vector2 stickTilt, ControllerIndex controllerIndex=ControllerIndex.Right, PlayerIndex playerIndex=PlayerIndex.One)
Gets the direction and magnitude of the stick's tilt for the indicated wand. Fails if the wand is una...
Definition: Input.cs:196
static bool GetButton(WandButton button, ControllerIndex controllerIndex=ControllerIndex.Right, PlayerIndex playerIndex=PlayerIndex.One)
Whether the indicated wand button is currently being pressed.
Definition: Input.cs:74
static void Update()
Definition: Input.cs:252
static bool TryGetButton(WandButton button, out bool pressed, ControllerIndex controllerIndex=ControllerIndex.Right, PlayerIndex playerIndex=PlayerIndex.One)
Whether the indicated wand button is currently being pressed. Fails if the wand is unavailable.
Definition: Input.cs:89
static bool GetWandAvailability(ControllerIndex controllerIndex=ControllerIndex.Right)
Gets the connection status of the indicated wand.
Definition: Input.cs:247
static float GetTrigger(ControllerIndex controllerIndex=ControllerIndex.Right, PlayerIndex playerIndex=PlayerIndex.One)
Gets the degree to which the trigger is depressed, from 0.0 (released) to 1.0 (fully depressed).
Definition: Input.cs:214
static bool GetButtonUp(WandButton button, ControllerIndex controllerIndex=ControllerIndex.Right, PlayerIndex playerIndex=PlayerIndex.One)
Whether the indicated wand button was released during this frame.
Definition: Input.cs:144
Provides access to player settings and functionality.
Definition: Player.cs:31
static uint MAX_SUPPORTED_PLAYERS
The Wand API and runtime.
Definition: Wand.cs:56
static bool TryCheckConnected(out bool connected, PlayerIndex playerIndex, ControllerIndex controllerIndex=ControllerIndex.Right)
Gets the connection status of the indicated wand.
Definition: Wand.cs:220
Definition: Log.cs:21
ControllerIndex
Since wands are all physically identical (they have no "handedness"), it doesn't make sense to addres...
PlayerIndex
The Player index (e.g. Player One, Player Two, etc)