[Add] Menu with configs and size fix

This commit is contained in:
2026-06-06 23:48:55 +07:00
parent 7104089c00
commit a9767c5301
29 changed files with 453 additions and 153 deletions
@@ -5,11 +5,11 @@ namespace Minesweeper.Config
[CreateAssetMenu(fileName = "MinesweeperGameConfig", menuName = "Minesweeper/Game Config")]
public sealed class MinesweeperGameConfig : ScriptableObject
{
[field: SerializeField, Min(1)] public int Width { get; private set; } = 9;
[field: SerializeField, Min(1)] public int Height { get; private set; } = 9;
[field: SerializeField, Min(1)] public int MinesCount { get; private set; } = 10;
[field: SerializeField] public KeyCode RestartKey { get; private set; } = KeyCode.R;
[field: SerializeField, Min(1)] public int MinSizeX { get; private set; } = 2;
[field: SerializeField, Min(1)] public int MaxSizeX { get; private set; } = 50;
[field: SerializeField, Min(1)] public int MinSizeY { get; private set; } = 2;
[field: SerializeField, Min(1)] public int MaxSizeY { get; private set; } = 50;
public bool IsValid => Width > 0 && Height > 0 && MinesCount > 0 && MinesCount < Width * Height;
public bool IsValid => MinSizeX > 0 && MinSizeY > 0 && MaxSizeX >= MinSizeX && MaxSizeY >= MinSizeY && MaxSizeX * MaxSizeY > 1;
}
}
+6 -27
View File
@@ -1,22 +1,17 @@
using System;
using System.Collections.Generic;
using Minesweeper.Config;
namespace Minesweeper.Core
{
public sealed class BoardService : IBoardService
{
private const int DefaultWidth = 9;
private const int DefaultHeight = 9;
private const int DefaultMinesCount = 10;
private readonly MinesweeperGameConfig config;
private readonly IGameSettingsService settingsService;
private readonly Random random = new Random();
private CellData[,] cells;
public BoardService(MinesweeperGameConfig config)
public BoardService(IGameSettingsService settingsService)
{
this.config = config;
this.settingsService = settingsService;
}
public int Width { get; private set; }
@@ -28,11 +23,9 @@ namespace Minesweeper.Core
public void InitializeEmptyBoard()
{
ResolveConfig(out var width, out var height, out var minesCount);
Width = width;
Height = height;
MinesCount = minesCount;
Width = settingsService.SizeX;
Height = settingsService.SizeY;
MinesCount = Math.Min(settingsService.MinesCount, Width * Height - 1);
OpenedSafeCellsCount = 0;
IsGenerated = false;
cells = new CellData[Width, Height];
@@ -147,20 +140,6 @@ namespace Minesweeper.Core
return result;
}
private void ResolveConfig(out int width, out int height, out int minesCount)
{
width = config.Width;
height = config.Height;
minesCount = config.MinesCount;
if (width <= 0 || height <= 0 || minesCount <= 0 || minesCount >= width * height)
{
width = DefaultWidth;
height = DefaultHeight;
minesCount = DefaultMinesCount;
}
}
private void EnsureInitialized()
{
if (cells == null)
@@ -0,0 +1,81 @@
using Minesweeper.Config;
using UnityEngine;
namespace Minesweeper.Core
{
public sealed class GameSettingsService : IGameSettingsService
{
private const int MinimumMinesCount = 1;
private readonly MinesweeperGameConfig config;
private readonly IGameSettingsStorage storage;
private GameSettingsValue current;
public GameSettingsService(MinesweeperGameConfig config, IGameSettingsStorage storage)
{
this.config = config;
this.storage = storage;
current = LoadInitialSettings();
}
public int SizeX => current.SizeX;
public int SizeY => current.SizeY;
public int MinesCount => current.MinesCount;
public GameSettingsValue Current => current;
public GameSettingsValue Clamp(GameSettingsValue value)
{
var sizeX = Mathf.Clamp(value.SizeX, config.MinSizeX, config.MaxSizeX);
var sizeY = Mathf.Clamp(value.SizeY, config.MinSizeY, config.MaxSizeY);
EnsureAtLeastTwoCells(ref sizeX, ref sizeY);
var mines = Mathf.Clamp(value.MinesCount, MinimumMinesCount, GetMaxMines(sizeX, sizeY));
return new GameSettingsValue(sizeX, sizeY, mines);
}
public bool ApplyAndSaveIfChanged(GameSettingsValue value)
{
var clamped = Clamp(value);
if (current.Equals(clamped))
{
return false;
}
current = clamped;
storage.Save(current);
return true;
}
public int GetMaxMines(int sizeX, int sizeY)
{
return Mathf.Max(MinimumMinesCount, sizeX * sizeY - 1);
}
private GameSettingsValue LoadInitialSettings()
{
if (storage.TryLoad(out var saved))
{
return Clamp(saved);
}
return Clamp(new GameSettingsValue(config.MinSizeX, config.MinSizeY, MinimumMinesCount));
}
private void EnsureAtLeastTwoCells(ref int sizeX, ref int sizeY)
{
if (sizeX * sizeY > 1)
{
return;
}
if (sizeX < config.MaxSizeX)
{
sizeX++;
}
else if (sizeY < config.MaxSizeY)
{
sizeY++;
}
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 38b7be3d055b5c34bbba9b36c0e11fc6
+21
View File
@@ -0,0 +1,21 @@
namespace Minesweeper.Core
{
public readonly struct GameSettingsValue
{
public GameSettingsValue(int sizeX, int sizeY, int minesCount)
{
SizeX = sizeX;
SizeY = sizeY;
MinesCount = minesCount;
}
public int SizeX { get; }
public int SizeY { get; }
public int MinesCount { get; }
public bool Equals(GameSettingsValue other)
{
return SizeX == other.SizeX && SizeY == other.SizeY && MinesCount == other.MinesCount;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 5a48645a1753ceb4ba0401ec44d70070
@@ -0,0 +1,14 @@
namespace Minesweeper.Core
{
public interface IGameSettingsService
{
int SizeX { get; }
int SizeY { get; }
int MinesCount { get; }
GameSettingsValue Current { get; }
GameSettingsValue Clamp(GameSettingsValue value);
bool ApplyAndSaveIfChanged(GameSettingsValue value);
int GetMaxMines(int sizeX, int sizeY);
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 3dcff3a940f664b4b96722f28e46636d
@@ -0,0 +1,8 @@
namespace Minesweeper.Core
{
public interface IGameSettingsStorage
{
bool TryLoad(out GameSettingsValue value);
void Save(GameSettingsValue value);
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 54d04c5398018bc418e1a2528c559d0a
@@ -28,6 +28,8 @@ namespace Minesweeper.Infrastructure
builder.RegisterInstance(GetConfig());
var resolvedUiConfig = GetUiConfig();
builder.RegisterInstance(resolvedUiConfig);
builder.Register<PlayerPrefsGameSettingsStorage>(Lifetime.Singleton).As<IGameSettingsStorage>();
builder.Register<GameSettingsService>(Lifetime.Singleton).As<IGameSettingsService>();
builder.Register<GameStateService>(Lifetime.Singleton).As<IGameStateService>();
builder.Register<BoardService>(Lifetime.Singleton).As<IBoardService>();
builder.Register<GamePauseService>(Lifetime.Singleton).As<IGamePauseService>();
@@ -86,7 +88,6 @@ namespace Minesweeper.Infrastructure
builder.Register<ResumeCommandHandler>(Lifetime.Singleton);
builder.Register<GoToMenuCommandHandler>(Lifetime.Singleton);
builder.Register<GameCommandDispatcher>(Lifetime.Singleton).As<IGameCommandDispatcher>();
builder.Register<RestartKeyInputService>(Lifetime.Singleton).As<ITickable>();
builder.Register<MainMenuPresenter>(Lifetime.Singleton);
builder.Register<TopPanelPresenter>(Lifetime.Singleton);
@@ -0,0 +1,32 @@
using Minesweeper.Core;
using UnityEngine;
namespace Minesweeper.Infrastructure
{
public sealed class PlayerPrefsGameSettingsStorage : IGameSettingsStorage
{
private const string SizeXKey = "Minesweeper.Settings.SizeX";
private const string SizeYKey = "Minesweeper.Settings.SizeY";
private const string MinesCountKey = "Minesweeper.Settings.MinesCount";
public bool TryLoad(out GameSettingsValue value)
{
if (!PlayerPrefs.HasKey(SizeXKey) || !PlayerPrefs.HasKey(SizeYKey) || !PlayerPrefs.HasKey(MinesCountKey))
{
value = default;
return false;
}
value = new GameSettingsValue(PlayerPrefs.GetInt(SizeXKey), PlayerPrefs.GetInt(SizeYKey), PlayerPrefs.GetInt(MinesCountKey));
return true;
}
public void Save(GameSettingsValue value)
{
PlayerPrefs.SetInt(SizeXKey, value.SizeX);
PlayerPrefs.SetInt(SizeYKey, value.SizeY);
PlayerPrefs.SetInt(MinesCountKey, value.MinesCount);
PlayerPrefs.Save();
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: a001fc2e924d95643a075190b6cadda1
@@ -1,50 +0,0 @@
using Minesweeper.Commands;
using Minesweeper.Config;
using Minesweeper.Core;
using UnityEngine.InputSystem;
using VContainer.Unity;
namespace Minesweeper.Infrastructure
{
public sealed class RestartKeyInputService : ITickable
{
private readonly IGameCommandDispatcher commandDispatcher;
private readonly MinesweeperGameConfig config;
private readonly IGameStateService gameStateService;
public RestartKeyInputService(IGameCommandDispatcher commandDispatcher, MinesweeperGameConfig config, IGameStateService gameStateService)
{
this.commandDispatcher = commandDispatcher;
this.config = config;
this.gameStateService = gameStateService;
}
public void Tick()
{
var keyboard = Keyboard.current;
if (keyboard == null || !IsRestartPressed(keyboard))
{
return;
}
if (gameStateService.Current == GameState.FieldSelection)
{
commandDispatcher.Dispatch(new StartGameCommand());
}
else
{
commandDispatcher.Dispatch(new RestartCommand());
}
}
private bool IsRestartPressed(Keyboard keyboard)
{
if (config.RestartKey == UnityEngine.KeyCode.R)
{
return keyboard.rKey.wasPressedThisFrame;
}
return false;
}
}
}
@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: c87c15d092dd420b85a09cc786496948
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -11,6 +11,7 @@ namespace Minesweeper.Presentation.Factories
{
private const string ContentImagePath = "Content/Image";
private const string ContentLabelPath = "Content/Text (TMP)";
private const string ContentPath = "Content";
private readonly MinesweeperUiConfig uiConfig;
@@ -33,9 +34,10 @@ namespace Minesweeper.Presentation.Factories
var button = go.GetComponent<Button>();
var backgroundImage = go.GetComponent<Image>();
var contentRoot = FindComponent<RectTransform>(go.transform, ContentPath);
var contentImage = FindComponent<Image>(go.transform, ContentImagePath);
var label = FindComponent<TMP_Text>(go.transform, ContentLabelPath);
view.Bind(button, backgroundImage, contentImage, label);
view.Bind(button, backgroundImage, contentRoot, contentImage, label);
view.AutoBind();
view.Initialize(cell.X, cell.Y);
return view;
@@ -12,7 +12,6 @@ namespace Minesweeper.Presentation.Factories
private const string BoardGridName = "BoardGrid";
private const string PausePanelName = "PausePanel";
private const string ResultPanelName = "ResultPanel";
private const string StartButtonPath = "StartButton";
private const string RestartButtonPath = "RestartButton";
private const string ContinueButtonPath = "ContinueButton";
private const string MainMenuButtonPath = "MainMenuButton";
@@ -40,7 +39,7 @@ namespace Minesweeper.Presentation.Factories
var result = SpawnScreen(catalog.ResultPanelPrefab, contentRoot, ResultPanelName, 3);
var mainMenuView = RequireComponent<MainMenuView>(mainMenu.transform, MainMenuPanelName);
mainMenuView.Bind(mainMenu, RequireChildComponent<Button>(mainMenu.transform, StartButtonPath));
mainMenuView.BindRoot(mainMenu);
var refs = new MinesweeperScreenRefs(
mainMenuView,
@@ -1,4 +1,5 @@
using Minesweeper.Commands;
using Minesweeper.Config;
using Minesweeper.Core;
using Minesweeper.Presentation.Views;
@@ -7,12 +8,16 @@ namespace Minesweeper.Presentation.Presenters
public sealed class MainMenuPresenter : IPresenter
{
private readonly IGameCommandDispatcher commandDispatcher;
private readonly MinesweeperGameConfig config;
private readonly IGameSettingsService settingsService;
private readonly IGameStateService gameStateService;
private readonly IMainMenuView view;
public MainMenuPresenter(IGameCommandDispatcher commandDispatcher, IGameStateService gameStateService, IMainMenuView view = null)
public MainMenuPresenter(IGameCommandDispatcher commandDispatcher, MinesweeperGameConfig config, IGameSettingsService settingsService, IGameStateService gameStateService, IMainMenuView view = null)
{
this.commandDispatcher = commandDispatcher;
this.config = config;
this.settingsService = settingsService;
this.gameStateService = gameStateService;
this.view = view;
}
@@ -22,7 +27,9 @@ namespace Minesweeper.Presentation.Presenters
if (view != null)
{
view.StartClicked += OnStartClicked;
view.SizeChanged += OnSizeChanged;
gameStateService.StateChanged += OnStateChanged;
RefreshMenuValues(settingsService.Current);
OnStateChanged(gameStateService.Current);
}
}
@@ -32,19 +39,29 @@ namespace Minesweeper.Presentation.Presenters
if (view != null)
{
view.StartClicked -= OnStartClicked;
view.SizeChanged -= OnSizeChanged;
gameStateService.StateChanged -= OnStateChanged;
}
}
private void OnStartClicked()
{
settingsService.ApplyAndSaveIfChanged(view.SelectedSettings);
commandDispatcher.Dispatch(new StartGameCommand());
}
private void OnSizeChanged()
{
var selected = settingsService.Clamp(view.SelectedSettings);
var maxMines = settingsService.GetMaxMines(selected.SizeX, selected.SizeY);
view.ConfigureMines(1, maxMines, selected.MinesCount);
}
private void OnStateChanged(GameState state)
{
if (state == GameState.FieldSelection)
{
RefreshMenuValues(settingsService.Current);
view.Show();
}
else
@@ -52,5 +69,13 @@ namespace Minesweeper.Presentation.Presenters
view.Hide();
}
}
private void RefreshMenuValues(GameSettingsValue settings)
{
var clamped = settingsService.Clamp(settings);
view.ConfigureSizeX(config.MinSizeX, config.MaxSizeX, clamped.SizeX);
view.ConfigureSizeY(config.MinSizeY, config.MaxSizeY, clamped.SizeY);
view.ConfigureMines(1, settingsService.GetMaxMines(clamped.SizeX, clamped.SizeY), clamped.MinesCount);
}
}
}
@@ -1,26 +1,25 @@
using System.Collections.Generic;
using Minesweeper.Config;
using Minesweeper.Core;
namespace Minesweeper.Presentation.ReadModels
{
public sealed class GameReadModel : IGameReadModel
{
private readonly MinesweeperGameConfig config;
private readonly IBoardService boardService;
private readonly IGameSettingsService settingsService;
private readonly IGameStateService gameStateService;
public GameReadModel(MinesweeperGameConfig config, IBoardService boardService, IGameStateService gameStateService)
public GameReadModel(IBoardService boardService, IGameSettingsService settingsService, IGameStateService gameStateService)
{
this.config = config;
this.boardService = boardService;
this.settingsService = settingsService;
this.gameStateService = gameStateService;
}
public GameState State => gameStateService.Current;
public int Width => boardService.Width > 0 ? boardService.Width : config.Width;
public int Height => boardService.Height > 0 ? boardService.Height : config.Height;
public int MinesCount => boardService.MinesCount > 0 ? boardService.MinesCount : config.MinesCount;
public int Width => boardService.Width > 0 ? boardService.Width : settingsService.SizeX;
public int Height => boardService.Height > 0 ? boardService.Height : settingsService.SizeY;
public int MinesCount => boardService.MinesCount > 0 ? boardService.MinesCount : settingsService.MinesCount;
public int FlaggedCellsCount => CountFlaggedCells();
public int RemainingMinesCount => MinesCount - FlaggedCellsCount;
+20 -2
View File
@@ -12,9 +12,11 @@ namespace Minesweeper.Presentation.Views
{
private const string ContentImagePath = "Content/Image";
private const string ContentLabelPath = "Content/Text (TMP)";
private const string ContentPath = "Content";
[SerializeField] private Button button;
[SerializeField] private Image backgroundImage;
[SerializeField] private RectTransform contentRoot;
[SerializeField] private Image contentImage;
[SerializeField] private TMP_Text label;
@@ -28,10 +30,11 @@ namespace Minesweeper.Presentation.Views
public event Action PressStarted;
public event Action PressEnded;
public void Bind(Button button, Image backgroundImage, Image contentImage, TMP_Text label)
public void Bind(Button button, Image backgroundImage, RectTransform contentRoot, Image contentImage, TMP_Text label)
{
this.button = button;
this.backgroundImage = backgroundImage;
this.contentRoot = contentRoot;
this.contentImage = contentImage;
this.label = label;
}
@@ -48,6 +51,15 @@ namespace Minesweeper.Presentation.Views
backgroundImage = GetComponent<Image>();
}
if (contentRoot == null)
{
var contentTransform = transform.Find(ContentPath);
if (contentTransform != null)
{
contentRoot = contentTransform.GetComponent<RectTransform>();
}
}
if (contentImage == null)
{
var contentImageTransform = transform.Find(ContentImagePath);
@@ -82,12 +94,18 @@ namespace Minesweeper.Presentation.Views
}
}
public void Render(BoardCellData cell, MinesweeperUiConfig config, float pixelsPerUnitMultiplier, bool revealUnflaggedMines)
public void Render(BoardCellData cell, MinesweeperUiConfig config, float pixelsPerUnitMultiplier, float contentPadding, bool revealUnflaggedMines)
{
gameObject.name = $"bt_{cell.X}_{cell.Y}_{cell.DisplayValue}";
isOpened = cell.IsOpened;
var revealMine = revealUnflaggedMines && cell.IsMine && !cell.IsFlagged;
if (contentRoot != null)
{
contentRoot.offsetMin = new Vector2(contentPadding, contentPadding);
contentRoot.offsetMax = new Vector2(-contentPadding, -contentPadding);
}
if (backgroundImage != null)
{
backgroundImage.pixelsPerUnitMultiplier = pixelsPerUnitMultiplier;
+13 -2
View File
@@ -13,6 +13,10 @@ namespace Minesweeper.Presentation.Views
{
private const float ResizeRefreshDelaySeconds = 0.5f;
private const float ResizeSizeEpsilon = 0.5f;
private const float MaximumCellPixelsPerUnitMultiplier = 1f;
private const float ContentPaddingReferenceCellSize = 202f;
private const float ContentPaddingReferencePadding = 15f;
private const float MinimumContentPadding = 1f;
[SerializeField] private GameObject gameRoot;
[SerializeField] private GameObject pauseRoot;
@@ -37,6 +41,7 @@ namespace Minesweeper.Presentation.Views
private bool resizeRefreshPending;
private int currentBoardWidth;
private int currentBoardHeight;
private float currentContentPadding = MinimumContentPadding;
private float currentPixelsPerUnitMultiplier = 1f;
private float resizeStableAt;
private Vector2 lastObservedLayoutSize;
@@ -195,7 +200,7 @@ namespace Minesweeper.Presentation.Views
var cell = cells[i];
if (cellsByCoordinate.TryGetValue(ToKey(cell.X, cell.Y), out var view))
{
view.Render(cell, uiConfig, currentPixelsPerUnitMultiplier, revealUnflaggedMines);
view.Render(cell, uiConfig, currentPixelsPerUnitMultiplier, currentContentPadding, revealUnflaggedMines);
}
}
}
@@ -348,10 +353,16 @@ namespace Minesweeper.Presentation.Views
gridLayoutGroup.padding = new RectOffset();
gridLayoutGroup.spacing = new Vector2(spacing, spacing);
gridLayoutGroup.cellSize = new Vector2(cellSize, cellSize);
currentPixelsPerUnitMultiplier = uiConfig.ReferenceCellSize / cellSize;
currentPixelsPerUnitMultiplier = Mathf.Min(MaximumCellPixelsPerUnitMultiplier, uiConfig.ReferenceCellSize / cellSize);
currentContentPadding = CalculateContentPadding(cellSize);
lastObservedLayoutSize = layoutSize;
}
private static float CalculateContentPadding(float cellSize)
{
return Mathf.Max(MinimumContentPadding, cellSize * ContentPaddingReferencePadding / ContentPaddingReferenceCellSize);
}
private Vector2 GetLayoutSourceSize()
{
var parentRect = boardPanel.parent as RectTransform;
@@ -1,12 +1,19 @@
using System;
using Minesweeper.Core;
namespace Minesweeper.Presentation.Views
{
public interface IMainMenuView : IView
{
event Action StartClicked;
event Action SizeChanged;
GameSettingsValue SelectedSettings { get; }
void Show();
void Hide();
void ConfigureSizeX(int min, int max, int value);
void ConfigureSizeY(int min, int max, int value);
void ConfigureMines(int min, int max, int value);
}
}
@@ -1,4 +1,5 @@
using System;
using Minesweeper.Core;
using UnityEngine;
using UnityEngine.UI;
@@ -8,8 +9,14 @@ namespace Minesweeper.Presentation.Views
{
[SerializeField] private GameObject root;
[SerializeField] private Button startButton;
[SerializeField] private MenuSliderView sizeXSlider = new MenuSliderView();
[SerializeField] private MenuSliderView sizeYSlider = new MenuSliderView();
[SerializeField] private MenuSliderView minesSlider = new MenuSliderView();
public event Action StartClicked;
public event Action SizeChanged;
public GameSettingsValue SelectedSettings => new GameSettingsValue(sizeXSlider.Value, sizeYSlider.Value, minesSlider.Value);
private void Awake()
{
@@ -17,8 +24,6 @@ namespace Minesweeper.Presentation.Views
{
root = gameObject;
}
AutoBind();
}
private void OnEnable()
@@ -27,6 +32,12 @@ namespace Minesweeper.Presentation.Views
{
startButton.onClick.AddListener(OnStartClicked);
}
sizeXSlider.ValueChanged += OnSizeSliderChanged;
sizeYSlider.ValueChanged += OnSizeSliderChanged;
sizeXSlider.AddListeners();
sizeYSlider.AddListeners();
minesSlider.AddListeners();
}
private void OnDisable()
@@ -35,6 +46,12 @@ namespace Minesweeper.Presentation.Views
{
startButton.onClick.RemoveListener(OnStartClicked);
}
sizeXSlider.RemoveListeners();
sizeYSlider.RemoveListeners();
minesSlider.RemoveListeners();
sizeXSlider.ValueChanged -= OnSizeSliderChanged;
sizeYSlider.ValueChanged -= OnSizeSliderChanged;
}
public void Show()
@@ -47,27 +64,34 @@ namespace Minesweeper.Presentation.Views
root.SetActive(false);
}
public void ConfigureSizeX(int min, int max, int value)
{
sizeXSlider.Configure(min, max, value, "Size X");
}
public void ConfigureSizeY(int min, int max, int value)
{
sizeYSlider.Configure(min, max, value, "Size Y");
}
public void ConfigureMines(int min, int max, int value)
{
minesSlider.Configure(min, max, value, "Mines Count");
}
private void OnStartClicked()
{
StartClicked?.Invoke();
}
public void Bind(GameObject root, Button startButton)
public void BindRoot(GameObject root)
{
this.root = root != null ? root : gameObject;
this.startButton = startButton;
}
private void AutoBind()
private void OnSizeSliderChanged(int value)
{
if (startButton == null)
{
var startButtonTransform = transform.Find("StartButton");
if (startButtonTransform != null)
{
startButton = startButtonTransform.GetComponent<Button>();
}
}
SizeChanged?.Invoke();
}
}
}
@@ -0,0 +1,89 @@
using System;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace Minesweeper.Presentation.Views
{
[Serializable]
public sealed class MenuSliderView
{
[SerializeField] private Slider slider;
[SerializeField] private TMP_Text minText;
[SerializeField] private TMP_Text maxText;
[SerializeField] private TMP_Text valueText;
[SerializeField] private string valueLabel;
public event Action<int> ValueChanged;
public int Value => slider != null ? Mathf.RoundToInt(slider.value) : 0;
public void Bind(Slider slider, TMP_Text minText, TMP_Text maxText, TMP_Text valueText)
{
RemoveListeners();
this.slider = slider;
this.minText = minText;
this.maxText = maxText;
this.valueText = valueText;
AddListeners();
}
public void AddListeners()
{
if (slider != null)
{
slider.onValueChanged.AddListener(OnValueChanged);
}
}
public void RemoveListeners()
{
if (slider != null)
{
slider.onValueChanged.RemoveListener(OnValueChanged);
}
}
public void Configure(int min, int max, int value, string label)
{
if (slider == null)
{
return;
}
valueLabel = label;
var clampedMax = Mathf.Max(min, max);
var clampedValue = Mathf.Clamp(value, min, clampedMax);
slider.wholeNumbers = true;
slider.minValue = min;
slider.maxValue = clampedMax;
slider.SetValueWithoutNotify(clampedValue);
SetText(minText, min);
SetText(maxText, clampedMax);
SetValueText(clampedValue);
}
private void OnValueChanged(float value)
{
var intValue = Mathf.RoundToInt(value);
SetValueText(intValue);
ValueChanged?.Invoke(intValue);
}
private void SetValueText(int value)
{
if (valueText != null)
{
valueText.text = string.IsNullOrEmpty(valueLabel) ? value.ToString() : $"{valueLabel}: {value}";
}
}
private static void SetText(TMP_Text text, int value)
{
if (text != null)
{
text.text = value.ToString();
}
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 15be53f58e067f944a33854111083046
@@ -1,4 +1,5 @@
using System;
using Minesweeper.Core;
namespace Minesweeper.Presentation.Views
{
@@ -10,6 +11,14 @@ namespace Minesweeper.Presentation.Views
remove { }
}
public event Action SizeChanged
{
add { }
remove { }
}
public GameSettingsValue SelectedSettings => default;
public void Show()
{
}
@@ -17,5 +26,17 @@ namespace Minesweeper.Presentation.Views
public void Hide()
{
}
public void ConfigureSizeX(int min, int max, int value)
{
}
public void ConfigureSizeY(int min, int max, int value)
{
}
public void ConfigureMines(int min, int max, int value)
{
}
}
}