Adding new Races, Main Menu -> Lobby flow, and what's needed to set up multiplayer testing.
This commit is contained in:
parent
60fa58b07f
commit
fdada6f132
29 changed files with 2581 additions and 176 deletions
|
|
@ -1,5 +1,6 @@
|
|||
// Assets/_Project/Scripts/UI/LobbyController.cs
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
|
@ -45,6 +46,22 @@ namespace TD.UI
|
|||
private Button leaveButton;
|
||||
private Label statusLabel;
|
||||
|
||||
// ----- Race selection overlay ------------------------------------
|
||||
|
||||
[Tooltip("Sibling RaceSelectionOverlay component that owns the race-pick UI. " +
|
||||
"Auto-located on the same GameObject if not assigned.")]
|
||||
[SerializeField] private RaceSelectionOverlay raceOverlay;
|
||||
|
||||
// Snapshot of player-list state from the last rebuild. RefreshPlayerList
|
||||
// skips rebuilding when this matches the current frame's signature —
|
||||
// critical because rebuilding every frame destroys the per-row buttons
|
||||
// mid-click, and UI Toolkit's Clickable manipulator needs the same
|
||||
// element instance to receive PointerDown AND PointerUp for the action
|
||||
// to fire. Pre-fix, clicks on Select Race / Ready / Unready were silently
|
||||
// lost because the button was destroyed between press and release.
|
||||
private string lastPlayerListSignature = string.Empty;
|
||||
private readonly StringBuilder signatureBuffer = new StringBuilder();
|
||||
|
||||
// ----- Lifecycle --------------------------------------------------
|
||||
|
||||
private void Start()
|
||||
|
|
@ -59,6 +76,13 @@ namespace TD.UI
|
|||
}
|
||||
|
||||
BuildUI(root);
|
||||
|
||||
// Initialize the race overlay with our root so it can install its
|
||||
// UI elements on top of the lobby. Hidden by default.
|
||||
if (raceOverlay == null) raceOverlay = GetComponent<RaceSelectionOverlay>();
|
||||
if (raceOverlay != null) raceOverlay.Initialize(root);
|
||||
else Debug.LogWarning("[LobbyController] No RaceSelectionOverlay component found — " +
|
||||
"race-picker button will be disabled. Add one to this GameObject.");
|
||||
}
|
||||
|
||||
private void Update()
|
||||
|
|
@ -131,16 +155,23 @@ namespace TD.UI
|
|||
|
||||
private void RefreshPlayerList()
|
||||
{
|
||||
// Sort by slot for stable ordering. AllPlayers is keyed by clientId
|
||||
// which may not be slot-ordered.
|
||||
var players = PlayerMatchState.AllPlayers.OrderBy(p => (int)p.Slot).ToList();
|
||||
|
||||
// Skip rebuild when the player-relevant state hasn't changed.
|
||||
// Without this guard the per-row buttons are destroyed every frame,
|
||||
// which loses clicks (see lastPlayerListSignature comment above).
|
||||
string signature = ComputePlayerListSignature(players);
|
||||
if (signature == lastPlayerListSignature) return;
|
||||
lastPlayerListSignature = signature;
|
||||
|
||||
playerListContainer.Clear();
|
||||
|
||||
ulong localId = NetworkManager.Singleton != null
|
||||
? NetworkManager.Singleton.LocalClientId
|
||||
: ulong.MaxValue;
|
||||
|
||||
// Sort by slot for stable ordering. AllPlayers is keyed by clientId
|
||||
// which may not be slot-ordered.
|
||||
var players = PlayerMatchState.AllPlayers.OrderBy(p => (int)p.Slot).ToList();
|
||||
|
||||
foreach (var pms in players)
|
||||
{
|
||||
bool isLocal = pms.OwnerClientId == localId;
|
||||
|
|
@ -151,6 +182,26 @@ namespace TD.UI
|
|||
playerListContainer.Add(new Label("(no players connected)") { style = { color = Color.gray } });
|
||||
}
|
||||
|
||||
// Compact signature of every field the player rows depend on. When this
|
||||
// changes we rebuild; when it's identical we leave the existing rows
|
||||
// (and their button event handlers) intact for click handling.
|
||||
private string ComputePlayerListSignature(System.Collections.Generic.List<PlayerMatchState> players)
|
||||
{
|
||||
signatureBuffer.Clear();
|
||||
foreach (var pms in players)
|
||||
{
|
||||
signatureBuffer.Append(pms.OwnerClientId);
|
||||
signatureBuffer.Append(':');
|
||||
signatureBuffer.Append((int)pms.Slot);
|
||||
signatureBuffer.Append(':');
|
||||
signatureBuffer.Append((int)pms.RaceSelection);
|
||||
signatureBuffer.Append(':');
|
||||
signatureBuffer.Append(pms.IsReady ? '1' : '0');
|
||||
signatureBuffer.Append(';');
|
||||
}
|
||||
return signatureBuffer.ToString();
|
||||
}
|
||||
|
||||
private VisualElement BuildPlayerRow(PlayerMatchState pms, bool isLocal)
|
||||
{
|
||||
var row = new VisualElement();
|
||||
|
|
@ -200,23 +251,17 @@ namespace TD.UI
|
|||
// Local-only controls.
|
||||
if (isLocal)
|
||||
{
|
||||
// PLACEHOLDER race picker — the RaceId enum only has None right
|
||||
// now (Phase 1.8 will fill it). For now the single button submits
|
||||
// RaceId.None, which keeps the ready-up gate effectively a no-op
|
||||
// (the server requires RaceSelection != None to allow ready). To
|
||||
// exercise the flow end-to-end before races exist, comment out
|
||||
// the gate in PlayerMatchState.SubmitReadyRpc.
|
||||
//
|
||||
// TODO (Phase 1.8): replace with a dropdown of RaceDefinition
|
||||
// assets discovered at runtime, each option calling
|
||||
// pms.SubmitRaceRpc(definition.Id).
|
||||
var pickRaceBtn = new Button(() => pms.SubmitRaceRpc(RaceId.None))
|
||||
// Race selection — opens the overlay that lets the player browse
|
||||
// the 4x4 race grid and pick one. The overlay handles submission
|
||||
// (PlayerMatchState.SubmitRaceRpc) directly, so we just open it.
|
||||
var pickRaceBtn = new Button(OpenRaceOverlay)
|
||||
{
|
||||
text = "Pick Race (stub)"
|
||||
text = "Select Race"
|
||||
};
|
||||
pickRaceBtn.style.minWidth = 130;
|
||||
pickRaceBtn.style.height = 28;
|
||||
pickRaceBtn.style.marginLeft = 12;
|
||||
pickRaceBtn.SetEnabled(raceOverlay != null);
|
||||
row.Add(pickRaceBtn);
|
||||
|
||||
var readyBtn = new Button(() => pms.SubmitReadyRpc(!pms.IsReady))
|
||||
|
|
@ -247,6 +292,16 @@ namespace TD.UI
|
|||
|
||||
// ----- Button handlers --------------------------------------------
|
||||
|
||||
private void OpenRaceOverlay()
|
||||
{
|
||||
if (raceOverlay == null)
|
||||
{
|
||||
Debug.LogWarning("[LobbyController] Race overlay not assigned.");
|
||||
return;
|
||||
}
|
||||
raceOverlay.Show();
|
||||
}
|
||||
|
||||
private void OnStartMatchClicked()
|
||||
{
|
||||
var svc = LobbyService.Instance;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue