[Add] Resize board
This commit is contained in:
@@ -14,5 +14,5 @@ MonoBehaviour:
|
|||||||
m_EditorClassIdentifier: Assembly-CSharp::Minesweeper.Config.MinesweeperGameConfig
|
m_EditorClassIdentifier: Assembly-CSharp::Minesweeper.Config.MinesweeperGameConfig
|
||||||
<Width>k__BackingField: 9
|
<Width>k__BackingField: 9
|
||||||
<Height>k__BackingField: 9
|
<Height>k__BackingField: 9
|
||||||
<MinesCount>k__BackingField: 10
|
<MinesCount>k__BackingField: 70
|
||||||
<RestartKey>k__BackingField: 114
|
<RestartKey>k__BackingField: 114
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ namespace Minesweeper.Presentation.Presenters
|
|||||||
{
|
{
|
||||||
var cells = readModel.GetCells();
|
var cells = readModel.GetCells();
|
||||||
view.SetMineCount(readModel.MinesCount);
|
view.SetMineCount(readModel.MinesCount);
|
||||||
view.RebuildBoard(cells, readModel.Width, readModel.Height, cellViewFactory);
|
view.RebuildBoard(cells, readModel.Width, readModel.Height, cellViewFactory, IsFinalState());
|
||||||
boardBuilt = true;
|
boardBuilt = true;
|
||||||
topPanelPresenter.RefreshCounters();
|
topPanelPresenter.RefreshCounters();
|
||||||
UpdateBoardInput();
|
UpdateBoardInput();
|
||||||
@@ -184,7 +184,13 @@ namespace Minesweeper.Presentation.Presenters
|
|||||||
|
|
||||||
private void RefreshBoard()
|
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()
|
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}";
|
gameObject.name = $"bt_{cell.X}_{cell.Y}_{cell.DisplayValue}";
|
||||||
isOpened = cell.IsOpened;
|
isOpened = cell.IsOpened;
|
||||||
|
var revealMine = revealUnflaggedMines && cell.IsMine && !cell.IsFlagged;
|
||||||
|
|
||||||
if (backgroundImage != null)
|
if (backgroundImage != null)
|
||||||
{
|
{
|
||||||
backgroundImage.pixelsPerUnitMultiplier = pixelsPerUnitMultiplier;
|
backgroundImage.pixelsPerUnitMultiplier = pixelsPerUnitMultiplier;
|
||||||
backgroundImage.color = cell.IsOpened ? config.OpenedCellColor : config.ClosedCellColor;
|
backgroundImage.color = cell.IsOpened || revealMine ? config.OpenedCellColor : config.ClosedCellColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (contentImage != null)
|
if (contentImage != null)
|
||||||
@@ -98,7 +99,7 @@ namespace Minesweeper.Presentation.Views
|
|||||||
contentImage.pixelsPerUnitMultiplier = pixelsPerUnitMultiplier;
|
contentImage.pixelsPerUnitMultiplier = pixelsPerUnitMultiplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderContent(cell, config);
|
RenderContent(cell, config, revealMine);
|
||||||
|
|
||||||
if (button != null)
|
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 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;
|
var showNumber = cell.IsOpened && !cell.IsMine && cell.NeighborMines > 0;
|
||||||
|
|
||||||
if (contentImage != null)
|
if (contentImage != null)
|
||||||
{
|
{
|
||||||
contentImage.gameObject.SetActive(showFlag || showMine);
|
contentImage.gameObject.SetActive(showFlag || (showMine && !showMineAsText));
|
||||||
if (showFlag)
|
if (showFlag)
|
||||||
{
|
{
|
||||||
contentImage.sprite = config.FlagSprite;
|
contentImage.sprite = config.FlagSprite;
|
||||||
@@ -129,8 +131,8 @@ namespace Minesweeper.Presentation.Views
|
|||||||
|
|
||||||
if (label != null)
|
if (label != null)
|
||||||
{
|
{
|
||||||
label.gameObject.SetActive(showNumber);
|
label.gameObject.SetActive(showNumber || showMineAsText);
|
||||||
label.text = showNumber ? cell.NeighborMines.ToString() : string.Empty;
|
label.text = showMineAsText ? "M" : showNumber ? cell.NeighborMines.ToString() : string.Empty;
|
||||||
label.color = config.GetNumberTextColor(cell.NeighborMines);
|
label.color = config.GetNumberTextColor(cell.NeighborMines);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ namespace Minesweeper.Presentation.Views
|
|||||||
{
|
{
|
||||||
public sealed class GameView : MonoBehaviour, IGameView
|
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 gameRoot;
|
||||||
[SerializeField] private GameObject pauseRoot;
|
[SerializeField] private GameObject pauseRoot;
|
||||||
[SerializeField] private GameObject resultRoot;
|
[SerializeField] private GameObject resultRoot;
|
||||||
@@ -28,8 +31,15 @@ namespace Minesweeper.Presentation.Views
|
|||||||
[SerializeField] private MinesweeperUiConfig uiConfig;
|
[SerializeField] private MinesweeperUiConfig uiConfig;
|
||||||
|
|
||||||
private readonly Dictionary<int, CellView> cellsByCoordinate = new Dictionary<int, CellView>();
|
private readonly Dictionary<int, CellView> cellsByCoordinate = new Dictionary<int, CellView>();
|
||||||
|
private IReadOnlyList<BoardCellData> currentCells;
|
||||||
private bool boardInputEnabled = true;
|
private bool boardInputEnabled = true;
|
||||||
|
private bool currentRevealUnflaggedMines;
|
||||||
|
private bool resizeRefreshPending;
|
||||||
|
private int currentBoardWidth;
|
||||||
|
private int currentBoardHeight;
|
||||||
private float currentPixelsPerUnitMultiplier = 1f;
|
private float currentPixelsPerUnitMultiplier = 1f;
|
||||||
|
private float resizeStableAt;
|
||||||
|
private Vector2 lastObservedLayoutSize;
|
||||||
|
|
||||||
public event Action RestartRequested;
|
public event Action RestartRequested;
|
||||||
public event Action GoToMenuRequested;
|
public event Action GoToMenuRequested;
|
||||||
@@ -58,6 +68,11 @@ namespace Minesweeper.Presentation.Views
|
|||||||
RemoveButtonListeners();
|
RemoveButtonListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void Update()
|
||||||
|
{
|
||||||
|
TrackBoardResize();
|
||||||
|
}
|
||||||
|
|
||||||
public void ShowGame()
|
public void ShowGame()
|
||||||
{
|
{
|
||||||
gameRoot.SetActive(true);
|
gameRoot.SetActive(true);
|
||||||
@@ -128,9 +143,13 @@ namespace Minesweeper.Presentation.Views
|
|||||||
uiConfig = config;
|
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();
|
ClearBoard();
|
||||||
|
currentBoardWidth = width;
|
||||||
|
currentBoardHeight = height;
|
||||||
|
currentCells = cells;
|
||||||
|
currentRevealUnflaggedMines = revealUnflaggedMines;
|
||||||
ConfigureGrid(width, height);
|
ConfigureGrid(width, height);
|
||||||
|
|
||||||
for (var i = 0; i < cells.Count; i++)
|
for (var i = 0; i < cells.Count; i++)
|
||||||
@@ -138,17 +157,20 @@ namespace Minesweeper.Presentation.Views
|
|||||||
CreateCell(cells[i], cellViewFactory);
|
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++)
|
for (var i = 0; i < cells.Count; i++)
|
||||||
{
|
{
|
||||||
var cell = cells[i];
|
var cell = cells[i];
|
||||||
if (cellsByCoordinate.TryGetValue(ToKey(cell.X, cell.Y), out var view))
|
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);
|
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)
|
private void ConfigureGrid(int width, int height)
|
||||||
@@ -245,10 +306,9 @@ namespace Minesweeper.Presentation.Views
|
|||||||
|
|
||||||
Canvas.ForceUpdateCanvases();
|
Canvas.ForceUpdateCanvases();
|
||||||
|
|
||||||
var parentRect = boardPanel.parent as RectTransform;
|
var layoutSize = GetLayoutSourceSize();
|
||||||
var rect = parentRect != null ? parentRect.rect : boardPanel.rect;
|
var panelWidth = layoutSize.x > 0f ? layoutSize.x : uiConfig.ReferenceCellSize * width;
|
||||||
var panelWidth = rect.width > 0f ? rect.width : uiConfig.ReferenceCellSize * width;
|
var panelHeight = layoutSize.y > 0f ? layoutSize.y : uiConfig.ReferenceCellSize * height;
|
||||||
var panelHeight = rect.height > 0f ? rect.height : uiConfig.ReferenceCellSize * height;
|
|
||||||
var cellSize = CalculateCellSize(panelWidth, panelHeight, width, height);
|
var cellSize = CalculateCellSize(panelWidth, panelHeight, width, height);
|
||||||
var padding = cellSize * uiConfig.BoardPaddingRatio;
|
var padding = cellSize * uiConfig.BoardPaddingRatio;
|
||||||
var spacing = cellSize * uiConfig.GridSpacingRatio;
|
var spacing = cellSize * uiConfig.GridSpacingRatio;
|
||||||
@@ -264,6 +324,28 @@ namespace Minesweeper.Presentation.Views
|
|||||||
gridLayoutGroup.spacing = new Vector2(spacing, spacing);
|
gridLayoutGroup.spacing = new Vector2(spacing, spacing);
|
||||||
gridLayoutGroup.cellSize = new Vector2(cellSize, cellSize);
|
gridLayoutGroup.cellSize = new Vector2(cellSize, cellSize);
|
||||||
currentPixelsPerUnitMultiplier = uiConfig.ReferenceCellSize / 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)
|
private float CalculateCellSize(float panelWidth, float panelHeight, int width, int height)
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ namespace Minesweeper.Presentation.Views
|
|||||||
void HideResult();
|
void HideResult();
|
||||||
void SetMineCount(int minesCount);
|
void SetMineCount(int minesCount);
|
||||||
void SetTimer(float seconds);
|
void SetTimer(float seconds);
|
||||||
void RebuildBoard(IReadOnlyList<BoardCellData> cells, int width, int height, ICellViewFactory cellViewFactory);
|
void RebuildBoard(IReadOnlyList<BoardCellData> cells, int width, int height, ICellViewFactory cellViewFactory, bool revealUnflaggedMines);
|
||||||
void RefreshBoard(IReadOnlyList<BoardCellData> cells);
|
void RefreshBoard(IReadOnlyList<BoardCellData> cells, bool revealUnflaggedMines);
|
||||||
void SetBoardInputEnabled(bool enabled);
|
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_ConstrainProportionsScale: 0
|
||||||
m_Children:
|
m_Children:
|
||||||
- {fileID: 1088026588}
|
- {fileID: 1088026588}
|
||||||
- {fileID: 628970845}
|
|
||||||
- {fileID: 1183237467}
|
- {fileID: 1183237467}
|
||||||
|
- {fileID: 628970845}
|
||||||
- {fileID: 1089412747}
|
- {fileID: 1089412747}
|
||||||
m_Father: {fileID: 289057769}
|
m_Father: {fileID: 289057769}
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
|
|||||||
Reference in New Issue
Block a user