[Fix] Board Optimization, Fix Screen Loading
This commit is contained in:
@@ -12,7 +12,7 @@ MonoBehaviour:
|
|||||||
m_Script: {fileID: 11500000, guid: b4e8d5c36f36bb443b640a85df3e7077, type: 3}
|
m_Script: {fileID: 11500000, guid: b4e8d5c36f36bb443b640a85df3e7077, type: 3}
|
||||||
m_Name: MinesweeperGameConfig
|
m_Name: MinesweeperGameConfig
|
||||||
m_EditorClassIdentifier: Assembly-CSharp::Minesweeper.Config.MinesweeperGameConfig
|
m_EditorClassIdentifier: Assembly-CSharp::Minesweeper.Config.MinesweeperGameConfig
|
||||||
<MinSizeX>k__BackingField: 2
|
<MinSizeX>k__BackingField: 3
|
||||||
<MaxSizeX>k__BackingField: 50
|
<MaxSizeX>k__BackingField: 30
|
||||||
<MinSizeY>k__BackingField: 2
|
<MinSizeY>k__BackingField: 3
|
||||||
<MaxSizeY>k__BackingField: 50
|
<MaxSizeY>k__BackingField: 30
|
||||||
|
|||||||
@@ -629,13 +629,12 @@ MonoBehaviour:
|
|||||||
autoInjectGameObjects: []
|
autoInjectGameObjects: []
|
||||||
gameConfig: {fileID: 11400000, guid: 4c24a7c2a548eff4fb21fa4a4bf3e741, type: 2}
|
gameConfig: {fileID: 11400000, guid: 4c24a7c2a548eff4fb21fa4a4bf3e741, type: 2}
|
||||||
uiConfig: {fileID: 11400000, guid: c8b7785713c7c8b49b853f7e5028a4fa, type: 2}
|
uiConfig: {fileID: 11400000, guid: c8b7785713c7c8b49b853f7e5028a4fa, type: 2}
|
||||||
screenCatalog:
|
|
||||||
<MainMenuPanelPrefab>k__BackingField: {fileID: 7682962739562644362, guid: 66407cd7142d6a945b37ca8dc5e7c6b7, type: 3}
|
|
||||||
<BoardGridPrefab>k__BackingField: {fileID: 463985621338212375, guid: 91c5885a4fbe47540abf4bfd814a32d0, type: 3}
|
|
||||||
<PausePanelPrefab>k__BackingField: {fileID: 2814582388565546678, guid: ec358f6fec19e3b469f516bd1ade70cd, type: 3}
|
|
||||||
<ResultPanelPrefab>k__BackingField: {fileID: 6869455415096409219, guid: 73d5a09dc8885e64a8c20a68ea82c5dc, type: 3}
|
|
||||||
contentRoot: {fileID: 1373940536}
|
contentRoot: {fileID: 1373940536}
|
||||||
topPanelView: {fileID: 1101876296}
|
topPanelView: {fileID: 1101876296}
|
||||||
|
mainMenuViewPrefab: {fileID: 0}
|
||||||
|
boardViewPrefab: {fileID: 0}
|
||||||
|
pauseViewPrefab: {fileID: 0}
|
||||||
|
resultViewPrefab: {fileID: 0}
|
||||||
--- !u!4 &1287266282
|
--- !u!4 &1287266282
|
||||||
Transform:
|
Transform:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
using System;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Minesweeper.Config
|
|
||||||
{
|
|
||||||
[Serializable]
|
|
||||||
public sealed class MinesweeperScreenCatalog
|
|
||||||
{
|
|
||||||
[field: SerializeField] public GameObject MainMenuPanelPrefab { get; private set; }
|
|
||||||
[field: SerializeField] public GameObject BoardGridPrefab { get; private set; }
|
|
||||||
[field: SerializeField] public GameObject PausePanelPrefab { get; private set; }
|
|
||||||
[field: SerializeField] public GameObject ResultPanelPrefab { get; private set; }
|
|
||||||
|
|
||||||
public bool IsValid => MainMenuPanelPrefab != null && BoardGridPrefab != null && PausePanelPrefab != null && ResultPanelPrefab != null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 6d0009ea7e70ee548a09cd95d482ec83
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 210d08f5d5948674fa0df51ed6a785b0
|
|
||||||
folderAsset: yes
|
|
||||||
DefaultImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -17,9 +17,12 @@ namespace Minesweeper.Infrastructure
|
|||||||
{
|
{
|
||||||
[SerializeField] private MinesweeperGameConfig gameConfig;
|
[SerializeField] private MinesweeperGameConfig gameConfig;
|
||||||
[SerializeField] private MinesweeperUiConfig uiConfig;
|
[SerializeField] private MinesweeperUiConfig uiConfig;
|
||||||
[SerializeField] private MinesweeperScreenCatalog screenCatalog = new MinesweeperScreenCatalog();
|
|
||||||
[SerializeField] private Transform contentRoot;
|
[SerializeField] private Transform contentRoot;
|
||||||
[SerializeField] private TopPanelView topPanelView;
|
[SerializeField] private TopPanelView topPanelView;
|
||||||
|
[SerializeField] private MainMenuView mainMenuViewPrefab;
|
||||||
|
[SerializeField] private BoardView boardViewPrefab;
|
||||||
|
[SerializeField] private PauseView pauseViewPrefab;
|
||||||
|
[SerializeField] private ResultView resultViewPrefab;
|
||||||
|
|
||||||
protected override void Configure(IContainerBuilder builder)
|
protected override void Configure(IContainerBuilder builder)
|
||||||
{
|
{
|
||||||
@@ -124,12 +127,12 @@ namespace Minesweeper.Infrastructure
|
|||||||
|
|
||||||
private MinesweeperScreenRefs SpawnScreens()
|
private MinesweeperScreenRefs SpawnScreens()
|
||||||
{
|
{
|
||||||
if (contentRoot == null || screenCatalog == null || !screenCatalog.IsValid)
|
return new MinesweeperScreenBootstrapper().Spawn(
|
||||||
{
|
contentRoot,
|
||||||
return default;
|
mainMenuViewPrefab,
|
||||||
}
|
boardViewPrefab,
|
||||||
|
pauseViewPrefab,
|
||||||
return new MinesweeperScreenBootstrapper().Spawn(contentRoot, screenCatalog);
|
resultViewPrefab);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using Minesweeper.Config;
|
|
||||||
using Minesweeper.Presentation.Views;
|
using Minesweeper.Presentation.Views;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
@@ -11,7 +10,12 @@ namespace Minesweeper.Presentation.Factories
|
|||||||
private const string PausePanelName = "PausePanel";
|
private const string PausePanelName = "PausePanel";
|
||||||
private const string ResultPanelName = "ResultPanel";
|
private const string ResultPanelName = "ResultPanel";
|
||||||
|
|
||||||
public MinesweeperScreenRefs Spawn(Transform contentRoot, MinesweeperScreenCatalog catalog)
|
public MinesweeperScreenRefs Spawn(
|
||||||
|
Transform contentRoot,
|
||||||
|
MainMenuView mainMenuPrefab,
|
||||||
|
BoardView boardPrefab,
|
||||||
|
PauseView pausePrefab,
|
||||||
|
ResultView resultPrefab)
|
||||||
{
|
{
|
||||||
if (contentRoot == null)
|
if (contentRoot == null)
|
||||||
{
|
{
|
||||||
@@ -19,47 +23,42 @@ namespace Minesweeper.Presentation.Factories
|
|||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (catalog == null || !catalog.IsValid)
|
if (mainMenuPrefab == null || boardPrefab == null || pausePrefab == null || resultPrefab == null)
|
||||||
{
|
{
|
||||||
Debug.LogError("Minesweeper screen bootstrap failed: screen catalog prefab references are incomplete.");
|
Debug.LogError("Minesweeper screen bootstrap failed: screen prefab references are incomplete.");
|
||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
ClearContent(contentRoot);
|
ClearContent(contentRoot);
|
||||||
|
|
||||||
var mainMenu = SpawnScreen(catalog.MainMenuPanelPrefab, contentRoot, MainMenuPanelName, 0);
|
var mainMenuView = SpawnScreen(mainMenuPrefab, contentRoot, MainMenuPanelName, 0);
|
||||||
var board = SpawnScreen(catalog.BoardGridPrefab, contentRoot, BoardGridName, 1);
|
var boardView = SpawnScreen(boardPrefab, contentRoot, BoardGridName, 1);
|
||||||
var pause = SpawnScreen(catalog.PausePanelPrefab, contentRoot, PausePanelName, 2);
|
var pauseView = SpawnScreen(pausePrefab, contentRoot, PausePanelName, 2);
|
||||||
var result = SpawnScreen(catalog.ResultPanelPrefab, contentRoot, ResultPanelName, 3);
|
var resultView = SpawnScreen(resultPrefab, contentRoot, ResultPanelName, 3);
|
||||||
|
|
||||||
var mainMenuView = RequireComponent<MainMenuView>(mainMenu.transform, MainMenuPanelName);
|
|
||||||
if (mainMenuView != null)
|
if (mainMenuView != null)
|
||||||
{
|
{
|
||||||
mainMenuView.BindRoot(mainMenu);
|
mainMenuView.BindRoot(mainMenuView.gameObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
var boardView = RequireComponent<BoardView>(board.transform, BoardGridName);
|
|
||||||
var pauseView = RequireComponent<PauseView>(pause.transform, PausePanelName);
|
|
||||||
var resultView = RequireComponent<ResultView>(result.transform, ResultPanelName);
|
|
||||||
|
|
||||||
var refs = new MinesweeperScreenRefs(
|
var refs = new MinesweeperScreenRefs(
|
||||||
mainMenuView,
|
mainMenuView,
|
||||||
boardView,
|
boardView,
|
||||||
pauseView,
|
pauseView,
|
||||||
resultView);
|
resultView);
|
||||||
|
|
||||||
mainMenu.SetActive(false);
|
mainMenuView.gameObject.SetActive(false);
|
||||||
board.SetActive(false);
|
boardView.gameObject.SetActive(false);
|
||||||
pause.SetActive(false);
|
pauseView.gameObject.SetActive(false);
|
||||||
result.SetActive(false);
|
resultView.gameObject.SetActive(false);
|
||||||
|
|
||||||
return refs;
|
return refs;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static GameObject SpawnScreen(GameObject prefab, Transform parent, string expectedName, int siblingIndex)
|
private static T SpawnScreen<T>(T prefab, Transform parent, string expectedName, int siblingIndex) where T : Component
|
||||||
{
|
{
|
||||||
var instance = Object.Instantiate(prefab, parent, false);
|
var instance = Object.Instantiate(prefab, parent, false);
|
||||||
instance.name = expectedName;
|
instance.gameObject.name = expectedName;
|
||||||
instance.transform.SetSiblingIndex(siblingIndex);
|
instance.transform.SetSiblingIndex(siblingIndex);
|
||||||
Stretch(instance.GetComponent<RectTransform>());
|
Stretch(instance.GetComponent<RectTransform>());
|
||||||
return instance;
|
return instance;
|
||||||
@@ -75,17 +74,6 @@ namespace Minesweeper.Presentation.Factories
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static T RequireComponent<T>(Transform root, string ownerName) where T : Component
|
|
||||||
{
|
|
||||||
var component = root.GetComponent<T>();
|
|
||||||
if (component == null)
|
|
||||||
{
|
|
||||||
Debug.LogError($"Minesweeper screen bootstrap failed: '{ownerName}' is missing {typeof(T).Name}.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return component;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void Stretch(RectTransform rectTransform)
|
private static void Stretch(RectTransform rectTransform)
|
||||||
{
|
{
|
||||||
if (rectTransform == null)
|
if (rectTransform == null)
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ 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 readonly Stack<CellView> pooledCells = new Stack<CellView>();
|
||||||
private IReadOnlyList<BoardCellData> currentCells;
|
private IReadOnlyList<BoardCellData> currentCells;
|
||||||
private bool inputEnabled = true;
|
private bool inputEnabled = true;
|
||||||
private bool currentRevealUnflaggedMines;
|
private bool currentRevealUnflaggedMines;
|
||||||
@@ -83,14 +84,27 @@ namespace Minesweeper.Presentation.Views
|
|||||||
currentBoardHeight = height;
|
currentBoardHeight = height;
|
||||||
currentCells = cells;
|
currentCells = cells;
|
||||||
currentRevealUnflaggedMines = revealUnflaggedMines;
|
currentRevealUnflaggedMines = revealUnflaggedMines;
|
||||||
|
var layoutWasEnabled = gridLayoutGroup != null && gridLayoutGroup.enabled;
|
||||||
|
SetGridLayoutEnabled(false);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
ConfigureGrid(width, height);
|
ConfigureGrid(width, height);
|
||||||
|
|
||||||
for (var i = 0; i < cells.Count; i++)
|
for (var i = 0; i < cells.Count; i++)
|
||||||
{
|
{
|
||||||
CreateCell(cells[i], cellViewFactory);
|
var cell = cells[i];
|
||||||
|
var view = CreateCell(cell, cellViewFactory);
|
||||||
|
if (view != null)
|
||||||
|
{
|
||||||
|
view.Render(cell, uiConfig, currentPixelsPerUnitMultiplier, currentContentPadding, revealUnflaggedMines);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
SetGridLayoutEnabled(layoutWasEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
Refresh(cells, revealUnflaggedMines);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Refresh(IReadOnlyList<BoardCellData> cells, bool revealUnflaggedMines)
|
public void Refresh(IReadOnlyList<BoardCellData> cells, bool revealUnflaggedMines)
|
||||||
@@ -207,17 +221,17 @@ namespace Minesweeper.Presentation.Views
|
|||||||
return Mathf.Max(uiConfig.MinimumCellSize, Mathf.Floor(Mathf.Min(cellByWidth, cellByHeight)));
|
return Mathf.Max(uiConfig.MinimumCellSize, Mathf.Floor(Mathf.Min(cellByWidth, cellByHeight)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateCell(BoardCellData cell, ICellViewFactory cellViewFactory)
|
private CellView CreateCell(BoardCellData cell, ICellViewFactory cellViewFactory)
|
||||||
{
|
{
|
||||||
if (gridLayoutGroup == null)
|
if (gridLayoutGroup == null)
|
||||||
{
|
{
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var view = cellViewFactory.CreateCell(cell, gridLayoutGroup.transform);
|
var view = GetOrCreateCell(cell, cellViewFactory);
|
||||||
if (view == null)
|
if (view == null)
|
||||||
{
|
{
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
view.SetInputEnabled(inputEnabled);
|
view.SetInputEnabled(inputEnabled);
|
||||||
@@ -226,6 +240,22 @@ namespace Minesweeper.Presentation.Views
|
|||||||
view.PressStarted += OnCellPressStarted;
|
view.PressStarted += OnCellPressStarted;
|
||||||
view.PressEnded += OnCellPressEnded;
|
view.PressEnded += OnCellPressEnded;
|
||||||
cellsByCoordinate[ToKey(cell.X, cell.Y)] = view;
|
cellsByCoordinate[ToKey(cell.X, cell.Y)] = view;
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CellView GetOrCreateCell(BoardCellData cell, ICellViewFactory cellViewFactory)
|
||||||
|
{
|
||||||
|
CellView view;
|
||||||
|
if (pooledCells.Count > 0)
|
||||||
|
{
|
||||||
|
view = pooledCells.Pop();
|
||||||
|
view.transform.SetParent(gridLayoutGroup.transform, false);
|
||||||
|
view.Initialize(cell.X, cell.Y);
|
||||||
|
view.gameObject.SetActive(true);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cellViewFactory.CreateCell(cell, gridLayoutGroup.transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Clear()
|
private void Clear()
|
||||||
@@ -238,19 +268,24 @@ namespace Minesweeper.Presentation.Views
|
|||||||
cell.FlagRequested -= OnCellFlagRequested;
|
cell.FlagRequested -= OnCellFlagRequested;
|
||||||
cell.PressStarted -= OnCellPressStarted;
|
cell.PressStarted -= OnCellPressStarted;
|
||||||
cell.PressEnded -= OnCellPressEnded;
|
cell.PressEnded -= OnCellPressEnded;
|
||||||
|
PoolCell(cell);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cellsByCoordinate.Clear();
|
cellsByCoordinate.Clear();
|
||||||
|
|
||||||
if (gridLayoutGroup == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = gridLayoutGroup.transform.childCount - 1; i >= 0; i--)
|
private void PoolCell(CellView cell)
|
||||||
{
|
{
|
||||||
Destroy(gridLayoutGroup.transform.GetChild(i).gameObject);
|
cell.gameObject.SetActive(false);
|
||||||
|
pooledCells.Push(cell);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetGridLayoutEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
if (gridLayoutGroup != null)
|
||||||
|
{
|
||||||
|
gridLayoutGroup.enabled = enabled;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ namespace Minesweeper.Presentation.Views
|
|||||||
{
|
{
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.y = y;
|
this.y = y;
|
||||||
|
gameObject.name = $"bt_{x}_{y}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetInputEnabled(bool enabled)
|
public void SetInputEnabled(bool enabled)
|
||||||
@@ -52,7 +53,6 @@ namespace Minesweeper.Presentation.Views
|
|||||||
|
|
||||||
public void Render(BoardCellData cell, MinesweeperUiConfig config, float pixelsPerUnitMultiplier, float contentPadding, 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;
|
isOpened = cell.IsOpened;
|
||||||
var revealMine = revealUnflaggedMines && cell.IsMine && !cell.IsFlagged;
|
var revealMine = revealUnflaggedMines && cell.IsMine && !cell.IsFlagged;
|
||||||
|
|
||||||
@@ -90,14 +90,14 @@ namespace Minesweeper.Presentation.Views
|
|||||||
|
|
||||||
if (contentImage != null)
|
if (contentImage != null)
|
||||||
{
|
{
|
||||||
contentImage.gameObject.SetActive(showFlag || (showMine && !showMineAsText));
|
SetActiveIfChanged(contentImage.gameObject, showFlag || (showMine && !showMineAsText));
|
||||||
if (showFlag)
|
if (showFlag)
|
||||||
{
|
{
|
||||||
contentImage.sprite = config.FlagSprite;
|
SetSpriteIfChanged(contentImage, config.FlagSprite);
|
||||||
}
|
}
|
||||||
else if (showMine)
|
else if (showMine)
|
||||||
{
|
{
|
||||||
contentImage.sprite = config.MineSprite;
|
SetSpriteIfChanged(contentImage, config.MineSprite);
|
||||||
}
|
}
|
||||||
|
|
||||||
contentImage.color = Color.white;
|
contentImage.color = Color.white;
|
||||||
@@ -105,12 +105,36 @@ namespace Minesweeper.Presentation.Views
|
|||||||
|
|
||||||
if (label != null)
|
if (label != null)
|
||||||
{
|
{
|
||||||
label.gameObject.SetActive(showNumber || showMineAsText);
|
SetActiveIfChanged(label.gameObject, showNumber || showMineAsText);
|
||||||
label.text = showMineAsText ? "M" : showNumber ? cell.NeighborMines.ToString() : string.Empty;
|
SetTextIfChanged(label, showMineAsText ? "M" : showNumber ? cell.NeighborMines.ToString() : string.Empty);
|
||||||
label.color = config.GetNumberTextColor(cell.NeighborMines);
|
label.color = config.GetNumberTextColor(cell.NeighborMines);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void SetActiveIfChanged(GameObject target, bool active)
|
||||||
|
{
|
||||||
|
if (target.activeSelf != active)
|
||||||
|
{
|
||||||
|
target.SetActive(active);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetSpriteIfChanged(Image image, Sprite sprite)
|
||||||
|
{
|
||||||
|
if (image.sprite != sprite)
|
||||||
|
{
|
||||||
|
image.sprite = sprite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetTextIfChanged(TMP_Text text, string value)
|
||||||
|
{
|
||||||
|
if (text.text != value)
|
||||||
|
{
|
||||||
|
text.text = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void OnPointerClick(PointerEventData eventData)
|
public void OnPointerClick(PointerEventData eventData)
|
||||||
{
|
{
|
||||||
if (!inputEnabled)
|
if (!inputEnabled)
|
||||||
|
|||||||
Reference in New Issue
Block a user