浏览代码

Removed Relay related user synch limiting behaviour in LobbyUser. Synching is all done in LobbySynchronizer.

Renamed Multiple previous callback methods in GameManager to a more future-tense version.
Removed all magic strings from LobbyConverters to cached nameof() constants.
/main/staging/2021_Upgrade/Async_Refactor
当前提交
9a25d84e
共有 11 个文件被更改,包括 164 次插入185 次删除
  1. 90
      Assets/Scripts/GameLobby/Game/GameManager.cs
  2. 90
      Assets/Scripts/GameLobby/Game/LobbyUser.cs
  3. 50
      Assets/Scripts/GameLobby/Game/LocalLobby.cs
  4. 41
      Assets/Scripts/GameLobby/Lobby/LobbyConverters.cs
  5. 6
      Assets/Scripts/GameLobby/Lobby/LobbyManager.cs
  6. 10
      Assets/Scripts/GameLobby/Lobby/LobbySynchronizer.cs
  7. 8
      Assets/Scripts/GameLobby/Relay/RelayUtpClient.cs
  8. 4
      Assets/Scripts/GameLobby/Relay/RelayUtpHost.cs
  9. 2
      Assets/Scripts/GameLobby/UI/JoinMenuUI.cs
  10. 4
      Assets/Scripts/GameLobby/UI/RecolorForLobbyType.cs
  11. 44
      Assets/Scripts/GameLobby/UI/ShowWhenLobbyStateUI.cs

90
Assets/Scripts/GameLobby/Game/GameManager.cs


using UnityEngine;
#if UNITY_EDITOR
using ParrelSync;
#endif
#endif
namespace LobbyRelaySample
{

#endregion
public LocalLobby LocalLobby => m_LocalLobby;
public LobbyUser LocalUser => m_LocalUser;
public Action<GameState> onGameStateChanged;
public GameState LocalGameState { get; private set; }
public LobbyManager LobbyManager { get; private set; }
LocalMenuState m_LocalMenuState = new LocalMenuState();
LobbyUser m_LocalUser;

List<vivox.VivoxUserHandler> m_vivoxUserHandlers;
LobbyColor m_lobbyColorFilter;
static GameManager m_GameManagerInstance;

if (lobby != null)
{
LobbyConverters.RemoteToLocal(lobby, m_LocalLobby);
OnCreatedLobby();
CreateLobby();
OnFailedJoin();
SetGameState(GameState.JoinMenu);
}
else if (type == MessageType.JoinLobbyRequest)
{

if (lobby != null)
{
LobbyConverters.RemoteToLocal(lobby, m_LocalLobby);
OnJoinedLobby();
JoinLobby();
OnFailedJoin();
SetGameState(GameState.JoinMenu);
}
else if (type == MessageType.QueryLobbies)
{

if (qr != null)
OnLobbiesQueried(LobbyConverters.QueryToLocalList(qr));
SetCurrentLobbies(LobbyConverters.QueryToLocalList(qr));
OnLobbyQueryFailed();
m_LobbyServiceData.State = LobbyQueryState.Error;
}
else if (type == MessageType.QuickJoin)
{

LobbyConverters.RemoteToLocal(lobby, m_LocalLobby);
OnJoinedLobby();
JoinLobby();
OnFailedJoin();
SetGameState(GameState.JoinMenu);
}
}
else if (type == MessageType.RenameRequest)

{
Locator.Get.Messenger.OnReceiveMessage(MessageType.DisplayErrorPopup, "Empty Name not allowed."); // Lobby error type, then HTTP error type.
Locator.Get.Messenger.OnReceiveMessage(MessageType.DisplayErrorPopup,
"Empty Name not allowed."); // Lobby error type, then HTTP error type.
return;
}

}
else if (type == MessageType.StartCountdown)
{
m_LocalLobby.State = LobbyState.CountDown;
m_LocalLobby.LobbyState = LobbyState.CountDown;
m_LocalLobby.State = LobbyState.Lobby;
m_LocalLobby.LobbyState = LobbyState.Lobby;
}
else if (type == MessageType.CompleteCountdown)
{

else if (type == MessageType.ConfirmInGameState)
{
m_LocalUser.UserStatus = UserStatus.InGame;
m_LocalLobby.State = LobbyState.InGame;
m_LocalLobby.LobbyState = LobbyState.InGame;
m_LocalLobby.State = LobbyState.Lobby;
m_LocalLobby.LobbyState = LobbyState.Lobby;
#region Setup
#region Setup
async void Awake()
{

void InitializeLocalValues()
{
m_LocalLobby = new LocalLobby {State = LobbyState.Lobby};
m_LocalLobby = new LocalLobby { LobbyState = LobbyState.Lobby };
// before the LocalLobby is populated during lobby join,
// so the LocalLobby must know about it already when that happens.
// before the LocalLobby is populated during lobby join,
// so the LocalLobby must know about it already when that happens.
void BeginObservers()
void BeginObservers()
{
foreach (var gameStateObs in m_LocalMenuStateObservers)
gameStateObs.BeginObserving(m_LocalMenuState);

void SetGameState(GameState state)
{
bool isLeavingLobby = (state == GameState.Menu || state == GameState.JoinMenu) && m_LocalMenuState.State == GameState.Lobby;
m_LocalMenuState.State = state;
bool isLeavingLobby = (state == GameState.Menu || state == GameState.JoinMenu) &&
LocalGameState == GameState.Lobby;
LocalGameState = state;
OnLeftLobby();
LeaveLobby();
onGameStateChanged.Invoke(LocalGameState);
void OnLobbiesQueried(IEnumerable<LocalLobby> lobbies)
void SetCurrentLobbies(IEnumerable<LocalLobby> lobbies)
{
var newLobbyDict = new Dictionary<string, LocalLobby>();
foreach (var lobby in lobbies)

m_LobbyServiceData.CurrentLobbies = newLobbyDict;
}
void OnLobbyQueryFailed()
{
m_LobbyServiceData.State = LobbyQueryState.Error;
}
void OnCreatedLobby()
void CreateLobby()
OnJoinedLobby();
JoinLobby();
void OnJoinedLobby()
void JoinLobby()
void OnLeftLobby()
void LeaveLobby()
{
m_LocalUser.ResetState();
#pragma warning disable 4014

}
}
/// <summary>
/// Back to Join menu if we fail to join for whatever reason.
/// </summary>
void OnFailedJoin()
{
SetGameState(GameState.JoinMenu);
}
void StartVivoxLogin()
{

IEnumerator RetryConnection(Action doConnection, string lobbyId)
{
yield return new WaitForSeconds(5);
if (m_LocalLobby != null && m_LocalLobby.LobbyID == lobbyId && !string.IsNullOrEmpty(lobbyId)) // Ensure we didn't leave the lobby during this waiting period.
if (m_LocalLobby != null && m_LocalLobby.LobbyID == lobbyId && !string.IsNullOrEmpty(lobbyId)
) // Ensure we didn't leave the lobby during this waiting period.
doConnection?.Invoke();
}

void ResetLocalLobby()
{
m_LocalLobby.CopyObserved(new LocalLobby.LobbyData(), new Dictionary<string, LobbyUser>());
m_LocalLobby.AddPlayer(m_LocalUser); // As before, the local player will need to be plugged into UI before the lobby join actually happens.
m_LocalLobby
.AddPlayer(m_LocalUser); // As before, the local player will need to be plugged into UI before the lobby join actually happens.
m_LocalLobby.RelayServer = null;
}

#endregion
}
}
}

90
Assets/Scripts/GameLobby/Game/LobbyUser.cs


{
None = 0,
Connecting = 1, // User has joined a lobby but has not yet connected to Relay.
Lobby = 2, // User is in a lobby and connected to Relay.
Ready = 4, // User has selected the ready button, to ready for the "game" to start.
InGame = 8, // User is part of a "game" that has started.
Menu = 16 // User is not in a lobby, in one of the main menus.
Lobby = 2, // User is in a lobby and connected to Relay.
Ready = 4, // User has selected the ready button, to ready for the "game" to start.
InGame = 8, // User is part of a "game" that has started.
Menu = 16 // User is not in a lobby, in one of the main menus.
}
/// <summary>

public class LobbyUser : Observed<LobbyUser>
{
public LobbyUser(bool isHost = false, string displayName = null, string id = null, EmoteType emote = EmoteType.None, UserStatus userStatus = UserStatus.Menu, bool isApproved = false)
public LobbyUser(bool isHost = false, string displayName = null, string id = null,
EmoteType emote = EmoteType.None, UserStatus userStatus = UserStatus.Menu, bool isApproved = false)
{
m_data = new UserData(isHost, displayName, id, emote, userStatus, isApproved);
}

public UserStatus UserStatus { get; set; }
public bool IsApproved { get; set; }
public UserData(bool isHost, string displayName, string id, EmoteType emote, UserStatus userStatus, bool isApproved)
public UserData(bool isHost, string displayName, string id, EmoteType emote, UserStatus userStatus,
bool isApproved)
{
IsHost = isHost;
DisplayName = displayName;

public void ResetState()
{
m_data = new UserData(false, m_data.DisplayName, m_data.ID, EmoteType.None, UserStatus.Menu, false); // ID and DisplayName should persist since this might be the local user.
m_data = new UserData(false, m_data.DisplayName, m_data.ID, EmoteType.None, UserStatus.Menu,
false); // ID and DisplayName should persist since this might be the local user.
}
#endregion

Emote = 4,
ID = 8,
UserStatus = 16,
IsApproved = 32
UserMembers m_lastChanged;
public UserMembers LastChanged => m_lastChanged;
public bool IsHost
{

if (m_data.IsHost != value)
{
m_data.IsHost = value;
m_lastChanged = UserMembers.IsHost;
OnChanged(this);
}
if (m_data.IsHost == value)
return;
m_data.IsHost = value;
OnChanged(this);
}
}

set
{
if (m_data.DisplayName != value)
{
m_data.DisplayName = value;
m_lastChanged = UserMembers.DisplayName;
OnChanged(this);
}
if (m_data.DisplayName == value)
return;
m_data.DisplayName = value;
OnChanged(this);
}
}

set
{
if (m_data.Emote != value)
{
m_data.Emote = value;
m_lastChanged = UserMembers.Emote;
OnChanged(this);
}
if (m_data.Emote == value)
return;
m_data.Emote = value;
OnChanged(this);
}
}

set
{
if (m_data.ID != value)
{
m_data.ID = value;
m_lastChanged = UserMembers.ID;
OnChanged(this);
}
if (m_data.ID == value)
return;
m_data.ID = value;
OnChanged(this);
if (m_userStatus != value)
{
m_userStatus = value;
m_lastChanged = UserMembers.UserStatus;
OnChanged(this);
}
if (m_userStatus == value)
return;
m_userStatus = value;
OnChanged(this);
UserData data = observed.m_data;
int lastChanged = // Set flags just for the members that will be changed.
(m_data.IsHost == data.IsHost ? 0 : (int)UserMembers.IsHost) |
(m_data.DisplayName == data.DisplayName ? 0 : (int)UserMembers.DisplayName) |
(m_data.ID == data.ID ? 0 : (int)UserMembers.ID) |
(m_data.Emote == data.Emote ? 0 : (int)UserMembers.Emote) |
(m_data.UserStatus == data.UserStatus ? 0 : (int)UserMembers.UserStatus);
if (lastChanged == 0) // Ensure something actually changed.
return;
m_data = data;
m_lastChanged = (UserMembers)lastChanged;
m_data = observed.m_data;
;
}
}

50
Assets/Scripts/GameLobby/Game/LocalLobby.cs


//Should be set to true when pushing lobby to the cloud, and set to false when done pulling.
//This is because we could get more than just our changes when we receive the latest lobby from our calls.
public bool changedByLobbySynch;
public Action<LocalLobby> onLobbyChanged { get; private set; }
#region LocalLobbyData
public struct LobbyData

public bool Locked { get; set; }
public int AvailableSlots { get; set; }
public int MaxPlayerCount { get; set; }
public LobbyState State { get; set; }
public LobbyColor Color { get; set; }
public LobbyState LobbyState { get; set; }
public LobbyColor LobbyColor { get; set; }
public long LastEdit { get; set; }
public LobbyData(LobbyData existing)

LobbyName = existing.LobbyName;
Private = existing.Private;
MaxPlayerCount = existing.MaxPlayerCount;
State = existing.State;
Color = existing.Color;
LobbyState = existing.LobbyState;
LobbyColor = existing.LobbyColor;
LastEdit = existing.LastEdit;
AvailableSlots = existing.AvailableSlots;

LobbyName = null;
Private = false;
MaxPlayerCount = -1;
State = LobbyState.Lobby;
Color = LobbyColor.None;
LobbyState = LobbyState.Lobby;
LobbyColor = LobbyColor.None;
LastEdit = 0;
AvailableSlots = 4;

sb.Append("AvailableSlots: ");
sb.AppendLine(AvailableSlots.ToString());
sb.Append("LobbyState: ");
sb.AppendLine(State.ToString());
sb.Append("Lobby State Last Edit: ");
sb.AppendLine(LobbyState.ToString());
sb.Append("Lobby LobbyState Last Edit: ");
sb.AppendLine(Color.ToString());
sb.AppendLine(LobbyColor.ToString());
sb.Append("RelayCode: ");
sb.AppendLine(RelayCode);
sb.Append("RelayNGO: ");

}
}
public LobbyState State
public LobbyState LobbyState
get => m_Data.State;
get => m_Data.LobbyState;
m_Data.State = value;
m_Data.LobbyState = value;
OnChanged(this);
}
}

}
}
public LobbyColor Color
public LobbyColor LobbyColor
get => m_Data.Color;
get => m_Data.LobbyColor;
if (m_Data.Color != value)
if (m_Data.LobbyColor != value)
m_Data.Color = value;
m_Data.LobbyColor = value;
}
public void OnChanged()
{
onLobbyChanged?.Invoke(this);
}
public void CopyObserved(LobbyData lobbyData, Dictionary<string, LobbyUser> lobbyUsers)

var pendingState = lobbyData.State;
var pendingColor = lobbyData.Color;
var pendingNgoCode = lobbyData.RelayNGOCode;
pendingState = m_Data.State;
pendingColor = m_Data.Color;
pendingNgoCode = m_Data.RelayNGOCode;
m_Data.State = pendingState;
m_Data.Color = pendingColor;
m_Data.RelayNGOCode = pendingNgoCode;
m_Data.LobbyState = lobbyData.LobbyState;;
m_Data.LobbyColor = lobbyData.LobbyColor;
m_Data.RelayNGOCode = lobbyData.RelayNGOCode;
if (lobbyUsers == null)
m_LobbyUsers = new Dictionary<string, LobbyUser>();

41
Assets/Scripts/GameLobby/Lobby/LobbyConverters.cs


/// </summary>
public static class LobbyConverters
{
const string key_RelayCode = nameof(LocalLobby.LobbyData.RelayCode);
const string key_RelayNGOCode = nameof(LocalLobby.LobbyData.RelayNGOCode);
const string key_LobbyState = nameof(LocalLobby.LobbyData.LobbyState);
const string key_LobbyColor = nameof(LocalLobby.LobbyData.LobbyColor);
const string key_LastEdit = nameof(LocalLobby.LobbyData.LastEdit);
const string key_Displayname = nameof(LobbyUser.DisplayName);
const string key_Userstatus = nameof(LobbyUser.UserStatus);
const string key_Emote = nameof(LobbyUser.Emote);
data.Add("RelayCode", lobby.RelayCode);
data.Add("RelayNGOCode", lobby.RelayNGOCode);
data.Add("State", ((int)lobby.State).ToString()); // Using an int is smaller than using the enum state's name.
data.Add("Color", ((int)lobby.Color).ToString());
data.Add("LastEdit", lobby.Data.LastEdit.ToString());
data.Add(key_RelayCode, lobby.RelayCode);
data.Add(key_RelayNGOCode, lobby.RelayNGOCode);
data.Add(key_LobbyState, ((int)lobby.LobbyState).ToString()); // Using an int is smaller than using the enum state's name.
data.Add(key_LobbyColor, ((int)lobby.LobbyColor).ToString());
data.Add(key_LastEdit, lobby.Data.LastEdit.ToString());
return data;
}

Dictionary<string, string> data = new Dictionary<string, string>();
if (user == null || string.IsNullOrEmpty(user.ID))
return data;
data.Add("DisplayName", user.DisplayName);
data.Add("UserStatus", ((int)user.UserStatus).ToString());
data.Add("Emote", ((int)user.Emote).ToString());
data.Add(key_Displayname, user.DisplayName);
data.Add(key_Userstatus, ((int)user.UserStatus).ToString());
data.Add(key_Emote , ((int)user.Emote).ToString());
return data;
}

LobbyName = remoteLobby.Name,
MaxPlayerCount = remoteLobby.MaxPlayers,
LastEdit = remoteLobby.LastUpdated.ToFileTimeUtc(),
RelayCode = remoteLobby.Data?.ContainsKey("RelayCode") == true ? remoteLobby.Data["RelayCode"].Value : localLobbyToUpdate.RelayCode, // By providing RelayCode through the lobby data with Member visibility, we ensure a client is connected to the lobby before they could attempt a relay connection, preventing timing issues between them.
RelayNGOCode = remoteLobby.Data?.ContainsKey("RelayNGOCode") == true ? remoteLobby.Data["RelayNGOCode"].Value : localLobbyToUpdate.RelayNGOCode,
State = remoteLobby.Data?.ContainsKey("State") == true ? (LobbyState)int.Parse(remoteLobby.Data["State"].Value) : LobbyState.Lobby,
Color = remoteLobby.Data?.ContainsKey("Color") == true ? (LobbyColor)int.Parse(remoteLobby.Data["Color"].Value) : LobbyColor.None,
RelayCode = remoteLobby.Data?.ContainsKey(key_RelayCode) == true ? remoteLobby.Data[key_RelayCode].Value : localLobbyToUpdate.RelayCode, // By providing RelayCode through the lobby data with Member visibility, we ensure a client is connected to the lobby before they could attempt a relay connection, preventing timing issues between them.
RelayNGOCode = remoteLobby.Data?.ContainsKey(key_RelayNGOCode) == true ? remoteLobby.Data[key_RelayNGOCode].Value : localLobbyToUpdate.RelayNGOCode,
LobbyState = remoteLobby.Data?.ContainsKey(key_LobbyState) == true ? (LobbyState)int.Parse(remoteLobby.Data[key_LobbyState].Value) : LobbyState.Lobby,
LobbyColor = remoteLobby.Data?.ContainsKey(key_LobbyColor) == true ? (LobbyColor)int.Parse(remoteLobby.Data[key_LobbyColor].Value) : LobbyColor.None,
};
Dictionary<string, LobbyUser> lobbyUsers = new Dictionary<string, LobbyUser>();

LobbyUser incomingData = new LobbyUser
{
IsHost = remoteLobby.HostId.Equals(player.Id),
DisplayName = player.Data?.ContainsKey("DisplayName") == true ? player.Data["DisplayName"].Value : default,
Emote = player.Data?.ContainsKey("Emote") == true ? (EmoteType)int.Parse(player.Data["Emote"].Value) : default,
UserStatus = player.Data?.ContainsKey("UserStatus") == true ? (UserStatus)int.Parse(player.Data["UserStatus"].Value) : UserStatus.Connecting,
DisplayName = player.Data?.ContainsKey(key_Displayname) == true ? player.Data[key_Displayname].Value : default,
Emote = player.Data?.ContainsKey(key_Emote) == true ? (EmoteType)int.Parse(player.Data[key_Emote].Value) : default,
UserStatus = player.Data?.ContainsKey(key_Userstatus) == true ? (UserStatus)int.Parse(player.Data[key_Userstatus].Value) : UserStatus.Connecting,
ID = player.Id,
};
lobbyUsers.Add(incomingData.ID, incomingData);

6
Assets/Scripts/GameLobby/Lobby/LobbyManager.cs


foreach (var dataNew in data)
{
// Special case: We want to be able to filter on our color data, so we need to supply an arbitrary index to retrieve later. Uses N# for numerics, instead of S# for strings.
DataObject.IndexOptions index = dataNew.Key == "Color" ? DataObject.IndexOptions.N1 : 0;
DataObject.IndexOptions index = dataNew.Key == "LobbyColor" ? DataObject.IndexOptions.N1 : 0;
DataObject
dataObj = new DataObject(DataObject.VisibilityOptions.Public, dataNew.Value,
index); // Public so that when we request the list of lobbies, we can get info about them for filtering.

dataCurr.Add(dataNew.Key, dataObj);
//Special Use: Get the state of the Local lobby so we can lock it from appearing in queries if it's not in the "Lobby" State
if (dataNew.Key == "State")
//Special Use: Get the state of the Local lobby so we can lock it from appearing in queries if it's not in the "Lobby" LobbyState
if (dataNew.Key == "LobbyState")
{
Enum.TryParse(dataNew.Value, out LobbyState lobbyState);
shouldLock = lobbyState != LobbyState.Lobby;

10
Assets/Scripts/GameLobby/Lobby/LobbySynchronizer.cs


// if (type == MessageType.ClientUserSeekingDisapproval)
// {
// bool shouldDisapprove =
// m_LocalLobby.State !=
// m_LocalLobby.LobbyState !=
// LobbyState.Lobby; // By not refreshing, it's possible to have a lobby in the lobby list UI after its countdown starts and then try joining.
// if (shouldDisapprove)
// (msg as Action<relay.Approval>)?.Invoke(relay.Approval.GameAlreadyStarted);

{
if (m_LocalChanges)
{
m_LocalLobby.changedByLobbySynch = true;
{
}
{
m_LocalLobby.changedByLobbySynch = true;
m_LocalLobby.changedByLobbySynch = false;
m_LocalLobby.changedByLobbySynch = false;
}
if (!LobbyHasHost())
{

8
Assets/Scripts/GameLobby/Relay/RelayUtpClient.cs


/// </summary>
private void DoUserUpdate(NetworkDriver driver, NetworkConnection connection, LobbyUser user)
{
// Only update with actual changes. (If multiple change at once, we send messages for each separately, but that shouldn't happen often.)
if (0 < (user.LastChanged & LobbyUser.UserMembers.DisplayName))
WriteString(driver, connection, user.ID, MsgType.PlayerName, user.DisplayName);
if (0 < (user.LastChanged & LobbyUser.UserMembers.Emote))
WriteByte(driver, connection, user.ID, MsgType.Emote, (byte)user.Emote);
if (0 < (user.LastChanged & LobbyUser.UserMembers.UserStatus))
WriteByte(driver, connection, user.ID, MsgType.ReadyState, (byte)user.UserStatus);
}
/// <summary>
/// Sometimes (e.g. when a new player joins), we need to send out the full current state of this player.

4
Assets/Scripts/GameLobby/Relay/RelayUtpHost.cs


break;
}
}
if (haveAllReadied && m_localLobby.State == LobbyState.Lobby) // Need to notify both this client and all others that all players have readied.
if (haveAllReadied && m_localLobby.LobbyState == LobbyState.Lobby) // Need to notify both this client and all others that all players have readied.
else if (!haveAllReadied && m_localLobby.State == LobbyState.CountDown) // Someone cancelled during the countdown, so abort the countdown.
else if (!haveAllReadied && m_localLobby.LobbyState == LobbyState.CountDown) // Someone cancelled during the countdown, so abort the countdown.
{
Locator.Get.Messenger.OnReceiveMessage(MessageType.CancelCountdown, null);
foreach (NetworkConnection connection in m_connections)

2
Assets/Scripts/GameLobby/UI/JoinMenuUI.cs


bool CanDisplay(LocalLobby lobby)
{
return lobby.Data.State == LobbyState.Lobby && !lobby.Private;
return lobby.Data.LobbyState == LobbyState.Lobby && !lobby.Private;
}
/// <summary>

4
Assets/Scripts/GameLobby/UI/RecolorForLobbyType.cs


public void UpdateLobby(LocalLobby lobby)
{
m_lobby = lobby;
Color color = s_colorsOrdered[(int)lobby.Color];
Color color = s_colorsOrdered[(int)lobby.LobbyColor];
foreach (Graphic graphic in m_toRecolor)
graphic.color = new Color(color.r, color.g, color.b, graphic.color.a);
}

public void ChangeColor(int color)
{
if (m_lobby != null)
m_lobby.Color = (LobbyColor)color;
m_lobby.LobbyColor = (LobbyColor)color;
}
}
}

44
Assets/Scripts/GameLobby/UI/ShowWhenLobbyStateUI.cs


using UnityEngine;
namespace LobbyRelaySample.UI
{
/// <summary>
/// UI element that is displayed when the lobby is in a particular state (e.g. counting down, in-game).
/// </summary>
public class ShowWhenLobbyStateUI : ObserverPanel<LocalLobby>
{
[SerializeField]
LobbyState m_ShowThisWhen;
public override void ObservedUpdated(LocalLobby observed)
{
if (m_ShowThisWhen.HasFlag(observed.State))
Show();
else
Hide();
}
}
}
using UnityEngine;
namespace LobbyRelaySample.UI
{
/// <summary>
/// UI element that is displayed when the lobby is in a particular state (e.g. counting down, in-game).
/// </summary>
public class ShowWhenLobbyStateUI : ObserverPanel<LocalLobby>
{
[SerializeField]
LobbyState m_ShowThisWhen;
public override void ObservedUpdated(LocalLobby observed)
{
if (m_ShowThisWhen.HasFlag(observed.LobbyState))
Show();
else
Hide();
}
}
}
正在加载...
取消
保存