From 49c9a7904dcdb409be7e3e180ca41cafa0f0607e Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Thu, 14 May 2026 01:12:47 +0200 Subject: [PATCH] refactor: TileRegistry replaces parallel tile/view collections --- Assets/Scripts/Core/Domain/GameSession.cs | 25 ++++------ .../Unity/FloorVisibilityManager.cs | 46 +++-------------- .../Infrastructure/Unity/GameBootstrap.cs | 40 +++++++-------- .../Infrastructure/Unity/LevelGenerator.cs | 21 ++++---- .../Infrastructure/Unity/TileRegistry.cs | 40 +++++++++++++++ Assets/Tests/EditMode/TileRegistryTests.cs | 49 +++++++++++++++++++ 6 files changed, 136 insertions(+), 85 deletions(-) create mode 100644 Assets/Scripts/Infrastructure/Unity/TileRegistry.cs create mode 100644 Assets/Tests/EditMode/TileRegistryTests.cs diff --git a/Assets/Scripts/Core/Domain/GameSession.cs b/Assets/Scripts/Core/Domain/GameSession.cs index 7e5f68f..2d75118 100644 --- a/Assets/Scripts/Core/Domain/GameSession.cs +++ b/Assets/Scripts/Core/Domain/GameSession.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using Core.Ports; using UnityEngine; using Random = System.Random; @@ -25,7 +26,7 @@ namespace Core.Domain public event Action OnSpawnNpc; public event Action OnSpawnPowerUp; - private readonly List _tiles; + private readonly IReadOnlyList _tiles; private readonly IPersistenceService _persistenceService; private readonly Random _rng = new(); private int _playerFloorIndex = 0; @@ -40,7 +41,7 @@ namespace Core.Domain public int ComboMultiplier { get; private set; } = 1; public event Action OnComboUpdated; - public GameSession(List tiles, IPersistenceService persistenceService) + public GameSession(IReadOnlyList tiles, IPersistenceService persistenceService) { _tiles = tiles; _persistenceService = persistenceService; @@ -124,15 +125,12 @@ namespace Core.Domain private void SpawnNextOrb() { - var validTiles = _tiles.FindAll(t => + var validTiles = _tiles.Where(t => t.CurrentState == TileState.Stable && - t.Floor == _playerFloorIndex - ); + t.Floor == _playerFloorIndex).ToList(); if (validTiles.Count == 0) - { - validTiles = _tiles.FindAll(t => t.CurrentState == TileState.Stable); - } + validTiles = _tiles.Where(t => t.CurrentState == TileState.Stable).ToList(); if (validTiles.Count == 0) { @@ -173,15 +171,12 @@ namespace Core.Domain private void SpawnRandomPowerUp() { - var validTiles = _tiles.FindAll(t => - t.CurrentState == TileState.Stable && - t.Floor == _playerFloorIndex - ); + var validTiles = _tiles.Where(t => + t.CurrentState == TileState.Stable && + t.Floor == _playerFloorIndex).ToList(); if (validTiles.Count == 0) - { - validTiles = _tiles.FindAll(t => t.CurrentState == TileState.Stable); - } + validTiles = _tiles.Where(t => t.CurrentState == TileState.Stable).ToList(); if (validTiles.Count == 0) return; diff --git a/Assets/Scripts/Infrastructure/Unity/FloorVisibilityManager.cs b/Assets/Scripts/Infrastructure/Unity/FloorVisibilityManager.cs index ca3e13b..4f02e24 100644 --- a/Assets/Scripts/Infrastructure/Unity/FloorVisibilityManager.cs +++ b/Assets/Scripts/Infrastructure/Unity/FloorVisibilityManager.cs @@ -1,6 +1,5 @@ using System.Collections; using System.Collections.Generic; -using Core.Domain; using UnityEngine; namespace Infrastructure.Unity @@ -10,44 +9,13 @@ namespace Infrastructure.Unity [SerializeField] private float fadeSpeed = 5f; [SerializeField] private float hiddenAlpha = 0.1f; [SerializeField] private float visibleAlpha = 1.0f; - - private GameSession _gameSession; + private List> _floors; private int _currentFloorIndex = -1; - - public void Initialize(GameSession gameSession, List allTiles, Dictionary tileViews, int totalFloors) + + public void Initialize(TileRegistry registry, int totalFloors) { - _gameSession = gameSession; - - _floors = new List>(); - for (var i = 0; i < totalFloors; i++) - { - _floors.Add(new List()); - } - - foreach (var tile in allTiles) - { - if (tileViews.TryGetValue(tile.Id, out var view)) - { - // Safety check for array bounds - if (tile.Floor < _floors.Count) - { - _floors[tile.Floor].Add(view); - } - } - } - } - - private void Update() - { - if (_gameSession == null) return; - - // Check if player changed floors - // We read the private field _playerFloorIndex via a public getter we need to add, - // OR we just track it locally if you updated GameSession to expose it. - // Assuming GameSession doesn't expose it publically yet, let's rely on GameBootstrap passing it or just hack it: - // Ideally, GameSession should emit an event 'OnFloorChanged'. - // For now, let's assume we can get it or we passed the player reference. + _floors = registry.GroupViewsByFloor(totalFloors); } // Call this from GameBootstrap.Update() @@ -75,9 +43,9 @@ namespace Infrastructure.Unity tile.SetAlpha(targetAlpha); } } - - if (i % 2 == 0) yield return null; + + if (i % 2 == 0) yield return null; } } } -} \ No newline at end of file +} diff --git a/Assets/Scripts/Infrastructure/Unity/GameBootstrap.cs b/Assets/Scripts/Infrastructure/Unity/GameBootstrap.cs index bfd92f1..4b43341 100644 --- a/Assets/Scripts/Infrastructure/Unity/GameBootstrap.cs +++ b/Assets/Scripts/Infrastructure/Unity/GameBootstrap.cs @@ -1,5 +1,4 @@ using System.Collections; -using System.Collections.Generic; using Core.Domain; using Core.Ports; using TMPro; @@ -36,8 +35,7 @@ namespace Infrastructure.Unity [Header("Power Ups")] [SerializeField] private PowerUpViewAdapter powerUpPrefab; - private readonly List _allTiles = new(); - private readonly Dictionary _tileViews = new(); + private readonly TileRegistry _tileRegistry = new(); private GameSession _gameSession; private IPersistenceService _persistenceService; private InputSystem_Actions _actions; @@ -67,7 +65,7 @@ namespace Infrastructure.Unity { _inputBlockTimer = 0.5f; _persistenceService = new PlayerPrefsPersistenceAdapter(); - _gameSession = new GameSession(_allTiles, _persistenceService); + _gameSession = new GameSession(_tileRegistry.AllTiles, _persistenceService); // Set Theme based on High Score ThemeManager.CurrentTheme = ThemeManager.GetTheme(_gameSession.HighScore); @@ -76,7 +74,7 @@ namespace Infrastructure.Unity if (levelGenerator) { - StartCoroutine(levelGenerator.GenerateAsync(soundManager, _allTiles, _tileViews, cameraController, + StartCoroutine(levelGenerator.GenerateAsync(soundManager, _tileRegistry, cameraController, rumbleManager, () => { @@ -84,7 +82,7 @@ namespace Infrastructure.Unity { floorVisibilityManager = gameObject.AddComponent(); } - floorVisibilityManager.Initialize(_gameSession, _allTiles, _tileViews, floorsCount); + floorVisibilityManager.Initialize(_tileRegistry, floorsCount); SpawnDeathPlane(); SpawnPlayer(); @@ -155,9 +153,10 @@ namespace Infrastructure.Unity // Hard Mode: Decay faster as score increases var decayMultiplier = 1.0f + (_gameSession.Score / 500f); - for (var i = _allTiles.Count - 1; i >= 0; i--) + var allTiles = _tileRegistry.AllTiles; + for (var i = allTiles.Count - 1; i >= 0; i--) { - _allTiles[i].Tick(dt * dilation * decayMultiplier); + allTiles[i].Tick(dt * dilation * decayMultiplier); } } @@ -185,7 +184,7 @@ namespace Infrastructure.Unity { if (_currentOrbInstance) Destroy(_currentOrbInstance); - if (!_tileViews.TryGetValue(tileId, out var tileView)) return; + if (!_tileRegistry.TryGetView(tileId, out var tileView)) return; if (!tileView) return; var spawnPos = tileView.transform.position + Vector3.up; @@ -303,18 +302,18 @@ namespace Infrastructure.Unity private void SpawnNpc() { - var validTiles = _allTiles.FindAll(t => t.Floor == 0 && t.CurrentState == TileState.Stable); + var validTiles = _tileRegistry.FindTiles(t => t.Floor == 0 && t.CurrentState == TileState.Stable); if (validTiles.Count == 0) { - validTiles = _allTiles.FindAll(t => t.CurrentState == TileState.Stable); + validTiles = _tileRegistry.FindTiles(t => t.CurrentState == TileState.Stable); } if (validTiles.Count == 0) return; var randomTile = validTiles[Random.Range(0, validTiles.Count)]; - if (!_tileViews.TryGetValue(randomTile.Id, out var tileView)) return; + if (!_tileRegistry.TryGetView(randomTile.Id, out var tileView)) return; if (!tileView) return; var spawnPos = tileView.transform.position + Vector3.up * 5f; @@ -359,7 +358,7 @@ namespace Infrastructure.Unity private void SpawnPowerUp(PowerUpType type, string tileId) { - if (!_tileViews.TryGetValue(tileId, out var tileView)) return; + if (!_tileRegistry.TryGetView(tileId, out var tileView)) return; if (!tileView) return; var spawnPos = tileView.transform.position + Vector3.up * 0.5f; @@ -381,21 +380,22 @@ namespace Infrastructure.Unity private void OnBeatMeasure() { - if (_allTiles.Count == 0) return; + var allTiles = _tileRegistry.AllTiles; + if (allTiles.Count == 0) return; var pulseCount = 25; - + for (var i = 0; i < pulseCount; i++) { - var randIndex = Random.Range(0, _allTiles.Count); - var tile = _allTiles[randIndex]; - + var randIndex = Random.Range(0, allTiles.Count); + var tile = allTiles[randIndex]; + if (tile.Floor < _currentPlayerFloorIndex) continue; if (tile.Floor > _currentPlayerFloorIndex + 1) continue; if (tile.CurrentState != TileState.Stable) continue; - - if (_tileViews.TryGetValue(tile.Id, out var tileView)) + + if (_tileRegistry.TryGetView(tile.Id, out var tileView)) { tileView.PulseEmission(Random.Range(1.2f, 2.0f)); } diff --git a/Assets/Scripts/Infrastructure/Unity/LevelGenerator.cs b/Assets/Scripts/Infrastructure/Unity/LevelGenerator.cs index 471f0c5..a5b76b5 100644 --- a/Assets/Scripts/Infrastructure/Unity/LevelGenerator.cs +++ b/Assets/Scripts/Infrastructure/Unity/LevelGenerator.cs @@ -32,34 +32,34 @@ namespace Infrastructure.Unity private TilePool _tilePool; - public IEnumerator GenerateAsync(SoundManager soundManager, List allTiles, Dictionary tileViews, CameraController camera, RumbleManager rumble, Action onComplete) + public IEnumerator GenerateAsync(SoundManager soundManager, TileRegistry registry, CameraController camera, RumbleManager rumble, Action onComplete) { _tilePool = new TilePool(tilePrefab, transform); - + var stopwatch = new Stopwatch(); stopwatch.Start(); - yield return GenerateFloorAsync(0, MapPatterns.GenerateSquare(gridSizeX, gridSizeY), soundManager, allTiles, tileViews, camera, rumble, stopwatch); - yield return GenerateFloorAsync(1, MapPatterns.GenerateDonut(gridSizeX, Mathf.FloorToInt(gridSizeX / 3f)), soundManager, allTiles, tileViews, camera, rumble, stopwatch); - yield return GenerateFloorAsync(2, MapPatterns.GenerateCircle(gridSizeX), soundManager, allTiles, tileViews, camera, rumble, stopwatch); + yield return GenerateFloorAsync(0, MapPatterns.GenerateSquare(gridSizeX, gridSizeY), soundManager, registry, camera, rumble, stopwatch); + yield return GenerateFloorAsync(1, MapPatterns.GenerateDonut(gridSizeX, Mathf.FloorToInt(gridSizeX / 3f)), soundManager, registry, camera, rumble, stopwatch); + yield return GenerateFloorAsync(2, MapPatterns.GenerateCircle(gridSizeX), soundManager, registry, camera, rumble, stopwatch); stopwatch?.Stop(); onComplete?.Invoke(); } private IEnumerator GenerateFloorAsync(int floorIndex, List coordinates, SoundManager soundManager, - List allTiles, Dictionary tileViews, CameraController camera, RumbleManager rumble, Stopwatch stopwatch) + TileRegistry registry, CameraController camera, RumbleManager rumble, Stopwatch stopwatch) { var yOffset = -(floorIndex * floorHeightDistance); var xOffset = gridSizeX / 2f; var zOffset = gridSizeY / 2f; - + const long frameBudgetMs = 5; foreach (var coord in coordinates) { var pos = new Vector3(coord.x - xOffset, yOffset, coord.y - zOffset); - CreateTile(pos, $"{floorIndex}_{coord.x}_{coord.y}", floorIndex, soundManager, allTiles, tileViews, camera, rumble); + CreateTile(pos, $"{floorIndex}_{coord.x}_{coord.y}", floorIndex, soundManager, registry, camera, rumble); if (stopwatch.ElapsedMilliseconds > frameBudgetMs) { @@ -129,7 +129,7 @@ namespace Infrastructure.Unity } private void CreateTile(Vector3 position, string id, int floorIndex, SoundManager soundManager, - List allTiles, Dictionary tileViews, CameraController camera, RumbleManager rumble) + TileRegistry registry, CameraController camera, RumbleManager rumble) { var go = _tilePool.Get(); go.transform.position = position; @@ -172,8 +172,7 @@ namespace Infrastructure.Unity } }; - allTiles.Add(tileLogic); - tileViews.Add(id, go); + registry.Register(tileLogic, go); } } } \ No newline at end of file diff --git a/Assets/Scripts/Infrastructure/Unity/TileRegistry.cs b/Assets/Scripts/Infrastructure/Unity/TileRegistry.cs new file mode 100644 index 0000000..2364730 --- /dev/null +++ b/Assets/Scripts/Infrastructure/Unity/TileRegistry.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using Core.Domain; + +namespace Infrastructure.Unity +{ + public class TileRegistry + { + private readonly List _tiles = new(); + private readonly Dictionary _views = new(); + + public IReadOnlyList AllTiles => _tiles; + + public void Register(Tile tile, TileViewAdapter view = null) + { + _tiles.Add(tile); + if (view != null) _views[tile.Id] = view; + } + + public bool TryGetView(string tileId, out TileViewAdapter view) + => _views.TryGetValue(tileId, out view); + + public List FindTiles(Predicate predicate) + => _tiles.FindAll(predicate); + + public List> GroupViewsByFloor(int floorCount) + { + var floors = new List>(floorCount); + for (var i = 0; i < floorCount; i++) floors.Add(new List()); + + foreach (var tile in _tiles) + { + if (tile.Floor < floorCount && _views.TryGetValue(tile.Id, out var view)) + floors[tile.Floor].Add(view); + } + + return floors; + } + } +} diff --git a/Assets/Tests/EditMode/TileRegistryTests.cs b/Assets/Tests/EditMode/TileRegistryTests.cs new file mode 100644 index 0000000..3ca79ab --- /dev/null +++ b/Assets/Tests/EditMode/TileRegistryTests.cs @@ -0,0 +1,49 @@ +using NUnit.Framework; +using Core.Domain; +using Infrastructure.Unity; + +namespace DecayGrid.Tests +{ + public class TileRegistryTests + { + private TileRegistry _registry; + + [SetUp] + public void SetUp() => _registry = new TileRegistry(); + + [Test] + public void Register_AddsToAllTiles() + { + var tile = new Tile("0_0_0", 0, 0.5f, 2f); + _registry.Register(tile); + Assert.AreEqual(1, _registry.AllTiles.Count); + } + + [Test] + public void FindTiles_ReturnsMatchingTiles() + { + var tile = new Tile("0_0_0", 0, 0.5f, 2f); + _registry.Register(tile); + var result = _registry.FindTiles(t => t.CurrentState == TileState.Stable); + Assert.AreEqual(1, result.Count); + } + + [Test] + public void FindTiles_ExcludesNonMatchingTiles() + { + var tile = new Tile("0_0_0", 0, 0.5f, 2f); + tile.StepOn(); // Now Warning + _registry.Register(tile); + var result = _registry.FindTiles(t => t.CurrentState == TileState.Stable); + Assert.AreEqual(0, result.Count); + } + + [Test] + public void TryGetView_ReturnsFalse_WhenNoViewRegistered() + { + var tile = new Tile("0_0_0", 0, 0.5f, 2f); + _registry.Register(tile); // no view + Assert.IsFalse(_registry.TryGetView("0_0_0", out _)); + } + } +}