Unity SDK Docs 1.5.0-beta.6
Loading...
Searching...
No Matches
Player.cs
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 */
16using System;
17using System.Collections.Generic;
18using UnityEngine;
20
21#if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
22using UnityEngine.InputSystem;
23#endif
24
25namespace 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 GameBoard.Scan();
192
193 Wand.Scan();
194 scanningForPlayers = false;
195 }
196
197 internal static bool TryGetGlassesHandle(PlayerIndex playerIndex, out GlassesHandle glassesHandle)
198 {
199 if (playerIndex == PlayerIndex.None)
200 {
201 glassesHandle = new GlassesHandle();
202 return false;
203 }
204 var playerAvailable = Instance.players.TryGetValue(playerIndex, out var playerCore);
205 glassesHandle = playerCore?.GlassesHandle ?? new GlassesHandle();
206 return playerAvailable;
207 }
208
209 internal static bool TryGetPlayerIndex(GlassesHandle glassesHandle, out PlayerIndex playerIndex)
210 {
211 foreach (var keyValuePair in Instance.players)
212 {
213 var currentPlayerIndex = keyValuePair.Key;
214 var currentPlayerCore = keyValuePair.Value;
215 if (currentPlayerCore.GlassesHandle == glassesHandle)
216 {
217 playerIndex = currentPlayerIndex;
218 return true;
219 }
220 }
221 playerIndex = PlayerIndex.None;
222 return false;
223 }
224
225 internal static bool AllSupportedPlayersConnected()
226 {
227 if(!TiltFiveSingletonHelper.TryGetISceneInfo(out var sceneInfo))
228 {
229 return false;
230 }
231 var supportedPlayers = sceneInfo.GetSupportedPlayerCount();
232
233 for(PlayerIndex playerIndex = PlayerIndex.One; playerIndex <= (PlayerIndex)supportedPlayers; playerIndex++)
234 {
235 // If any supported player isn't connected, return false
236 if(!IsConnected(playerIndex))
237 {
238 return false;
239 }
240 }
241 // Otherwise they're all connected, and we can return true.
242 return true;
243 }
244
249 internal static bool TryAddPlayer(GlassesHandle glassesHandle, out PlayerIndex playerIndex)
250 {
251 var players = Instance.players;
252 if (players.Count >= GlassesSettings.MAX_SUPPORTED_GLASSES_COUNT)
253 {
254 playerIndex = PlayerIndex.None;
255 return false;
256 }
257
258 if(TryGetPlayerIndex(glassesHandle, out var existingPlayerIndex))
259 {
260 playerIndex = PlayerIndex.None;
261 return false; // This player already exists.
262 }
263
264 foreach (PlayerIndex currentPlayerIndex in Enum.GetValues(typeof(PlayerIndex)))
265 {
266 if (currentPlayerIndex == PlayerIndex.None)
267 {
268 continue;
269 }
270
271 // Assign the smallest (numerically) playerIndex that isn't already assigned.
272 // For example, if player #1 disappeared, and player #2 is still available,
273 // then we assign the specified glasses to a new player #1.
274 if (!players.ContainsKey(currentPlayerIndex))
275 {
276 Glasses.TryGetFriendlyName(glassesHandle, out var friendlyName);
277 players[currentPlayerIndex] = new PlayerCore(glassesHandle, friendlyName);
278
279 Log.Info($"Player {currentPlayerIndex} created. Glasses: {glassesHandle} (\"{friendlyName}\")");
280
281 // Default control should go to the lowest player index.
282 // If this playerIndex is lower than that of any other current players,
283 // reset the default glasses handle.
284
285
286 bool lowestPlayerIndex = false;
287
288 // TODO: Determine whether this is the lowest player index.
289 // Until then, default control stays with player 2 in the above scenario.
290
291 if (lowestPlayerIndex)
292 {
293 Glasses.SetDefaultGlassesHandle(glassesHandle);
294 }
295
296 playerIndex = currentPlayerIndex;
297 return true;
298 }
299 }
300
301 // We shouldn't ever reach this.
302 playerIndex = PlayerIndex.None;
303 return false;
304 }
305
306 internal static void OnDisable()
307 {
308 Glasses.OnDisable();
309 GameBoard.OnDisable();
310 Wand.OnDisable();
311 }
312
313 internal static bool TryGetFirstConnectedPlayer(out PlayerIndex playerIndex)
314 {
315 for(PlayerIndex i = PlayerIndex.One; i <= PlayerIndex.Four; i++)
316 {
317 if(IsConnected(i))
318 {
319 playerIndex = i;
320 return true;
321 }
322 }
323
324 playerIndex = PlayerIndex.None;
325 return false;
326 }
327
328#if UNITY_EDITOR
329
334 internal static void DrawGizmos(PlayerSettings playerSettings)
335 {
336 if (playerSettings == null)
337 {
338 return;
339 }
340
341 var gameBoardSettings = playerSettings.gameboardSettings;
342
343 if (gameBoardSettings.currentGameBoard != null)
344 {
345 gameBoardSettings.currentGameBoard.DrawGizmo(playerSettings.scaleSettings, gameBoardSettings);
346 }
347 }
348#endif
349
350 #endregion
351
352
353 #region Private Classes
354
355 private class PlayerCore
356 {
357 public readonly GlassesHandle GlassesHandle;
358 public readonly string FriendlyName;
359
360 public PlayerCore(GlassesHandle glassesHandle, string friendlyName)
361 {
362 GlassesHandle = glassesHandle;
363 FriendlyName = friendlyName;
364 }
365
366 internal void Update(PlayerSettings playerSettings, SpectatorSettings spectatorSettings)
367 {
368 if (!Glasses.Validate(playerSettings.glassesSettings, spectatorSettings, GlassesHandle))
369 {
370 Glasses.Reset(playerSettings.glassesSettings, spectatorSettings, GlassesHandle);
371 }
372 GetLatestPoseData(playerSettings, spectatorSettings);
373 }
374
375 internal void Reset(PlayerSettings playerSettings, SpectatorSettings spectatorSettings)
376 {
377 Glasses.Reset(playerSettings.glassesSettings, spectatorSettings, GlassesHandle);
378 }
379
380
384 private void GetLatestPoseData(PlayerSettings playerSettings, SpectatorSettings spectatorSettings)
385 {
386 var glassesSettings = playerSettings.glassesSettings;
387 var rightWandSettings = playerSettings.rightWandSettings;
388 var leftWandSettings = playerSettings.leftWandSettings;
389 var scaleSettings = playerSettings.scaleSettings;
390 var gameboardSettings = playerSettings.gameboardSettings;
391
392 Glasses.Update(GlassesHandle, glassesSettings, scaleSettings, gameboardSettings, spectatorSettings);
393 GameBoard.Update(GlassesHandle, gameboardSettings, scaleSettings);
394 Wand.Update(GlassesHandle, rightWandSettings, scaleSettings, gameboardSettings);
395 Wand.Update(GlassesHandle, leftWandSettings, scaleSettings, gameboardSettings);
396 }
397 }
398
399 #endregion
400
401
402 #region Public Classes
403
431 public class WaitUntilPlayerConnected : CustomYieldInstruction
432 {
433 public override bool keepWaiting => !TiltFive.Player.IsConnected(playerIndex);
434 private PlayerIndex playerIndex = PlayerIndex.One;
435
436 public WaitUntilPlayerConnected(PlayerIndex playerIndex)
437 {
438 this.playerIndex = playerIndex;
439 }
440 }
441
442 #endregion
443 }
444}
The Glasses API and runtime.
Definition Glasses.cs:35
static bool IsConnected(PlayerIndex playerIndex=PlayerIndex.One)
Indicate if the specified glasses are connected.
Definition Glasses.cs:404
The Logger.
Definition Log.cs:42
static void Info(string m, params object[] list)
INFO logging function call.
Definition Log.cs:140
Provides access to player settings and functionality.
Definition Player.cs:31
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
The Tilt Five manager.
bool TryGetPlayerSettings(PlayerIndex playerIndex, out PlayerSettings playerSettings)
Gets the player settings for the specified player.
The Tilt Five manager.
PlayerIndex
The Player index (e.g. Player One, Player Two, etc)