[Add] Resize board
This commit is contained in:
@@ -14,5 +14,5 @@ MonoBehaviour:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::Minesweeper.Config.MinesweeperGameConfig
|
||||
<Width>k__BackingField: 9
|
||||
<Height>k__BackingField: 9
|
||||
<MinesCount>k__BackingField: 10
|
||||
<MinesCount>k__BackingField: 70
|
||||
<RestartKey>k__BackingField: 114
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -2820,8 +2820,8 @@ RectTransform:
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 1088026588}
|
||||
- {fileID: 628970845}
|
||||
- {fileID: 1183237467}
|
||||
- {fileID: 628970845}
|
||||
- {fileID: 1089412747}
|
||||
m_Father: {fileID: 289057769}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
|
||||
Reference in New Issue
Block a user