Initial commit
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace LiveOpsExamples
|
||||
{
|
||||
public class BackpackModalPanel : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private PanelRenderer panelRenderer;
|
||||
[SerializeField] private CozyCollectorController controller;
|
||||
[SerializeField] private VisualTreeAsset backpackItemRowTemplate;
|
||||
[SerializeField] private CozyCollectorSpriteLibrary spriteLibrary;
|
||||
|
||||
private VisualElement _root;
|
||||
private ScrollView _itemsScroll;
|
||||
private VisualElement _emptyNote;
|
||||
private Button _closeButton;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
panelRenderer.RegisterUIReloadCallback(OnUIReload);
|
||||
controller.ModalChanged += ApplyVisibility;
|
||||
controller.BackpackChanged += Rebuild;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
panelRenderer.UnregisterUIReloadCallback(OnUIReload);
|
||||
controller.ModalChanged -= ApplyVisibility;
|
||||
controller.BackpackChanged -= Rebuild;
|
||||
}
|
||||
|
||||
private void OnUIReload(PanelRenderer renderer, VisualElement root)
|
||||
{
|
||||
_root = root.Q<VisualElement>("backpack-modal");
|
||||
_itemsScroll = _root.Q<ScrollView>("backpack-items-scroll");
|
||||
_emptyNote = _root.Q<VisualElement>("backpack-empty");
|
||||
_closeButton = _root.Q<Button>("backpack-close-button");
|
||||
|
||||
_closeButton.clicked -= HandleCloseClicked;
|
||||
_closeButton.clicked += HandleCloseClicked;
|
||||
|
||||
ApplyVisibility();
|
||||
Rebuild();
|
||||
}
|
||||
|
||||
private void ApplyVisibility()
|
||||
{
|
||||
_root.style.display = controller.ActiveModal == CozyModal.Backpack ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
}
|
||||
|
||||
private void Rebuild()
|
||||
{
|
||||
_itemsScroll.contentContainer.Clear();
|
||||
|
||||
var items = controller.BackpackItems;
|
||||
_emptyNote.style.display = items == null || items.Count == 0 ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
if (items == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var item in items)
|
||||
{
|
||||
var rowRoot = backpackItemRowTemplate.CloneTree();
|
||||
_itemsScroll.contentContainer.Add(rowRoot);
|
||||
|
||||
var row = new BackpackItemRowView(rowRoot);
|
||||
row.Set(
|
||||
spriteLibrary.ForSlug(item.Slug),
|
||||
string.IsNullOrEmpty(item.Name) ? item.Slug : item.Name,
|
||||
item.Slug,
|
||||
item.Amount);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleCloseClicked()
|
||||
{
|
||||
controller.CloseModal();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0c73aae57f5c749b88f5bf2c22538af5
|
||||
@@ -0,0 +1,70 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace LiveOpsExamples
|
||||
{
|
||||
public class BootOverlayPanel : MonoBehaviour
|
||||
{
|
||||
private const float SpinnerDegreesPerSecond = 180f;
|
||||
|
||||
[SerializeField] private PanelRenderer panelRenderer;
|
||||
[SerializeField] private CozyCollectorController controller;
|
||||
|
||||
private VisualElement _overlay;
|
||||
private Label _statusLabel;
|
||||
private VisualElement _spinner;
|
||||
private IVisualElementScheduledItem _spinSchedule;
|
||||
private float _angle;
|
||||
private float _lastTick;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
panelRenderer.RegisterUIReloadCallback(OnUIReload);
|
||||
controller.PhaseChanged += ApplyVisibility;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
panelRenderer.UnregisterUIReloadCallback(OnUIReload);
|
||||
controller.PhaseChanged -= ApplyVisibility;
|
||||
_spinSchedule?.Pause();
|
||||
}
|
||||
|
||||
private void OnUIReload(PanelRenderer renderer, VisualElement root)
|
||||
{
|
||||
_overlay = root.Q<VisualElement>("boot-overlay");
|
||||
_statusLabel = root.Q<Label>("boot-status");
|
||||
_spinner = root.Q<VisualElement>("boot-spinner");
|
||||
_spinSchedule = _spinner.schedule.Execute(Spin).Every(16);
|
||||
ApplyVisibility();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
_statusLabel.text = controller.StatusMessage;
|
||||
}
|
||||
|
||||
private void Spin()
|
||||
{
|
||||
var now = Time.unscaledTime;
|
||||
_angle = (_angle + SpinnerDegreesPerSecond * (now - _lastTick)) % 360f;
|
||||
_lastTick = now;
|
||||
_spinner.style.rotate = new Rotate(Angle.Degrees(_angle));
|
||||
}
|
||||
|
||||
private void ApplyVisibility()
|
||||
{
|
||||
var visible = controller.Phase == CozyPhase.Booting || controller.Phase == CozyPhase.Connecting;
|
||||
_overlay.style.display = visible ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
if (visible)
|
||||
{
|
||||
_lastTick = Time.unscaledTime;
|
||||
_spinSchedule.Resume();
|
||||
}
|
||||
else
|
||||
{
|
||||
_spinSchedule.Pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f54172384bff5402686bb558e2abb5b7
|
||||
@@ -0,0 +1,89 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace LiveOpsExamples
|
||||
{
|
||||
public class EventInspectorPanel : MonoBehaviour
|
||||
{
|
||||
private static readonly Dictionary<string, Color> CapabilityColors = new Dictionary<string, Color>
|
||||
{
|
||||
{ "auth", new Color(0.486f, 0.769f, 1f) },
|
||||
{ "config", new Color(0.945f, 0.776f, 0.490f) },
|
||||
{ "scenario", new Color(0.769f, 0.635f, 1f) },
|
||||
{ "store", new Color(1f, 0.698f, 0.490f) },
|
||||
{ "leaderboard", new Color(0.482f, 0.878f, 0.690f) },
|
||||
{ "storage", new Color(0.624f, 0.690f, 0.769f) },
|
||||
{ "inventory", new Color(0.898f, 0.604f, 0.796f) },
|
||||
{ "wallet", new Color(1f, 0.831f, 0.475f) },
|
||||
{ "system", new Color(0.667f, 0.706f, 0.635f) },
|
||||
};
|
||||
|
||||
private static readonly Dictionary<string, string> CapabilityLabels = new Dictionary<string, string>
|
||||
{
|
||||
{ "auth", "Auth" },
|
||||
{ "config", "Config" },
|
||||
{ "scenario", "Scenario" },
|
||||
{ "store", "Store" },
|
||||
{ "leaderboard", "Board" },
|
||||
{ "storage", "Storage" },
|
||||
{ "inventory", "Items" },
|
||||
{ "wallet", "Wallet" },
|
||||
{ "system", "System" },
|
||||
};
|
||||
|
||||
private static readonly Color FallbackColor = new Color(0.667f, 0.706f, 0.635f);
|
||||
|
||||
[SerializeField] private PanelRenderer panelRenderer;
|
||||
[SerializeField] private CozyCollectorController controller;
|
||||
[SerializeField] private VisualTreeAsset logRowTemplate;
|
||||
|
||||
private ScrollView _scroll;
|
||||
private VisualElement _emptyNote;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
panelRenderer.RegisterUIReloadCallback(OnUIReload);
|
||||
controller.LogAdded += Rebuild;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
panelRenderer.UnregisterUIReloadCallback(OnUIReload);
|
||||
controller.LogAdded -= Rebuild;
|
||||
}
|
||||
|
||||
private void OnUIReload(PanelRenderer renderer, VisualElement root)
|
||||
{
|
||||
_scroll = root.Q<ScrollView>("event-log-scroll");
|
||||
_emptyNote = root.Q("event-log-empty");
|
||||
|
||||
Rebuild();
|
||||
}
|
||||
|
||||
private void Rebuild()
|
||||
{
|
||||
var container = _scroll.contentContainer;
|
||||
container.Clear();
|
||||
|
||||
var entries = controller.LogEntries;
|
||||
_emptyNote.style.display = entries == null || entries.Count == 0 ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
if (entries == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var entry in entries)
|
||||
{
|
||||
var rowRoot = logRowTemplate.CloneTree();
|
||||
container.Add(rowRoot);
|
||||
|
||||
string key = entry.Capability == null ? string.Empty : entry.Capability.ToLowerInvariant();
|
||||
string label = CapabilityLabels.TryGetValue(key, out var known) ? known : entry.Capability;
|
||||
Color color = CapabilityColors.TryGetValue(key, out var knownColor) ? knownColor : FallbackColor;
|
||||
string detail = string.IsNullOrEmpty(entry.Detail) ? string.Empty : $" — {entry.Detail}";
|
||||
new LogRowView(rowRoot).Set(entry.Timestamp.ToString("HH:mm:ss"), label, color, entry.Message, detail);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a134e625b6e114c4b80b9f55a14e48b3
|
||||
@@ -0,0 +1,119 @@
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace LiveOpsExamples
|
||||
{
|
||||
public class HudPanel : MonoBehaviour
|
||||
{
|
||||
private const string TimerLowClass = "timer-low";
|
||||
|
||||
[SerializeField] private PanelRenderer panelRenderer;
|
||||
[SerializeField] private CozyCollectorController controller;
|
||||
[SerializeField] private GameWorld world;
|
||||
|
||||
private Label _scoreLabel;
|
||||
private Label _bestLabel;
|
||||
private Label _timeLeftLabel;
|
||||
private VisualElement _timerFill;
|
||||
private Label _walletLabel;
|
||||
private Button _shopButton;
|
||||
private Button _backpackButton;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
panelRenderer.RegisterUIReloadCallback(OnUIReload);
|
||||
world.StateChanged += Refresh;
|
||||
world.RoundFinished += HandleRoundFinished;
|
||||
controller.WalletsChanged += RefreshWallet;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
panelRenderer.UnregisterUIReloadCallback(OnUIReload);
|
||||
world.StateChanged -= Refresh;
|
||||
world.RoundFinished -= HandleRoundFinished;
|
||||
controller.WalletsChanged -= RefreshWallet;
|
||||
}
|
||||
|
||||
private void OnUIReload(PanelRenderer renderer, VisualElement root)
|
||||
{
|
||||
_scoreLabel = root.Q<Label>("hud-score");
|
||||
_bestLabel = root.Q<Label>("hud-best");
|
||||
_timeLeftLabel = root.Q<Label>("hud-time-left");
|
||||
_timerFill = root.Q("hud-timer-fill");
|
||||
_walletLabel = root.Q<Label>("hud-wallet");
|
||||
_shopButton = root.Q<Button>("hud-shop-button");
|
||||
_backpackButton = root.Q<Button>("hud-backpack-button");
|
||||
|
||||
_shopButton.clicked -= HandleShopClicked;
|
||||
_shopButton.clicked += HandleShopClicked;
|
||||
_backpackButton.clicked -= HandleBackpackClicked;
|
||||
_backpackButton.clicked += HandleBackpackClicked;
|
||||
|
||||
Refresh();
|
||||
RefreshWallet();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
RefreshTimer();
|
||||
}
|
||||
|
||||
private void Refresh()
|
||||
{
|
||||
_scoreLabel.text = world.Score.ToString();
|
||||
_bestLabel.text = controller.Save != null ? controller.Save.BestScore.ToString() : "0";
|
||||
RefreshTimer();
|
||||
}
|
||||
|
||||
private void RefreshTimer()
|
||||
{
|
||||
float roundSeconds = controller.Tuning != null ? Mathf.Max(1f, controller.Tuning.RoundSeconds) : 1f;
|
||||
float fraction = Mathf.Clamp01(world.TimeLeftSeconds / roundSeconds);
|
||||
_timeLeftLabel.text = $"{Mathf.CeilToInt(Mathf.Max(0f, world.TimeLeftSeconds))}s";
|
||||
_timerFill.style.width = Length.Percent(fraction * 100f);
|
||||
_timerFill.EnableInClassList(TimerLowClass, fraction <= 0.2f);
|
||||
}
|
||||
|
||||
private void RefreshWallet()
|
||||
{
|
||||
_walletLabel.text = WalletLabel();
|
||||
}
|
||||
|
||||
private string WalletLabel()
|
||||
{
|
||||
var wallets = controller.Wallets;
|
||||
if (wallets == null || wallets.Count == 0)
|
||||
{
|
||||
return "—";
|
||||
}
|
||||
|
||||
var builder = new StringBuilder();
|
||||
foreach (var wallet in wallets)
|
||||
{
|
||||
if (builder.Length > 0)
|
||||
{
|
||||
builder.Append(" · ");
|
||||
}
|
||||
builder.Append($"{wallet.Balance:0.##} {wallet.Currency}".TrimEnd());
|
||||
}
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private void HandleRoundFinished(int score, int collected)
|
||||
{
|
||||
Refresh();
|
||||
}
|
||||
|
||||
private void HandleShopClicked()
|
||||
{
|
||||
controller.OpenModal(CozyModal.Shop);
|
||||
}
|
||||
|
||||
private void HandleBackpackClicked()
|
||||
{
|
||||
controller.OpenModal(CozyModal.Backpack);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2e5e5cb2ad54545a6847ad8c90d1f7d8
|
||||
@@ -0,0 +1,102 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace LiveOpsExamples
|
||||
{
|
||||
public class OfferModalPanel : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private PanelRenderer panelRenderer;
|
||||
[SerializeField] private CozyCollectorController controller;
|
||||
|
||||
private VisualElement root;
|
||||
private Label titleLabel;
|
||||
private Label nameLabel;
|
||||
private Label priceLabel;
|
||||
private Button buyButton;
|
||||
private Button declineButton;
|
||||
private Button closeButton;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
panelRenderer.RegisterUIReloadCallback(OnUIReload);
|
||||
controller.ModalChanged += ApplyVisibility;
|
||||
controller.OfferChanged += Refresh;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
panelRenderer.UnregisterUIReloadCallback(OnUIReload);
|
||||
controller.ModalChanged -= ApplyVisibility;
|
||||
controller.OfferChanged -= Refresh;
|
||||
}
|
||||
|
||||
private void OnUIReload(PanelRenderer renderer, VisualElement root)
|
||||
{
|
||||
this.root = root.Q<VisualElement>("offer-modal");
|
||||
titleLabel = this.root.Q<Label>("offer-title");
|
||||
nameLabel = this.root.Q<Label>("offer-name");
|
||||
priceLabel = this.root.Q<Label>("offer-price");
|
||||
buyButton = this.root.Q<Button>("offer-buy-button");
|
||||
declineButton = this.root.Q<Button>("offer-decline-button");
|
||||
closeButton = this.root.Q<Button>("offer-close-button");
|
||||
|
||||
buyButton.clicked -= HandleBuyClicked;
|
||||
buyButton.clicked += HandleBuyClicked;
|
||||
declineButton.clicked -= HandleDeclineClicked;
|
||||
declineButton.clicked += HandleDeclineClicked;
|
||||
closeButton.clicked -= HandleCloseClicked;
|
||||
closeButton.clicked += HandleCloseClicked;
|
||||
|
||||
ApplyVisibility();
|
||||
Refresh();
|
||||
}
|
||||
|
||||
private void ApplyVisibility()
|
||||
{
|
||||
root.style.display = controller.ActiveModal == CozyModal.Offer ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
}
|
||||
|
||||
private void Refresh()
|
||||
{
|
||||
var offerInfo = controller.CurrentOffer;
|
||||
if (offerInfo == null)
|
||||
{
|
||||
titleLabel.text = "Offer";
|
||||
nameLabel.text = "—";
|
||||
priceLabel.text = string.Empty;
|
||||
buyButton.SetEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
titleLabel.text = string.IsNullOrEmpty(offerInfo.StoreName) ? offerInfo.StoreSlug : offerInfo.StoreName;
|
||||
|
||||
var offer = offerInfo.Offers != null && offerInfo.Offers.Count > 0 ? offerInfo.Offers[0] : null;
|
||||
if (offer == null)
|
||||
{
|
||||
nameLabel.text = "—";
|
||||
priceLabel.text = string.Empty;
|
||||
buyButton.SetEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
nameLabel.text = string.IsNullOrEmpty(offer.Name) ? offer.OfferId : offer.Name;
|
||||
priceLabel.text = ShopModalPanel.PriceLabel(offer.Price, offer.Currency);
|
||||
buyButton.SetEnabled(true);
|
||||
}
|
||||
|
||||
private void HandleBuyClicked()
|
||||
{
|
||||
controller.BuyScenarioOffer();
|
||||
}
|
||||
|
||||
private void HandleDeclineClicked()
|
||||
{
|
||||
controller.DeclineScenarioOffer();
|
||||
}
|
||||
|
||||
private void HandleCloseClicked()
|
||||
{
|
||||
controller.CloseModal();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b996708622cc54d328cac546b893e111
|
||||
@@ -0,0 +1,92 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace LiveOpsExamples
|
||||
{
|
||||
public class RemoteConfigPanel : MonoBehaviour
|
||||
{
|
||||
private const string FlashClass = "flash";
|
||||
private const long FlashMilliseconds = 600;
|
||||
|
||||
[SerializeField] private PanelRenderer panelRenderer;
|
||||
[SerializeField] private CozyCollectorController controller;
|
||||
|
||||
private Label _playerSpeedValue;
|
||||
private Label _spawnIntervalValue;
|
||||
private Label _collectibleScoreValue;
|
||||
private Label _roundSecondsValue;
|
||||
private VisualElement _playerSpeedRow;
|
||||
private VisualElement _spawnIntervalRow;
|
||||
private VisualElement _collectibleScoreRow;
|
||||
private VisualElement _roundSecondsRow;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
panelRenderer.RegisterUIReloadCallback(OnUIReload);
|
||||
controller.TuningChanged += HandleTuningChanged;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
panelRenderer.UnregisterUIReloadCallback(OnUIReload);
|
||||
controller.TuningChanged -= HandleTuningChanged;
|
||||
}
|
||||
|
||||
private void OnUIReload(PanelRenderer renderer, VisualElement root)
|
||||
{
|
||||
_playerSpeedValue = root.Q<Label>("rc-value-player-speed");
|
||||
_spawnIntervalValue = root.Q<Label>("rc-value-spawn-interval");
|
||||
_collectibleScoreValue = root.Q<Label>("rc-value-collectible-score");
|
||||
_roundSecondsValue = root.Q<Label>("rc-value-round-seconds");
|
||||
_playerSpeedRow = root.Q("rc-row-player-speed");
|
||||
_spawnIntervalRow = root.Q("rc-row-spawn-interval");
|
||||
_collectibleScoreRow = root.Q("rc-row-collectible-score");
|
||||
_roundSecondsRow = root.Q("rc-row-round-seconds");
|
||||
|
||||
Refresh();
|
||||
}
|
||||
|
||||
private void Refresh()
|
||||
{
|
||||
var tuning = controller.Tuning;
|
||||
if (tuning == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_playerSpeedValue.text = $"{Mathf.RoundToInt(tuning.PlayerSpeed)}";
|
||||
_spawnIntervalValue.text = $"{Mathf.RoundToInt(tuning.SpawnIntervalMs)} ms";
|
||||
_collectibleScoreValue.text = $"+{Mathf.RoundToInt(tuning.CollectibleScore)}";
|
||||
_roundSecondsValue.text = $"{Mathf.RoundToInt(tuning.RoundSeconds)} s";
|
||||
}
|
||||
|
||||
private void HandleTuningChanged(string key)
|
||||
{
|
||||
Refresh();
|
||||
|
||||
string normalized = key == null ? string.Empty : key.ToLowerInvariant().Replace("_", string.Empty).Replace("-", string.Empty).Replace(" ", string.Empty);
|
||||
if (normalized.Contains("playerspeed"))
|
||||
{
|
||||
Flash(_playerSpeedRow);
|
||||
}
|
||||
else if (normalized.Contains("spawninterval"))
|
||||
{
|
||||
Flash(_spawnIntervalRow);
|
||||
}
|
||||
else if (normalized.Contains("collectiblescore"))
|
||||
{
|
||||
Flash(_collectibleScoreRow);
|
||||
}
|
||||
else if (normalized.Contains("roundseconds"))
|
||||
{
|
||||
Flash(_roundSecondsRow);
|
||||
}
|
||||
}
|
||||
|
||||
private static void Flash(VisualElement row)
|
||||
{
|
||||
row.AddToClassList(FlashClass);
|
||||
row.schedule.Execute(() => row.RemoveFromClassList(FlashClass)).StartingIn(FlashMilliseconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1d57e6a031317461cb98e41f6940c8d3
|
||||
@@ -0,0 +1,117 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace LiveOpsExamples
|
||||
{
|
||||
public class RoundResultModalPanel : MonoBehaviour
|
||||
{
|
||||
private const int MaxLeaderboardRows = 10;
|
||||
|
||||
[SerializeField] private PanelRenderer panelRenderer;
|
||||
[SerializeField] private CozyCollectorController controller;
|
||||
[SerializeField] private VisualTreeAsset leaderboardRowTemplate;
|
||||
|
||||
private VisualElement root;
|
||||
private Label scoreLabel;
|
||||
private Label bestLabel;
|
||||
private Label collectedLabel;
|
||||
private ScrollView leaderboardScroll;
|
||||
private VisualElement emptyNote;
|
||||
private Button playAgainButton;
|
||||
private Button claimButton;
|
||||
private Button endButton;
|
||||
private Button closeButton;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
panelRenderer.RegisterUIReloadCallback(OnUIReload);
|
||||
controller.ModalChanged += ApplyVisibility;
|
||||
controller.RoundResultChanged += Refresh;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
panelRenderer.UnregisterUIReloadCallback(OnUIReload);
|
||||
controller.ModalChanged -= ApplyVisibility;
|
||||
controller.RoundResultChanged -= Refresh;
|
||||
}
|
||||
|
||||
private void OnUIReload(PanelRenderer renderer, VisualElement root)
|
||||
{
|
||||
this.root = root.Q<VisualElement>("round-result-modal");
|
||||
scoreLabel = this.root.Q<Label>("rr-score");
|
||||
bestLabel = this.root.Q<Label>("rr-best");
|
||||
collectedLabel = this.root.Q<Label>("rr-collected");
|
||||
leaderboardScroll = this.root.Q<ScrollView>("rr-leaderboard-scroll");
|
||||
emptyNote = this.root.Q<VisualElement>("rr-empty");
|
||||
playAgainButton = this.root.Q<Button>("rr-play-again-button");
|
||||
claimButton = this.root.Q<Button>("rr-claim-button");
|
||||
endButton = this.root.Q<Button>("rr-end-button");
|
||||
closeButton = this.root.Q<Button>("rr-close-button");
|
||||
|
||||
playAgainButton.clicked -= HandlePlayAgainClicked;
|
||||
playAgainButton.clicked += HandlePlayAgainClicked;
|
||||
claimButton.clicked -= HandleClaimClicked;
|
||||
claimButton.clicked += HandleClaimClicked;
|
||||
endButton.clicked -= HandleEndClicked;
|
||||
endButton.clicked += HandleEndClicked;
|
||||
closeButton.clicked -= HandleCloseClicked;
|
||||
closeButton.clicked += HandleCloseClicked;
|
||||
|
||||
ApplyVisibility();
|
||||
Refresh();
|
||||
}
|
||||
|
||||
private void ApplyVisibility()
|
||||
{
|
||||
root.style.display = controller.ActiveModal == CozyModal.RoundResult ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
}
|
||||
|
||||
private void Refresh()
|
||||
{
|
||||
var result = controller.RoundResult;
|
||||
scoreLabel.text = result != null ? result.Score.ToString() : "0";
|
||||
bestLabel.text = result != null ? result.Best.ToString() : "0";
|
||||
collectedLabel.text = result != null ? result.Collected.ToString() : "0";
|
||||
|
||||
leaderboardScroll.Clear();
|
||||
|
||||
var entries = result != null ? result.TopEntries : null;
|
||||
emptyNote.style.display = entries == null || entries.Count == 0 ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
if (entries == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int count = Mathf.Min(entries.Count, MaxLeaderboardRows);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var entry = entries[i];
|
||||
var rowRoot = leaderboardRowTemplate.CloneTree();
|
||||
var row = new LeaderboardRowView(rowRoot);
|
||||
row.Set(entry.Rank, string.IsNullOrEmpty(entry.PlayerName) ? "player" : entry.PlayerName, entry.Score);
|
||||
leaderboardScroll.Add(rowRoot);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandlePlayAgainClicked()
|
||||
{
|
||||
controller.PlayAgain();
|
||||
}
|
||||
|
||||
private void HandleClaimClicked()
|
||||
{
|
||||
controller.ClaimScenarioLeaderboardReward();
|
||||
}
|
||||
|
||||
private void HandleEndClicked()
|
||||
{
|
||||
controller.EndScenarioLeaderboard();
|
||||
}
|
||||
|
||||
private void HandleCloseClicked()
|
||||
{
|
||||
controller.CloseModal();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6f3916e69e6ce4c78b909c659160ad95
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cde562fa676ee420795c27bd66549cab
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,29 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace LiveOpsExamples
|
||||
{
|
||||
public class BackpackItemRowView
|
||||
{
|
||||
private readonly VisualElement _icon;
|
||||
private readonly Label _nameLabel;
|
||||
private readonly Label _metaLabel;
|
||||
private readonly Label _amountLabel;
|
||||
|
||||
public BackpackItemRowView(VisualElement root)
|
||||
{
|
||||
_icon = root.Q<VisualElement>("item-icon");
|
||||
_nameLabel = root.Q<Label>("item-name");
|
||||
_metaLabel = root.Q<Label>("item-meta");
|
||||
_amountLabel = root.Q<Label>("item-amount");
|
||||
}
|
||||
|
||||
public void Set(Sprite icon, string itemName, string slug, int amount)
|
||||
{
|
||||
_icon.style.backgroundImage = new StyleBackground(icon);
|
||||
_nameLabel.text = itemName;
|
||||
_metaLabel.text = slug;
|
||||
_amountLabel.text = $"×{amount}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d7a25568da97f48f3b07abf321ad2f82
|
||||
@@ -0,0 +1,25 @@
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace LiveOpsExamples
|
||||
{
|
||||
public class LeaderboardRowView
|
||||
{
|
||||
private readonly Label rankLabel;
|
||||
private readonly Label playerLabel;
|
||||
private readonly Label scoreLabel;
|
||||
|
||||
public LeaderboardRowView(VisualElement root)
|
||||
{
|
||||
rankLabel = root.Q<Label>("lb-rank");
|
||||
playerLabel = root.Q<Label>("lb-player");
|
||||
scoreLabel = root.Q<Label>("lb-score");
|
||||
}
|
||||
|
||||
public void Set(int rank, string playerName, int score)
|
||||
{
|
||||
rankLabel.text = rank > 0 ? $"#{rank}" : "—";
|
||||
playerLabel.text = playerName;
|
||||
scoreLabel.text = score.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 29788c49080d642ff919c1d25b5dd979
|
||||
@@ -0,0 +1,31 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace LiveOpsExamples
|
||||
{
|
||||
public class LogRowView
|
||||
{
|
||||
private readonly Label _timeLabel;
|
||||
private readonly Label _tagLabel;
|
||||
private readonly Label _messageLabel;
|
||||
private readonly Label _detailLabel;
|
||||
|
||||
public LogRowView(VisualElement root)
|
||||
{
|
||||
_timeLabel = root.Q<Label>("log-time");
|
||||
_tagLabel = root.Q<Label>("log-tag");
|
||||
_messageLabel = root.Q<Label>("log-message");
|
||||
_detailLabel = root.Q<Label>("log-detail");
|
||||
}
|
||||
|
||||
public void Set(string time, string tag, Color tagColor, string message, string detail)
|
||||
{
|
||||
_timeLabel.text = time;
|
||||
_tagLabel.text = tag;
|
||||
_tagLabel.style.color = tagColor;
|
||||
_tagLabel.style.backgroundColor = new Color(tagColor.r, tagColor.g, tagColor.b, 0.18f);
|
||||
_messageLabel.text = message;
|
||||
_detailLabel.text = detail;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ffa213acafee84d818a3cf631f0bb01b
|
||||
@@ -0,0 +1,26 @@
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace LiveOpsExamples
|
||||
{
|
||||
public class ShopOfferRowView
|
||||
{
|
||||
private readonly Label _nameLabel;
|
||||
private readonly Label _priceLabel;
|
||||
private readonly Button _buyButton;
|
||||
|
||||
public Button BuyButton => _buyButton;
|
||||
|
||||
public ShopOfferRowView(VisualElement root)
|
||||
{
|
||||
_nameLabel = root.Q<Label>("offer-name");
|
||||
_priceLabel = root.Q<Label>("offer-price");
|
||||
_buyButton = root.Q<Button>("offer-buy-button");
|
||||
}
|
||||
|
||||
public void Set(string offerName, string price)
|
||||
{
|
||||
_nameLabel.text = offerName;
|
||||
_priceLabel.text = price;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 94bf1e4d6fb50418487ed96aa4d65d4b
|
||||
@@ -0,0 +1,32 @@
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace LiveOpsExamples
|
||||
{
|
||||
public class ToastView
|
||||
{
|
||||
private const string ToneInfoClass = "tone-info";
|
||||
private const string ToneSuccessClass = "tone-success";
|
||||
private const string ToneErrorClass = "tone-error";
|
||||
|
||||
private readonly Label _titleLabel;
|
||||
private readonly Label _messageLabel;
|
||||
|
||||
public ToastView(VisualElement root)
|
||||
{
|
||||
Root = root;
|
||||
_titleLabel = root.Q<Label>("toast-title");
|
||||
_messageLabel = root.Q<Label>("toast-message");
|
||||
}
|
||||
|
||||
public VisualElement Root { get; }
|
||||
|
||||
public void Set(string title, string message, CozyToastTone tone)
|
||||
{
|
||||
_titleLabel.text = title;
|
||||
_messageLabel.text = message;
|
||||
Root.EnableInClassList(ToneInfoClass, tone == CozyToastTone.Info);
|
||||
Root.EnableInClassList(ToneSuccessClass, tone == CozyToastTone.Success);
|
||||
Root.EnableInClassList(ToneErrorClass, tone == CozyToastTone.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5d468efab7b7645f78186c03b82f5d32
|
||||
@@ -0,0 +1,87 @@
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace LiveOpsExamples
|
||||
{
|
||||
public class SessionCardPanel : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private PanelRenderer panelRenderer;
|
||||
[SerializeField] private CozyCollectorController controller;
|
||||
|
||||
private Label _nicknameLabel;
|
||||
private Label _playerIdLabel;
|
||||
private Label _deviceIdLabel;
|
||||
private Label _regionLabel;
|
||||
private Label _projectKeyLabel;
|
||||
private Label _walletsLabel;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
panelRenderer.RegisterUIReloadCallback(OnUIReload);
|
||||
controller.PhaseChanged += Refresh;
|
||||
controller.WalletsChanged += RefreshWallets;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
panelRenderer.UnregisterUIReloadCallback(OnUIReload);
|
||||
controller.PhaseChanged -= Refresh;
|
||||
controller.WalletsChanged -= RefreshWallets;
|
||||
}
|
||||
|
||||
private void OnUIReload(PanelRenderer renderer, VisualElement root)
|
||||
{
|
||||
_nicknameLabel = root.Q<Label>("session-nickname");
|
||||
_playerIdLabel = root.Q<Label>("session-player-id");
|
||||
_deviceIdLabel = root.Q<Label>("session-device-id");
|
||||
_regionLabel = root.Q<Label>("session-region");
|
||||
_projectKeyLabel = root.Q<Label>("session-project-key");
|
||||
_walletsLabel = root.Q<Label>("session-wallets");
|
||||
|
||||
Refresh();
|
||||
}
|
||||
|
||||
private void Refresh()
|
||||
{
|
||||
var identity = controller.Identity;
|
||||
if (identity == null)
|
||||
{
|
||||
_nicknameLabel.text = "—";
|
||||
_playerIdLabel.text = "—";
|
||||
_deviceIdLabel.text = "—";
|
||||
_regionLabel.text = "—";
|
||||
_projectKeyLabel.text = "—";
|
||||
}
|
||||
else
|
||||
{
|
||||
_nicknameLabel.text = string.IsNullOrEmpty(identity.Nickname)
|
||||
? $"Player {identity.PlayerId.Substring(0, Mathf.Min(6, identity.PlayerId.Length))}"
|
||||
: identity.Nickname;
|
||||
_playerIdLabel.text = identity.PlayerId;
|
||||
_deviceIdLabel.text = identity.DeviceId;
|
||||
_regionLabel.text = string.IsNullOrEmpty(identity.Region) ? "global" : identity.Region;
|
||||
_projectKeyLabel.text = identity.ProjectKey;
|
||||
}
|
||||
|
||||
RefreshWallets();
|
||||
}
|
||||
|
||||
private void RefreshWallets()
|
||||
{
|
||||
var wallets = controller.Wallets;
|
||||
if (wallets == null || wallets.Count == 0)
|
||||
{
|
||||
_walletsLabel.text = "No wallets";
|
||||
return;
|
||||
}
|
||||
|
||||
var builder = new StringBuilder();
|
||||
foreach (var wallet in wallets)
|
||||
{
|
||||
builder.AppendLine($"{wallet.Balance:0.##} {wallet.Currency}".TrimEnd());
|
||||
}
|
||||
_walletsLabel.text = builder.ToString().TrimEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 900b9e31987914b56a3bba037e2a830c
|
||||
@@ -0,0 +1,43 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace LiveOpsExamples
|
||||
{
|
||||
public class SetupOverlayPanel : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private PanelRenderer panelRenderer;
|
||||
[SerializeField] private CozyCollectorController controller;
|
||||
|
||||
private VisualElement _overlay;
|
||||
private Label _errorLabel;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
panelRenderer.RegisterUIReloadCallback(OnUIReload);
|
||||
controller.PhaseChanged += ApplyVisibility;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
panelRenderer.UnregisterUIReloadCallback(OnUIReload);
|
||||
controller.PhaseChanged -= ApplyVisibility;
|
||||
}
|
||||
|
||||
private void OnUIReload(PanelRenderer renderer, VisualElement root)
|
||||
{
|
||||
_overlay = root.Q<VisualElement>("setup-overlay");
|
||||
_errorLabel = root.Q<Label>("setup-error");
|
||||
ApplyVisibility();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
_errorLabel.text = controller.SetupError;
|
||||
}
|
||||
|
||||
private void ApplyVisibility()
|
||||
{
|
||||
_overlay.style.display = controller.Phase == CozyPhase.Setup ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6de014257693b4ced830566d18e49444
|
||||
@@ -0,0 +1,115 @@
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace LiveOpsExamples
|
||||
{
|
||||
public class ShopModalPanel : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private PanelRenderer panelRenderer;
|
||||
[SerializeField] private CozyCollectorController controller;
|
||||
[SerializeField] private VisualTreeAsset shopOfferRowTemplate;
|
||||
|
||||
private VisualElement _root;
|
||||
private Label _walletLabel;
|
||||
private ScrollView _offersScroll;
|
||||
private VisualElement _emptyNote;
|
||||
private Button _closeButton;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
panelRenderer.RegisterUIReloadCallback(OnUIReload);
|
||||
controller.ModalChanged += ApplyVisibility;
|
||||
controller.ShopChanged += Rebuild;
|
||||
controller.WalletsChanged += RefreshWallet;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
panelRenderer.UnregisterUIReloadCallback(OnUIReload);
|
||||
controller.ModalChanged -= ApplyVisibility;
|
||||
controller.ShopChanged -= Rebuild;
|
||||
controller.WalletsChanged -= RefreshWallet;
|
||||
}
|
||||
|
||||
private void OnUIReload(PanelRenderer renderer, VisualElement root)
|
||||
{
|
||||
_root = root.Q<VisualElement>("shop-modal");
|
||||
_walletLabel = _root.Q<Label>("shop-wallet");
|
||||
_offersScroll = _root.Q<ScrollView>("shop-offers-scroll");
|
||||
_emptyNote = _root.Q<VisualElement>("shop-empty");
|
||||
_closeButton = _root.Q<Button>("shop-close-button");
|
||||
|
||||
_closeButton.clicked -= HandleCloseClicked;
|
||||
_closeButton.clicked += HandleCloseClicked;
|
||||
|
||||
ApplyVisibility();
|
||||
RefreshWallet();
|
||||
Rebuild();
|
||||
}
|
||||
|
||||
private void ApplyVisibility()
|
||||
{
|
||||
_root.style.display = controller.ActiveModal == CozyModal.Shop ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
}
|
||||
|
||||
private void RefreshWallet()
|
||||
{
|
||||
var wallets = controller.Wallets;
|
||||
if (wallets == null || wallets.Count == 0)
|
||||
{
|
||||
_walletLabel.text = "Wallet: —";
|
||||
return;
|
||||
}
|
||||
|
||||
var builder = new StringBuilder("Wallet: ");
|
||||
foreach (var wallet in wallets)
|
||||
{
|
||||
if (builder.Length > "Wallet: ".Length)
|
||||
{
|
||||
builder.Append(" · ");
|
||||
}
|
||||
builder.Append($"{wallet.Balance:0.##} {wallet.Currency}".TrimEnd());
|
||||
}
|
||||
_walletLabel.text = builder.ToString();
|
||||
}
|
||||
|
||||
private void Rebuild()
|
||||
{
|
||||
_offersScroll.contentContainer.Clear();
|
||||
|
||||
var offers = controller.ShopOffers;
|
||||
_emptyNote.style.display = offers == null || offers.Count == 0 ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
if (offers == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var offer in offers)
|
||||
{
|
||||
var rowRoot = shopOfferRowTemplate.CloneTree();
|
||||
_offersScroll.contentContainer.Add(rowRoot);
|
||||
|
||||
var row = new ShopOfferRowView(rowRoot);
|
||||
row.Set(string.IsNullOrEmpty(offer.Name) ? offer.OfferId : offer.Name, PriceLabel(offer.Price, offer.Currency));
|
||||
|
||||
string offerId = offer.OfferId;
|
||||
row.BuyButton.clicked += () => controller.BuyShopOffer(offerId);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string PriceLabel(double price, string currency)
|
||||
{
|
||||
if (price <= 0)
|
||||
{
|
||||
return "Free";
|
||||
}
|
||||
return $"{price:0.##} {currency}".TrimEnd();
|
||||
}
|
||||
|
||||
private void HandleCloseClicked()
|
||||
{
|
||||
controller.CloseModal();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 36789e549043d400a92f3a84849b4038
|
||||
@@ -0,0 +1,78 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace LiveOpsExamples
|
||||
{
|
||||
public class ToastsPanel : MonoBehaviour
|
||||
{
|
||||
private const float ToastLifetimeSeconds = 4.2f;
|
||||
|
||||
[SerializeField] private PanelRenderer panelRenderer;
|
||||
[SerializeField] private CozyCollectorController controller;
|
||||
[SerializeField] private VisualTreeAsset toastTemplate;
|
||||
|
||||
private readonly List<ToastView> _toasts = new List<ToastView>();
|
||||
private readonly List<float> _dismissAt = new List<float>();
|
||||
|
||||
private VisualElement _container;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
panelRenderer.RegisterUIReloadCallback(OnUIReload);
|
||||
controller.ToastRequested += HandleToastRequested;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
panelRenderer.UnregisterUIReloadCallback(OnUIReload);
|
||||
controller.ToastRequested -= HandleToastRequested;
|
||||
}
|
||||
|
||||
private void OnUIReload(PanelRenderer renderer, VisualElement root)
|
||||
{
|
||||
_container = root.Q("toasts");
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
for (int i = _toasts.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (Time.unscaledTime >= _dismissAt[i])
|
||||
{
|
||||
Dismiss(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleToastRequested(string title, string message, CozyToastTone tone)
|
||||
{
|
||||
var root = toastTemplate.CloneTree();
|
||||
_container.Add(root);
|
||||
|
||||
var toast = new ToastView(root);
|
||||
toast.Set(title, message, tone);
|
||||
toast.Root.RegisterCallback<ClickEvent>(_ => Dismiss(toast));
|
||||
|
||||
_toasts.Add(toast);
|
||||
_dismissAt.Add(Time.unscaledTime + ToastLifetimeSeconds);
|
||||
}
|
||||
|
||||
private void Dismiss(ToastView toast)
|
||||
{
|
||||
int index = _toasts.IndexOf(toast);
|
||||
if (index >= 0)
|
||||
{
|
||||
Dismiss(index);
|
||||
}
|
||||
}
|
||||
|
||||
private void Dismiss(int index)
|
||||
{
|
||||
var toast = _toasts[index];
|
||||
_toasts.RemoveAt(index);
|
||||
_dismissAt.RemoveAt(index);
|
||||
toast.Root.RemoveFromHierarchy();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 57e0ed949960344bd80072dd54a52c4f
|
||||
@@ -0,0 +1,134 @@
|
||||
using System.Collections;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace LiveOpsExamples
|
||||
{
|
||||
public static class UiDebugProbe
|
||||
{
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
|
||||
private static void Install()
|
||||
{
|
||||
new GameObject("UiDebugProbe").AddComponent<Probe>();
|
||||
}
|
||||
|
||||
private sealed class Probe : MonoBehaviour
|
||||
{
|
||||
private PanelRenderer _panelRenderer;
|
||||
private VisualElement _root;
|
||||
|
||||
private IEnumerator Start()
|
||||
{
|
||||
yield return null;
|
||||
yield return null;
|
||||
|
||||
_panelRenderer = FindFirstObjectByType<PanelRenderer>();
|
||||
if (_panelRenderer == null)
|
||||
{
|
||||
Debug.Log("[UiProbe] PanelRenderer: NOT FOUND");
|
||||
}
|
||||
else
|
||||
{
|
||||
_panelRenderer.RegisterUIReloadCallback(OnUIReload);
|
||||
}
|
||||
|
||||
if (_root == null)
|
||||
{
|
||||
Dump("after-scene-load");
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
yield return new WaitForSecondsRealtime(10f);
|
||||
Dump("periodic");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (_panelRenderer != null)
|
||||
{
|
||||
_panelRenderer.UnregisterUIReloadCallback(OnUIReload);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUIReload(PanelRenderer renderer, VisualElement root)
|
||||
{
|
||||
_root = root;
|
||||
Dump("ui-reload");
|
||||
}
|
||||
|
||||
private void Dump(string tag)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine($"[UiProbe] --- {tag} ---");
|
||||
sb.AppendLine($"screen={Screen.width}x{Screen.height}");
|
||||
|
||||
if (_panelRenderer == null)
|
||||
{
|
||||
sb.AppendLine("PanelRenderer: NOT FOUND");
|
||||
Debug.Log(sb.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
var ps = _panelRenderer.panelSettings;
|
||||
sb.AppendLine(ps == null
|
||||
? "panelSettings: NULL"
|
||||
: $"panelSettings={ps.name} scaleMode={ps.scaleMode} ref={ps.referenceResolution.x}x{ps.referenceResolution.y} match={ps.match} scale={ps.scale}");
|
||||
|
||||
if (_root == null)
|
||||
{
|
||||
sb.AppendLine("root: NULL (reload callback not fired yet)");
|
||||
Debug.Log(sb.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
sb.AppendLine($"root worldBound={_root.worldBound} children={_root.childCount}");
|
||||
var uiRoot = _root.childCount > 0 ? _root[0] : null;
|
||||
sb.AppendLine($"uiRoot={uiRoot?.name} children={uiRoot?.childCount}");
|
||||
if (uiRoot != null)
|
||||
{
|
||||
foreach (var child in uiRoot.Children())
|
||||
{
|
||||
Describe(sb, child);
|
||||
}
|
||||
}
|
||||
|
||||
var renderers = FindObjectsByType<PanelRenderer>(FindObjectsSortMode.None);
|
||||
sb.AppendLine($"panelRenderers={renderers.Length} panel={(_root.panel == null ? "null" : "ok")}");
|
||||
|
||||
Describe(sb, _root.Q("hud-score"));
|
||||
Describe(sb, _root.Q("hud-timer-fill"));
|
||||
Describe(sb, _root.Q("session-card"));
|
||||
Describe(sb, _root.Q("remote-config"));
|
||||
Describe(sb, _root.Q("event-log-scroll"));
|
||||
|
||||
var controller = FindFirstObjectByType<CozyCollectorController>();
|
||||
sb.AppendLine(controller == null
|
||||
? "controller: NOT FOUND"
|
||||
: $"controller phase={controller.Phase} activeModal={controller.ActiveModal} status='{controller.StatusMessage}'");
|
||||
|
||||
var cam = Camera.main;
|
||||
sb.AppendLine(cam == null
|
||||
? "camera: NOT FOUND"
|
||||
: $"camera rect={cam.rect} targetTexture={(cam.targetTexture == null ? "null" : cam.targetTexture.name)} enabled={cam.enabled} clearFlags={cam.clearFlags} time={Time.time:0.#} timeScale={Time.timeScale}");
|
||||
|
||||
Debug.Log(sb.ToString());
|
||||
}
|
||||
|
||||
private static void Describe(StringBuilder sb, VisualElement element)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
sb.AppendLine(" <element not found>");
|
||||
return;
|
||||
}
|
||||
|
||||
var r = element.resolvedStyle;
|
||||
sb.AppendLine($" {element.name}: display={r.display} visibility={r.visibility} opacity={r.opacity} " +
|
||||
$"size={r.width:0.#}x{r.height:0.#} worldBound={element.worldBound} classes='{string.Join(",", element.GetClasses())}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b70a6370f0b8b48aabebe030ae087adf
|
||||
Reference in New Issue
Block a user