lobby tweaking

This commit is contained in:
2025-10-05 19:01:48 +02:00
parent 6ebb88cb26
commit 586d364772
2 changed files with 200 additions and 5 deletions

View File

@@ -461,6 +461,7 @@ namespace MegaKoop.Steam
public void OpenFriendsOverlay() { Debug.Log("[SteamLobbyService] OpenFriendsOverlay stub"); } public void OpenFriendsOverlay() { Debug.Log("[SteamLobbyService] OpenFriendsOverlay stub"); }
public IReadOnlyList<(string steamId, string name, bool online)> GetFriends() => new List<(string, string, bool)>(); public IReadOnlyList<(string steamId, string name, bool online)> GetFriends() => new List<(string, string, bool)>();
public void InviteFriendBySteamId(string steamId) { Debug.Log("[SteamLobbyService] InviteFriendBySteamId stub"); } public void InviteFriendBySteamId(string steamId) { Debug.Log("[SteamLobbyService] InviteFriendBySteamId stub"); }
public bool IsKickedLocal() => false;
public IReadOnlyList<(string steamId, string name, bool isReady, bool isHost)> GetMembers() => new List<(string,string,bool,bool)>(); public IReadOnlyList<(string steamId, string name, bool isReady, bool isHost)> GetMembers() => new List<(string,string,bool,bool)>();
public void JoinLobbyByCode(string code) { _isInLobby = true; IsHost = false; LobbyCode = code; OnLobbyEntered?.Invoke(); } public void JoinLobbyByCode(string code) { _isInLobby = true; IsHost = false; LobbyCode = code; OnLobbyEntered?.Invoke(); }
} }

View File

@@ -64,9 +64,17 @@ namespace MegaKoop.UI
private Button btnLeaveLobby; private Button btnLeaveLobby;
private Button btnBackFromLobby; private Button btnBackFromLobby;
[Header("Player Ready Styling")]
[SerializeField] private Color readyBorderColor = new Color(0.2f, 0.82f, 0.35f, 1f);
[SerializeField] private Color notReadyBorderColor = new Color(0.85f, 0.25f, 0.25f, 1f);
[SerializeField] private float avatarBorderThickness = 12f;
// Selection // Selection
private string selectedPlayerSteamId = string.Empty; private string selectedPlayerSteamId = string.Empty;
// Cached readiness state per member
private readonly Dictionary<string, bool> memberReadyCache = new Dictionary<string, bool>();
// Steam service // Steam service
private SteamLobbyService steam; private SteamLobbyService steam;
@@ -364,6 +372,7 @@ namespace MegaKoop.UI
private void OnLobbyCreated() private void OnLobbyCreated()
{ {
selectedPlayerSteamId = string.Empty; selectedPlayerSteamId = string.Empty;
memberReadyCache.Clear();
UpdateUIFromSteam(); UpdateUIFromSteam();
// Auto-open invite overlay for the host // Auto-open invite overlay for the host
if (steam != null && steam.IsInLobby && steam.IsHost) if (steam != null && steam.IsInLobby && steam.IsHost)
@@ -376,6 +385,7 @@ namespace MegaKoop.UI
private void OnLobbyEntered() private void OnLobbyEntered()
{ {
selectedPlayerSteamId = string.Empty; selectedPlayerSteamId = string.Empty;
memberReadyCache.Clear();
UpdateUIFromSteam(); UpdateUIFromSteam();
// Auto-open invite overlay if we are the host entering our lobby // Auto-open invite overlay if we are the host entering our lobby
if (steam != null && steam.IsInLobby && steam.IsHost) if (steam != null && steam.IsInLobby && steam.IsHost)
@@ -388,6 +398,7 @@ namespace MegaKoop.UI
private void OnLobbyLeft() private void OnLobbyLeft()
{ {
selectedPlayerSteamId = string.Empty; selectedPlayerSteamId = string.Empty;
memberReadyCache.Clear();
UpdateUIFromSteam(); UpdateUIFromSteam();
} }
@@ -427,9 +438,24 @@ namespace MegaKoop.UI
if (av) if (av)
{ {
var img = av.GetComponent<Image>(); var img = av.GetComponent<Image>();
if (img && steam.TryGetAvatarSprite(steamId, out var spr2, true)) if (img)
{ {
img.sprite = spr2; img.color = Color.white; img.preserveAspect = true; if (steam.TryGetAvatarSprite(steamId, out var spr2, true))
{
img.sprite = spr2;
img.color = Color.white;
img.preserveAspect = true;
}
else
{
img.sprite = null;
img.color = new Color(0.3f, 0.6f, 0.3f, 1f);
}
if (TryGetMemberReadyState(steamId, out var isReady))
{
ApplyReadyBorder(img, isReady);
}
} }
} }
} }
@@ -572,12 +598,19 @@ namespace MegaKoop.UI
{ {
if (IsHost || !IsInLobby || steam == null) return; if (IsHost || !IsInLobby || steam == null) return;
var members = GetSteamMembers(); var members = GetSteamMembers();
CacheMemberReadyStates(members);
var localId = steam.LocalSteamIdString; var localId = steam.LocalSteamIdString;
bool current = false; bool current = false;
var me = members.FirstOrDefault(m => m.steamId == localId); var me = members.FirstOrDefault(m => m.steamId == localId);
// When tuple is default, steamId will be null; guard // When tuple is default, steamId will be null; guard
if (me.steamId == localId) current = me.isReady; if (me.steamId == localId) current = me.isReady;
steam.SetReady(!current); bool newReadyState = !current;
steam.SetReady(newReadyState);
if (!string.IsNullOrEmpty(localId))
{
memberReadyCache[localId] = newReadyState;
UpdateAvatarUIForSteamId(localId);
}
UpdateUIFromSteam(); UpdateUIFromSteam();
} }
@@ -664,7 +697,15 @@ namespace MegaKoop.UI
private void UpdateVisibility() private void UpdateVisibility()
{ {
if (btnStartGame) btnStartGame.gameObject.SetActive(IsInLobby && IsHost); if (btnStartGame)
{
bool canHostStart = IsInLobby && IsHost;
btnStartGame.gameObject.SetActive(canHostStart);
if (btnStartGame.gameObject.activeSelf)
{
btnStartGame.interactable = AreAllPlayersReady();
}
}
if (btnLeaveLobby) btnLeaveLobby.gameObject.SetActive(IsInLobby); if (btnLeaveLobby) btnLeaveLobby.gameObject.SetActive(IsInLobby);
if (btnToggleReady) btnToggleReady.gameObject.SetActive(IsInLobby && !IsHost); if (btnToggleReady) btnToggleReady.gameObject.SetActive(IsInLobby && !IsHost);
if (btnInviteFriends) btnInviteFriends.gameObject.SetActive(IsInLobby && IsHost); if (btnInviteFriends) btnInviteFriends.gameObject.SetActive(IsInLobby && IsHost);
@@ -681,6 +722,137 @@ namespace MegaKoop.UI
return steam != null ? steam.GetMembers().ToList() : new List<(string, string, bool, bool)>(); return steam != null ? steam.GetMembers().ToList() : new List<(string, string, bool, bool)>();
} }
private void CacheMemberReadyStates(IEnumerable<(string steamId, string name, bool isReady, bool isHost)> members)
{
memberReadyCache.Clear();
if (members == null) return;
foreach (var member in members)
{
if (string.IsNullOrEmpty(member.steamId)) continue;
memberReadyCache[member.steamId] = member.isReady;
}
}
private bool TryGetMemberReadyState(string steamId, out bool isReady)
{
isReady = false;
if (string.IsNullOrEmpty(steamId)) return false;
if (memberReadyCache.TryGetValue(steamId, out var cachedReady))
{
isReady = cachedReady;
return true;
}
var members = GetSteamMembers();
CacheMemberReadyStates(members);
foreach (var member in members)
{
if (member.steamId == steamId)
{
isReady = member.isReady;
return true;
}
}
return false;
}
private bool AreAllPlayersReady()
{
if (!IsInLobby) return false;
if (memberReadyCache.Count == 0)
{
CacheMemberReadyStates(GetSteamMembers());
}
if (memberReadyCache.Count == 0) return false;
foreach (var kvp in memberReadyCache)
{
if (!kvp.Value) return false;
}
return true;
}
private void ApplyReadyBorder(Image avatarImage, bool isReady)
{
if (avatarImage == null) return;
var legacyOutline = avatarImage.GetComponent<Outline>();
if (legacyOutline != null) legacyOutline.enabled = false;
var borderImage = EnsureAvatarBorderImage(avatarImage);
if (borderImage == null) return;
if (avatarBorderThickness <= 0f)
{
borderImage.enabled = false;
return;
}
borderImage.enabled = true;
borderImage.color = isReady ? readyBorderColor : notReadyBorderColor;
var avatarRect = avatarImage.rectTransform;
var borderRect = borderImage.rectTransform;
borderRect.anchorMin = avatarRect.anchorMin;
borderRect.anchorMax = avatarRect.anchorMax;
borderRect.pivot = avatarRect.pivot;
borderRect.anchoredPosition = avatarRect.anchoredPosition;
borderRect.localPosition = avatarRect.localPosition;
borderRect.localRotation = avatarRect.localRotation;
borderRect.localScale = avatarRect.localScale;
var padding = avatarBorderThickness * 2f;
borderRect.sizeDelta = avatarRect.sizeDelta + new Vector2(padding, padding);
}
private Image EnsureAvatarBorderImage(Image avatarImage)
{
if (avatarImage == null) return null;
var parent = avatarImage.transform.parent;
if (parent == null) return null;
const string borderName = "Image_AvatarBorder";
Transform borderTransform = null;
for (int i = 0; i < parent.childCount; i++)
{
var child = parent.GetChild(i);
if (child != null && child.name == borderName)
{
borderTransform = child;
break;
}
}
if (borderTransform == null)
{
var borderGO = new GameObject(borderName, typeof(RectTransform), typeof(Image));
borderTransform = borderGO.transform;
borderTransform.SetParent(parent, false);
}
var borderImage = borderTransform.GetComponent<Image>();
if (borderImage == null)
{
borderImage = borderTransform.gameObject.AddComponent<Image>();
}
borderImage.raycastTarget = false;
borderImage.preserveAspect = false;
borderImage.type = Image.Type.Simple;
var layoutElement = borderTransform.GetComponent<LayoutElement>();
if (layoutElement == null)
{
layoutElement = borderTransform.gameObject.AddComponent<LayoutElement>();
}
layoutElement.ignoreLayout = true;
var avatarIndex = avatarImage.transform.GetSiblingIndex();
borderTransform.SetSiblingIndex(avatarIndex);
avatarImage.transform.SetSiblingIndex(borderTransform.GetSiblingIndex() + 1);
return borderImage;
}
private void RebuildPlayers() private void RebuildPlayers()
{ {
if (contentPlayers == null) return; if (contentPlayers == null) return;
@@ -694,6 +866,7 @@ namespace MegaKoop.UI
foreach (var go in toDestroy) DestroyImmediate(go); foreach (var go in toDestroy) DestroyImmediate(go);
var members = GetSteamMembers(); var members = GetSteamMembers();
CacheMemberReadyStates(members);
if (textPlayerCount) textPlayerCount.text = $"{members.Count}/{(ddMaxPlayers ? int.Parse(ddMaxPlayers.options[ddMaxPlayers.value].text) : 4)}"; if (textPlayerCount) textPlayerCount.text = $"{members.Count}/{(ddMaxPlayers ? int.Parse(ddMaxPlayers.options[ddMaxPlayers.value].text) : 4)}";
if (members.Count == 0) if (members.Count == 0)
@@ -735,6 +908,14 @@ namespace MegaKoop.UI
var goLE = go.GetComponent<LayoutElement>(); if (!goLE) goLE = go.AddComponent<LayoutElement>(); var goLE = go.GetComponent<LayoutElement>(); if (!goLE) goLE = go.AddComponent<LayoutElement>();
goLE.minHeight = 100; goLE.flexibleWidth = 1f; goLE.minHeight = 100; goLE.flexibleWidth = 1f;
var horizontalLayout = go.GetComponent<HorizontalLayoutGroup>();
if (horizontalLayout)
{
var paddingValue = Mathf.CeilToInt(avatarBorderThickness);
if (horizontalLayout.padding.left < paddingValue) horizontalLayout.padding.left = paddingValue;
if (horizontalLayout.padding.right < paddingValue) horizontalLayout.padding.right = paddingValue;
}
// Children // Children
// Find avatar inside this item (not globally) // Find avatar inside this item (not globally)
Image avatarGO = null; Image avatarGO = null;
@@ -786,7 +967,20 @@ namespace MegaKoop.UI
avRT.anchoredPosition = Vector2.zero; avRT.anchoredPosition = Vector2.zero;
avRT.sizeDelta = new Vector2(96, 96); avRT.sizeDelta = new Vector2(96, 96);
var avLE = avatarGO.GetComponent<LayoutElement>() ?? avatarGO.gameObject.AddComponent<LayoutElement>(); var avLE = avatarGO.GetComponent<LayoutElement>() ?? avatarGO.gameObject.AddComponent<LayoutElement>();
avLE.minWidth = 96; avLE.minHeight = 96; var avatarSize = Mathf.Max(avRT.sizeDelta.x, avRT.sizeDelta.y);
var paddedSize = avatarSize + avatarBorderThickness * 2f;
avLE.minWidth = paddedSize;
avLE.minHeight = paddedSize;
avLE.preferredWidth = paddedSize;
avLE.preferredHeight = paddedSize;
if (goLE != null)
{
goLE.minWidth = Mathf.Max(goLE.minWidth, paddedSize);
goLE.preferredWidth = Mathf.Max(goLE.preferredWidth, paddedSize);
}
ApplyReadyBorder(avatarGO, ready);
} }
// Selection handler // Selection handler