Tilt Five™ Unity API  1.4.1
Player.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 System.Collections.Generic;
18 using UnityEngine;
19 using TiltFive.Logging;
20 
21 #if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
22 using UnityEngine.InputSystem;
23 #endif
24 
25 namespace TiltFive
26 {
30  public class Player : Singleton<Player>
31  {
32  #region Private Fields
33 
34  private Dictionary<PlayerIndex, PlayerCore> players = new Dictionary<PlayerIndex, PlayerCore>();
35 
36  internal static bool scanningForPlayers = false;
37 
38  #endregion
39 
40 
41  #region Public Functions
42 
48  public static bool IsConnected(PlayerIndex playerIndex)
49  {
50  return playerIndex != PlayerIndex.None && Instance.players.TryGetValue(playerIndex, out var playerCore);
51  }
52 
53 
60  public static bool TryGetFriendlyName(PlayerIndex playerIndex, out string friendlyName)
61  {
62  if (playerIndex == PlayerIndex.None || !Instance.players.TryGetValue(playerIndex, out var playerCore))
63  {
64  friendlyName = "";
65  return false;
66  }
67 
68  friendlyName = playerCore.FriendlyName;
69  return true;
70  }
71 
79  public static bool TryGetSettings(PlayerIndex playerIndex, out PlayerSettings playerSettings)
80  {
81  // We need to obtain a TiltFiveManager2 or TiltFiveManager to query for player settings.
82  // Since we don't know which (if either) is available, we'll use TiltFiveSingletonHelper.
83  if (playerIndex == PlayerIndex.None || !TiltFiveSingletonHelper.TryGetISceneInfo(out var sceneInfo))
84  {
85  playerSettings = null;
86  return false;
87  }
88 
89  if (sceneInfo is TiltFiveManager2 tiltFiveManager2
90  && tiltFiveManager2.TryGetPlayerSettings(playerIndex, out var resultingPlayerSettings))
91  {
92  playerSettings = resultingPlayerSettings;
93  return true;
94  }
95 
96  // If we are dealing with a TiltFiveManager, the scene is singleplayer-only; reject other player indices.
97  if (sceneInfo is TiltFiveManager tiltFiveManager && playerIndex == PlayerIndex.One)
98  {
99  playerSettings = tiltFiveManager.playerSettings;
100  return true;
101  }
102 
103  playerSettings = null;
104  return false;
105  }
106 
107  #endregion
108 
109 
110  #region Internal Functions
111 
116  internal static void Update(PlayerSettings playerSettings, SpectatorSettings spectatorSettings)
117  {
118  if (scanningForPlayers)
119  {
120  return;
121  }
122 
123  if (playerSettings != null && Instance.players.TryGetValue(playerSettings.PlayerIndex, out var playerCore))
124  {
125  var playerIndex = playerSettings.PlayerIndex;
126 
127  // Check to make sure the glasses for this player are still connected.
128  if (!Glasses.IsConnected(playerCore.GlassesHandle))
129  {
130  // This player's glasses are gone, so it's time to destroy this PlayerCore.
131  Log.Info($"Player {playerIndex} was removed due to their glasses (handle: {playerCore.GlassesHandle}) being disconnected.");
132  Instance.players.Remove(playerIndex);
133  return;
134  }
135 
136  playerCore.Update(playerSettings, spectatorSettings);
137  }
138  }
139 
144  internal static void Reset(PlayerSettings playerSettings, SpectatorSettings spectatorSettings)
145  {
146  var playerIndex = playerSettings.PlayerIndex;
147 
148  if (playerSettings != null && Instance.players.TryGetValue(playerIndex, out var playerCore))
149  {
150  if (Glasses.IsConnected(playerCore.GlassesHandle))
151  {
152  playerCore.Reset(playerSettings, spectatorSettings);
153  }
154  else
155  {
156  Validate(playerSettings);
157  }
158  }
159  }
160 
165  internal static void Validate(PlayerSettings playerSettings)
166  {
167  if (playerSettings == null)
168  {
169  return;
170  }
171 
172  var scaleSettings = playerSettings.scaleSettings;
173  scaleSettings.contentScaleRatio = Mathf.Clamp(scaleSettings.contentScaleRatio, ScaleSettings.MIN_CONTENT_SCALE_RATIO, float.MaxValue);
174 
175  playerSettings.Validate();
176  }
177 
178 
182  internal static void ScanForNewPlayers()
183  {
184  if (scanningForPlayers)
185  {
186  return;
187  }
188  scanningForPlayers = true;
189  Glasses.Scan();
190 
191  Wand.Scan();
192  scanningForPlayers = false;
193  }
194 
195  internal static bool TryGetGlassesHandle(PlayerIndex playerIndex, out GlassesHandle glassesHandle)
196  {
197  if (playerIndex == PlayerIndex.None)
198  {
199  glassesHandle = new GlassesHandle();
200  return false;
201  }
202  var playerAvailable = Instance.players.TryGetValue(playerIndex, out var playerCore);
203  glassesHandle = playerCore?.GlassesHandle ?? new GlassesHandle();
204  return playerAvailable;
205  }
206 
207  internal static bool TryGetPlayerIndex(GlassesHandle glassesHandle, out PlayerIndex playerIndex)
208  {
209  foreach (var keyValuePair in Instance.players)
210  {
211  var currentPlayerIndex = keyValuePair.Key;
212  var currentPlayerCore = keyValuePair.Value;
213  if (currentPlayerCore.GlassesHandle == glassesHandle)
214  {
215  playerIndex = currentPlayerIndex;
216  return true;
217  }
218  }
219  playerIndex = PlayerIndex.None;
220  return false;
221  }
222 
223  internal static bool AllSupportedPlayersConnected()
224  {
225  if(!TiltFiveSingletonHelper.TryGetISceneInfo(out var sceneInfo))
226  {
227  return false;
228  }
229  var supportedPlayers = sceneInfo.GetSupportedPlayerCount();
230 
231  for(PlayerIndex playerIndex = PlayerIndex.One; playerIndex <= (PlayerIndex)supportedPlayers; playerIndex++)
232  {
233  // If any supported player isn't connected, return false
234  if(!IsConnected(playerIndex))
235  {
236  return false;
237  }
238  }
239  // Otherwise they're all connected, and we can return true.
240  return true;
241  }
242 
247  internal static bool TryAddPlayer(GlassesHandle glassesHandle, out PlayerIndex playerIndex)
248  {
249  var players = Instance.players;
250  if (players.Count >= GlassesSettings.MAX_SUPPORTED_GLASSES_COUNT)
251  {
252  playerIndex = PlayerIndex.None;
253  return false;
254  }
255 
256  if(TryGetPlayerIndex(glassesHandle, out var existingPlayerIndex))
257  {
258  playerIndex = PlayerIndex.None;
259  return false; // This player already exists.
260  }
261 
262  foreach (PlayerIndex currentPlayerIndex in Enum.GetValues(typeof(PlayerIndex)))
263  {
264  if (currentPlayerIndex == PlayerIndex.None)
265  {
266  continue;
267  }
268 
269  // Assign the smallest (numerically) playerIndex that isn't already assigned.
270  // For example, if player #1 disappeared, and player #2 is still available,
271  // then we assign the specified glasses to a new player #1.
272  if (!players.ContainsKey(currentPlayerIndex))
273  {
274  Glasses.TryGetFriendlyName(glassesHandle, out var friendlyName);
275  players[currentPlayerIndex] = new PlayerCore(glassesHandle, friendlyName);
276 
277  Log.Info($"Player {currentPlayerIndex} created. Glasses: {glassesHandle} (\"{friendlyName}\")");
278 
279  // Default control should go to the lowest player index.
280  // If this playerIndex is lower than that of any other current players,
281  // reset the default glasses handle.
282 
283 
284  bool lowestPlayerIndex = false;
285 
286  // TODO: Determine whether this is the lowest player index.
287  // Until then, default control stays with player 2 in the above scenario.
288 
289  if (lowestPlayerIndex)
290  {
291  Glasses.SetDefaultGlassesHandle(glassesHandle);
292  }
293 
294  playerIndex = currentPlayerIndex;
295  return true;
296  }
297  }
298 
299  // We shouldn't ever reach this.
300  playerIndex = PlayerIndex.None;
301  return false;
302  }
303 
304  internal static void OnDisable()
305  {
306  Glasses.OnDisable();
307  Wand.OnDisable();
308  }
309 
310  internal static bool TryGetFirstConnectedPlayer(out PlayerIndex playerIndex)
311  {
312  for(PlayerIndex i = PlayerIndex.One; i <= PlayerIndex.Four; i++)
313  {
314  if(IsConnected(i))
315  {
316  playerIndex = i;
317  return true;
318  }
319  }
320 
321  playerIndex = PlayerIndex.None;
322  return false;
323  }
324 
325 #if UNITY_EDITOR
326 
331  internal static void DrawGizmos(PlayerSettings playerSettings)
332  {
333  if (playerSettings == null)
334  {
335  return;
336  }
337 
338  var gameBoardSettings = playerSettings.gameboardSettings;
339 
340  if (gameBoardSettings.currentGameBoard != null)
341  {
342  gameBoardSettings.currentGameBoard.DrawGizmo(playerSettings.scaleSettings, gameBoardSettings);
343  }
344  }
345 #endif
346 
347  #endregion
348 
349 
350  #region Private Classes
351 
352  private class PlayerCore
353  {
354  public readonly GlassesHandle GlassesHandle;
355  public readonly string FriendlyName;
356 
357  public PlayerCore(GlassesHandle glassesHandle, string friendlyName)
358  {
359  GlassesHandle = glassesHandle;
360  FriendlyName = friendlyName;
361  }
362 
363  internal void Update(PlayerSettings playerSettings, SpectatorSettings spectatorSettings)
364  {
365  if (!Glasses.Validate(playerSettings.glassesSettings, spectatorSettings, GlassesHandle))
366  {
367  Glasses.Reset(playerSettings.glassesSettings, spectatorSettings, GlassesHandle);
368  }
369  GetLatestPoseData(playerSettings, spectatorSettings);
370  }
371 
372  internal void Reset(PlayerSettings playerSettings, SpectatorSettings spectatorSettings)
373  {
374  Glasses.Reset(playerSettings.glassesSettings, spectatorSettings, GlassesHandle);
375  }
376 
377 
381  private void GetLatestPoseData(PlayerSettings playerSettings, SpectatorSettings spectatorSettings)
382  {
383  var glassesSettings = playerSettings.glassesSettings;
384  var rightWandSettings = playerSettings.rightWandSettings;
385  var leftWandSettings = playerSettings.leftWandSettings;
386  var scaleSettings = playerSettings.scaleSettings;
387  var gameboardSettings = playerSettings.gameboardSettings;
388 
389  Glasses.Update(GlassesHandle, glassesSettings, scaleSettings, gameboardSettings, spectatorSettings);
390  Wand.Update(GlassesHandle, rightWandSettings, scaleSettings, gameboardSettings);
391  Wand.Update(GlassesHandle, leftWandSettings, scaleSettings, gameboardSettings);
392  }
393  }
394 
395  #endregion
396 
397 
398  #region Public Classes
399 
427  public class WaitUntilPlayerConnected : CustomYieldInstruction
428  {
429  public override bool keepWaiting => !TiltFive.Player.IsConnected(playerIndex);
431 
433  {
434  this.playerIndex = playerIndex;
435  }
436  }
437 
438  #endregion
439  }
440 }
The Glasses API and runtime.
Definition: Glasses.cs:35
static void Reset(GlassesSettings glassesSettings, SpectatorSettings spectatorSettings=null, PlayerIndex playerIndex=PlayerIndex.None)
Reset this T:TiltFive.Glasses.
Definition: Glasses.cs:317
static void Update(GlassesSettings glassesSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
Updates this T:TiltFive.Glasses.
Definition: Glasses.cs:342
static bool Validate(GlassesSettings glassesSettings, SpectatorSettings spectatorSettings=null, PlayerIndex playerIndex=PlayerIndex.One)
Validates the specified glassesSettings with the specified glasses core.
Definition: Glasses.cs:332
The Logger.
Definition: Log.cs:42
static void Info(string m, params object[] list)
INFO logging function call.
Definition: Log.cs:140
readonly string FriendlyName
Definition: Player.cs:355
PlayerCore(GlassesHandle glassesHandle, string friendlyName)
Definition: Player.cs:357
readonly GlassesHandle GlassesHandle
Definition: Player.cs:354
void GetLatestPoseData(PlayerSettings playerSettings, SpectatorSettings spectatorSettings)
Obtains the latest pose for all trackable objects.
Definition: Player.cs:381
Suspends coroutine execution until the provided player has connected.
Definition: Player.cs:428
WaitUntilPlayerConnected(PlayerIndex playerIndex)
Definition: Player.cs:432
Provides access to player settings and functionality.
Definition: Player.cs:31
Dictionary< PlayerIndex, PlayerCore > players
Definition: Player.cs:34
static bool TryGetSettings(PlayerIndex playerIndex, out PlayerSettings playerSettings)
Obtains the PlayerSettings corresponding to the specified player.
Definition: Player.cs:79
static bool IsConnected(PlayerIndex playerIndex)
Determines whether the specified player has an associated pair of glasses connected.
Definition: Player.cs:48
static bool TryGetFriendlyName(PlayerIndex playerIndex, out string friendlyName)
Attempts to get the friendly name assigned to the specified player's glasses.
Definition: Player.cs:60
ScaleSettings scaleSettings
GameBoardSettings gameboardSettings
WandSettings leftWandSettings
GlassesSettings glassesSettings
WandSettings rightWandSettings
The Tilt Five manager.
bool TryGetPlayerSettings(PlayerIndex playerIndex, out PlayerSettings playerSettings)
Gets the player settings for the specified player.
The Tilt Five manager.
static bool TryGetISceneInfo(out ISceneInfo sceneInfo)
The Wand API and runtime.
Definition: Wand.cs:56
static void Update(WandSettings wandSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings, PlayerIndex playerIndex=PlayerIndex.One)
Definition: Wand.cs:305
Definition: Log.cs:21
PlayerIndex
The Player index (e.g. Player One, Player Two, etc)