using System.Collections; using System.Collections.Generic; using UnityEngine; namespace Hallucinate.GameSetup.Maze { /// /// Responsible for the visual representation of the maze. /// Handles spawning, pooling, and animations. /// public class MazeRenderer : MonoBehaviour { [SerializeField] private MazeVisualProfile visualProfile; private readonly Dictionary _spawnedCells = new Dictionary(); private Transform _container; public void Initialize(MazeGrid grid, Transform container) { _container = container; grid.OnCellChanged += HandleCellChanged; // Initial render for (int z = 0; z < grid.Depth; z++) { for (int x = 0; x < grid.Width; x++) { UpdateCellVisual(x, z, grid.GetCell(x, z), false); } } } public void Clear() { // IMPORTANT: Stop all running animations to prevent accessing destroyed objects StopAllCoroutines(); foreach (var cell in _spawnedCells.Values) { if (cell != null) Destroy(cell); } _spawnedCells.Clear(); } private void HandleCellChanged(int x, int z, MazeCellType type) { UpdateCellVisual(x, z, type, true); } private void UpdateCellVisual(int x, int z, MazeCellType type, bool animate) { Vector2Int pos = new Vector2Int(x, z); // Remove old visual if exists if (_spawnedCells.TryGetValue(pos, out GameObject oldObj)) { Destroy(oldObj); _spawnedCells.Remove(pos); } GameObject prefab = visualProfile.GetPrefab(type); if (prefab == null) return; Vector3 worldPos = new Vector3(x * visualProfile.scale, 0, z * visualProfile.scale); GameObject newObj = Instantiate(prefab, worldPos, Quaternion.identity, _container); newObj.transform.localScale = Vector3.one * visualProfile.scale; _spawnedCells[pos] = newObj; if (animate) { StartCoroutine(AnimateCell(newObj.transform)); } } private IEnumerator AnimateCell(Transform target) { if (target == null) yield break; float duration = visualProfile.animationDuration; float elapsed = 0; Vector3 finalScale = target.localScale; target.localScale = Vector3.zero; while (elapsed < duration) { // Extra safety check in case the object is destroyed mid-animation if (target == null) yield break; elapsed += Time.deltaTime; float t = elapsed / duration; float s = Mathf.Sin(t * Mathf.PI * 0.5f); target.localScale = finalScale * s; yield return null; } if (target != null) { target.localScale = finalScale; } } } }