[Add] Resize board

This commit is contained in:
2026-06-06 22:58:20 +07:00
parent bb2463d970
commit 1a6f403d72
7 changed files with 114 additions and 24 deletions
@@ -176,7 +176,7 @@ namespace Minesweeper.Presentation.Presenters
{
var cells = readModel.GetCells();
view.SetMineCount(readModel.MinesCount);
view.RebuildBoard(cells, readModel.Width, readModel.Height, cellViewFactory);
view.RebuildBoard(cells, readModel.Width, readModel.Height, cellViewFactory, IsFinalState());
boardBuilt = true;
topPanelPresenter.RefreshCounters();
UpdateBoardInput();
@@ -184,7 +184,13 @@ namespace Minesweeper.Presentation.Presenters
private void RefreshBoard()
{
view.RefreshBoard(readModel.GetCells());
view.RefreshBoard(readModel.GetCells(), IsFinalState());
}
private bool IsFinalState()
{
var state = gameStateService.Current;
return state == GameState.Lost || state == GameState.Won;
}
private void UpdateBoardInput()
+10 -8
View File
@@ -82,15 +82,16 @@ namespace Minesweeper.Presentation.Views
}
}
public void Render(BoardCellData cell, MinesweeperUiConfig config, float pixelsPerUnitMultiplier)
public void Render(BoardCellData cell, MinesweeperUiConfig config, float pixelsPerUnitMultiplier, bool revealUnflaggedMines)
{
gameObject.name = $"bt_{cell.X}_{cell.Y}_{cell.DisplayValue}";
isOpened = cell.IsOpened;
var revealMine = revealUnflaggedMines && cell.IsMine && !cell.IsFlagged;
if (backgroundImage != null)
{
backgroundImage.pixelsPerUnitMultiplier = pixelsPerUnitMultiplier;
backgroundImage.color = cell.IsOpened ? config.OpenedCellColor : config.ClosedCellColor;
backgroundImage.color = cell.IsOpened || revealMine ? config.OpenedCellColor : config.ClosedCellColor;
}
if (contentImage != null)
@@ -98,7 +99,7 @@ namespace Minesweeper.Presentation.Views
contentImage.pixelsPerUnitMultiplier = pixelsPerUnitMultiplier;
}
RenderContent(cell, config);
RenderContent(cell, config, revealMine);
if (button != null)
{
@@ -106,15 +107,16 @@ namespace Minesweeper.Presentation.Views
}
}
private void RenderContent(BoardCellData cell, MinesweeperUiConfig config)
private void RenderContent(BoardCellData cell, MinesweeperUiConfig config, bool revealMine)
{
var showFlag = cell.IsFlagged && !cell.IsOpened;
var showMine = cell.IsOpened && cell.IsMine;
var showMine = (cell.IsOpened && cell.IsMine) || revealMine;
var showMineAsText = showMine && config.MineSprite == null;
var showNumber = cell.IsOpened && !cell.IsMine && cell.NeighborMines > 0;
if (contentImage != null)
{
contentImage.gameObject.SetActive(showFlag || showMine);
contentImage.gameObject.SetActive(showFlag || (showMine && !showMineAsText));
if (showFlag)
{
contentImage.sprite = config.FlagSprite;
@@ -129,8 +131,8 @@ namespace Minesweeper.Presentation.Views
if (label != null)
{
label.gameObject.SetActive(showNumber);
label.text = showNumber ? cell.NeighborMines.ToString() : string.Empty;
label.gameObject.SetActive(showNumber || showMineAsText);
label.text = showMineAsText ? "M" : showNumber ? cell.NeighborMines.ToString() : string.Empty;
label.color = config.GetNumberTextColor(cell.NeighborMines);
}
}
+90 -8
View File
@@ -11,6 +11,9 @@ namespace Minesweeper.Presentation.Views
{
public sealed class GameView : MonoBehaviour, IGameView
{
private const float ResizeRefreshDelaySeconds = 0.5f;
private const float ResizeSizeEpsilon = 0.5f;
[SerializeField] private GameObject gameRoot;
[SerializeField] private GameObject pauseRoot;
[SerializeField] private GameObject resultRoot;
@@ -28,8 +31,15 @@ namespace Minesweeper.Presentation.Views
[SerializeField] private MinesweeperUiConfig uiConfig;
private readonly Dictionary<int, CellView> cellsByCoordinate = new Dictionary<int, CellView>();
private IReadOnlyList<BoardCellData> currentCells;
private bool boardInputEnabled = true;
private bool currentRevealUnflaggedMines;
private bool resizeRefreshPending;
private int currentBoardWidth;
private int currentBoardHeight;
private float currentPixelsPerUnitMultiplier = 1f;
private float resizeStableAt;
private Vector2 lastObservedLayoutSize;
public event Action RestartRequested;
public event Action GoToMenuRequested;
@@ -58,6 +68,11 @@ namespace Minesweeper.Presentation.Views
RemoveButtonListeners();
}
private void Update()
{
TrackBoardResize();
}
public void ShowGame()
{
gameRoot.SetActive(true);
@@ -128,9 +143,13 @@ namespace Minesweeper.Presentation.Views
uiConfig = config;
}
public void RebuildBoard(IReadOnlyList<BoardCellData> cells, int width, int height, ICellViewFactory cellViewFactory)
public void RebuildBoard(IReadOnlyList<BoardCellData> cells, int width, int height, ICellViewFactory cellViewFactory, bool revealUnflaggedMines)
{
ClearBoard();
currentBoardWidth = width;
currentBoardHeight = height;
currentCells = cells;
currentRevealUnflaggedMines = revealUnflaggedMines;
ConfigureGrid(width, height);
for (var i = 0; i < cells.Count; i++)
@@ -138,17 +157,20 @@ namespace Minesweeper.Presentation.Views
CreateCell(cells[i], cellViewFactory);
}
RefreshBoard(cells);
RefreshBoard(cells, revealUnflaggedMines);
}
public void RefreshBoard(IReadOnlyList<BoardCellData> cells)
public void RefreshBoard(IReadOnlyList<BoardCellData> cells, bool revealUnflaggedMines)
{
currentCells = cells;
currentRevealUnflaggedMines = revealUnflaggedMines;
for (var i = 0; i < cells.Count; i++)
{
var cell = cells[i];
if (cellsByCoordinate.TryGetValue(ToKey(cell.X, cell.Y), out var view))
{
view.Render(cell, uiConfig, currentPixelsPerUnitMultiplier);
view.Render(cell, uiConfig, currentPixelsPerUnitMultiplier, revealUnflaggedMines);
}
}
}
@@ -234,6 +256,45 @@ namespace Minesweeper.Presentation.Views
{
boardPanel.gameObject.SetActive(active);
}
if (active)
{
ResetResizeTracking();
}
}
private void TrackBoardResize()
{
if (boardPanel == null || !boardPanel.gameObject.activeInHierarchy || currentCells == null || currentBoardWidth <= 0 || currentBoardHeight <= 0)
{
return;
}
var layoutSize = GetLayoutSourceSize();
if (layoutSize.x <= 0f || layoutSize.y <= 0f)
{
return;
}
if (HasSizeChanged(layoutSize, lastObservedLayoutSize))
{
lastObservedLayoutSize = layoutSize;
resizeStableAt = Time.unscaledTime + ResizeRefreshDelaySeconds;
resizeRefreshPending = true;
return;
}
if (resizeRefreshPending && Time.unscaledTime >= resizeStableAt)
{
resizeRefreshPending = false;
RefreshBoardLayout();
}
}
private void RefreshBoardLayout()
{
ConfigureGrid(currentBoardWidth, currentBoardHeight);
RefreshBoard(currentCells, currentRevealUnflaggedMines);
}
private void ConfigureGrid(int width, int height)
@@ -245,10 +306,9 @@ namespace Minesweeper.Presentation.Views
Canvas.ForceUpdateCanvases();
var parentRect = boardPanel.parent as RectTransform;
var rect = parentRect != null ? parentRect.rect : boardPanel.rect;
var panelWidth = rect.width > 0f ? rect.width : uiConfig.ReferenceCellSize * width;
var panelHeight = rect.height > 0f ? rect.height : uiConfig.ReferenceCellSize * height;
var layoutSize = GetLayoutSourceSize();
var panelWidth = layoutSize.x > 0f ? layoutSize.x : uiConfig.ReferenceCellSize * width;
var panelHeight = layoutSize.y > 0f ? layoutSize.y : uiConfig.ReferenceCellSize * height;
var cellSize = CalculateCellSize(panelWidth, panelHeight, width, height);
var padding = cellSize * uiConfig.BoardPaddingRatio;
var spacing = cellSize * uiConfig.GridSpacingRatio;
@@ -264,6 +324,28 @@ namespace Minesweeper.Presentation.Views
gridLayoutGroup.spacing = new Vector2(spacing, spacing);
gridLayoutGroup.cellSize = new Vector2(cellSize, cellSize);
currentPixelsPerUnitMultiplier = uiConfig.ReferenceCellSize / cellSize;
lastObservedLayoutSize = layoutSize;
}
private Vector2 GetLayoutSourceSize()
{
var parentRect = boardPanel.parent as RectTransform;
var rect = parentRect != null ? parentRect.rect : boardPanel.rect;
return rect.size;
}
private void ResetResizeTracking()
{
resizeRefreshPending = false;
if (boardPanel != null)
{
lastObservedLayoutSize = GetLayoutSourceSize();
}
}
private static bool HasSizeChanged(Vector2 current, Vector2 previous)
{
return Mathf.Abs(current.x - previous.x) > ResizeSizeEpsilon || Mathf.Abs(current.y - previous.y) > ResizeSizeEpsilon;
}
private float CalculateCellSize(float panelWidth, float panelHeight, int width, int height)
@@ -24,8 +24,8 @@ namespace Minesweeper.Presentation.Views
void HideResult();
void SetMineCount(int minesCount);
void SetTimer(float seconds);
void RebuildBoard(IReadOnlyList<BoardCellData> cells, int width, int height, ICellViewFactory cellViewFactory);
void RefreshBoard(IReadOnlyList<BoardCellData> cells);
void RebuildBoard(IReadOnlyList<BoardCellData> cells, int width, int height, ICellViewFactory cellViewFactory, bool revealUnflaggedMines);
void RefreshBoard(IReadOnlyList<BoardCellData> cells, bool revealUnflaggedMines);
void SetBoardInputEnabled(bool enabled);
}
}
@@ -87,11 +87,11 @@ namespace Minesweeper.Presentation.Views
{
}
public void RebuildBoard(IReadOnlyList<BoardCellData> cells, int width, int height, ICellViewFactory cellViewFactory)
public void RebuildBoard(IReadOnlyList<BoardCellData> cells, int width, int height, ICellViewFactory cellViewFactory, bool revealUnflaggedMines)
{
}
public void RefreshBoard(IReadOnlyList<BoardCellData> cells)
public void RefreshBoard(IReadOnlyList<BoardCellData> cells, bool revealUnflaggedMines)
{
}