[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_Name: MinesweeperGameConfig
|
||||
m_EditorClassIdentifier: Assembly-CSharp::Minesweeper.Config.MinesweeperGameConfig
|
||||
<MinSizeX>k__BackingField: 2
|
||||
<MaxSizeX>k__BackingField: 50
|
||||
<MinSizeY>k__BackingField: 2
|
||||
<MaxSizeY>k__BackingField: 50
|
||||
<MinSizeX>k__BackingField: 3
|
||||
<MaxSizeX>k__BackingField: 30
|
||||
<MinSizeY>k__BackingField: 3
|
||||
<MaxSizeY>k__BackingField: 30
|
||||
|
||||
@@ -629,13 +629,12 @@ MonoBehaviour:
|
||||
autoInjectGameObjects: []
|
||||
gameConfig: {fileID: 11400000, guid: 4c24a7c2a548eff4fb21fa4a4bf3e741, 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}
|
||||
topPanelView: {fileID: 1101876296}
|
||||
mainMenuViewPrefab: {fileID: 0}
|
||||
boardViewPrefab: {fileID: 0}
|
||||
pauseViewPrefab: {fileID: 0}
|
||||
resultViewPrefab: {fileID: 0}
|
||||
--- !u!4 &1287266282
|
||||
Transform:
|
||||
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 MinesweeperUiConfig uiConfig;
|
||||
[SerializeField] private MinesweeperScreenCatalog screenCatalog = new MinesweeperScreenCatalog();
|
||||
[SerializeField] private Transform contentRoot;
|
||||
[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)
|
||||
{
|
||||
@@ -124,12 +127,12 @@ namespace Minesweeper.Infrastructure
|
||||
|
||||
private MinesweeperScreenRefs SpawnScreens()
|
||||
{
|
||||
if (contentRoot == null || screenCatalog == null || !screenCatalog.IsValid)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
return new MinesweeperScreenBootstrapper().Spawn(contentRoot, screenCatalog);
|
||||
return new MinesweeperScreenBootstrapper().Spawn(
|
||||
contentRoot,
|
||||
mainMenuViewPrefab,
|
||||
boardViewPrefab,
|
||||
pauseViewPrefab,
|
||||
resultViewPrefab);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using Minesweeper.Config;
|
||||
using Minesweeper.Presentation.Views;
|
||||
using UnityEngine;
|
||||
|
||||
@@ -11,7 +10,12 @@ namespace Minesweeper.Presentation.Factories
|
||||
private const string PausePanelName = "PausePanel";
|
||||
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)
|
||||
{
|
||||
@@ -19,47 +23,42 @@ namespace Minesweeper.Presentation.Factories
|
||||
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;
|
||||
}
|
||||
|
||||
ClearContent(contentRoot);
|
||||
|
||||
var mainMenu = SpawnScreen(catalog.MainMenuPanelPrefab, contentRoot, MainMenuPanelName, 0);
|
||||
var board = SpawnScreen(catalog.BoardGridPrefab, contentRoot, BoardGridName, 1);
|
||||
var pause = SpawnScreen(catalog.PausePanelPrefab, contentRoot, PausePanelName, 2);
|
||||
var result = SpawnScreen(catalog.ResultPanelPrefab, contentRoot, ResultPanelName, 3);
|
||||
var mainMenuView = SpawnScreen(mainMenuPrefab, contentRoot, MainMenuPanelName, 0);
|
||||
var boardView = SpawnScreen(boardPrefab, contentRoot, BoardGridName, 1);
|
||||
var pauseView = SpawnScreen(pausePrefab, contentRoot, PausePanelName, 2);
|
||||
var resultView = SpawnScreen(resultPrefab, contentRoot, ResultPanelName, 3);
|
||||
|
||||
var mainMenuView = RequireComponent<MainMenuView>(mainMenu.transform, MainMenuPanelName);
|
||||
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(
|
||||
mainMenuView,
|
||||
boardView,
|
||||
pauseView,
|
||||
resultView);
|
||||
|
||||
mainMenu.SetActive(false);
|
||||
board.SetActive(false);
|
||||
pause.SetActive(false);
|
||||
result.SetActive(false);
|
||||
mainMenuView.gameObject.SetActive(false);
|
||||
boardView.gameObject.SetActive(false);
|
||||
pauseView.gameObject.SetActive(false);
|
||||
resultView.gameObject.SetActive(false);
|
||||
|
||||
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);
|
||||
instance.name = expectedName;
|
||||
instance.gameObject.name = expectedName;
|
||||
instance.transform.SetSiblingIndex(siblingIndex);
|
||||
Stretch(instance.GetComponent<RectTransform>());
|
||||
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)
|
||||
{
|
||||
if (rectTransform == null)
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace Minesweeper.Presentation.Views
|
||||
[SerializeField] private MinesweeperUiConfig uiConfig;
|
||||
|
||||
private readonly Dictionary<int, CellView> cellsByCoordinate = new Dictionary<int, CellView>();
|
||||
private readonly Stack<CellView> pooledCells = new Stack<CellView>();
|
||||
private IReadOnlyList<BoardCellData> currentCells;
|
||||
private bool inputEnabled = true;
|
||||
private bool currentRevealUnflaggedMines;
|
||||
@@ -83,14 +84,27 @@ namespace Minesweeper.Presentation.Views
|
||||
currentBoardHeight = height;
|
||||
currentCells = cells;
|
||||
currentRevealUnflaggedMines = revealUnflaggedMines;
|
||||
ConfigureGrid(width, height);
|
||||
var layoutWasEnabled = gridLayoutGroup != null && gridLayoutGroup.enabled;
|
||||
SetGridLayoutEnabled(false);
|
||||
|
||||
for (var i = 0; i < cells.Count; i++)
|
||||
try
|
||||
{
|
||||
CreateCell(cells[i], cellViewFactory);
|
||||
}
|
||||
ConfigureGrid(width, height);
|
||||
|
||||
Refresh(cells, revealUnflaggedMines);
|
||||
for (var i = 0; i < cells.Count; i++)
|
||||
{
|
||||
var cell = cells[i];
|
||||
var view = CreateCell(cell, cellViewFactory);
|
||||
if (view != null)
|
||||
{
|
||||
view.Render(cell, uiConfig, currentPixelsPerUnitMultiplier, currentContentPadding, revealUnflaggedMines);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
SetGridLayoutEnabled(layoutWasEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
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)));
|
||||
}
|
||||
|
||||
private void CreateCell(BoardCellData cell, ICellViewFactory cellViewFactory)
|
||||
private CellView CreateCell(BoardCellData cell, ICellViewFactory cellViewFactory)
|
||||
{
|
||||
if (gridLayoutGroup == null)
|
||||
{
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
var view = cellViewFactory.CreateCell(cell, gridLayoutGroup.transform);
|
||||
var view = GetOrCreateCell(cell, cellViewFactory);
|
||||
if (view == null)
|
||||
{
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
view.SetInputEnabled(inputEnabled);
|
||||
@@ -226,6 +240,22 @@ namespace Minesweeper.Presentation.Views
|
||||
view.PressStarted += OnCellPressStarted;
|
||||
view.PressEnded += OnCellPressEnded;
|
||||
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()
|
||||
@@ -238,19 +268,24 @@ namespace Minesweeper.Presentation.Views
|
||||
cell.FlagRequested -= OnCellFlagRequested;
|
||||
cell.PressStarted -= OnCellPressStarted;
|
||||
cell.PressEnded -= OnCellPressEnded;
|
||||
PoolCell(cell);
|
||||
}
|
||||
}
|
||||
|
||||
cellsByCoordinate.Clear();
|
||||
}
|
||||
|
||||
if (gridLayoutGroup == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
private void PoolCell(CellView cell)
|
||||
{
|
||||
cell.gameObject.SetActive(false);
|
||||
pooledCells.Push(cell);
|
||||
}
|
||||
|
||||
for (var i = gridLayoutGroup.transform.childCount - 1; i >= 0; i--)
|
||||
private void SetGridLayoutEnabled(bool enabled)
|
||||
{
|
||||
if (gridLayoutGroup != null)
|
||||
{
|
||||
Destroy(gridLayoutGroup.transform.GetChild(i).gameObject);
|
||||
gridLayoutGroup.enabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ namespace Minesweeper.Presentation.Views
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
gameObject.name = $"bt_{x}_{y}";
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
gameObject.name = $"bt_{cell.X}_{cell.Y}_{cell.DisplayValue}";
|
||||
isOpened = cell.IsOpened;
|
||||
var revealMine = revealUnflaggedMines && cell.IsMine && !cell.IsFlagged;
|
||||
|
||||
@@ -90,14 +90,14 @@ namespace Minesweeper.Presentation.Views
|
||||
|
||||
if (contentImage != null)
|
||||
{
|
||||
contentImage.gameObject.SetActive(showFlag || (showMine && !showMineAsText));
|
||||
SetActiveIfChanged(contentImage.gameObject, showFlag || (showMine && !showMineAsText));
|
||||
if (showFlag)
|
||||
{
|
||||
contentImage.sprite = config.FlagSprite;
|
||||
SetSpriteIfChanged(contentImage, config.FlagSprite);
|
||||
}
|
||||
else if (showMine)
|
||||
{
|
||||
contentImage.sprite = config.MineSprite;
|
||||
SetSpriteIfChanged(contentImage, config.MineSprite);
|
||||
}
|
||||
|
||||
contentImage.color = Color.white;
|
||||
@@ -105,12 +105,36 @@ namespace Minesweeper.Presentation.Views
|
||||
|
||||
if (label != null)
|
||||
{
|
||||
label.gameObject.SetActive(showNumber || showMineAsText);
|
||||
label.text = showMineAsText ? "M" : showNumber ? cell.NeighborMines.ToString() : string.Empty;
|
||||
SetActiveIfChanged(label.gameObject, showNumber || showMineAsText);
|
||||
SetTextIfChanged(label, showMineAsText ? "M" : showNumber ? cell.NeighborMines.ToString() : string.Empty);
|
||||
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)
|
||||
{
|
||||
if (!inputEnabled)
|
||||
|
||||
Reference in New Issue
Block a user