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 with safety checks. /// 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() { 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); if (_spawnedCells.TryGetValue(pos, out GameObject oldObj)) { Destroy(oldObj); _spawnedCells.Remove(pos); } GameObject prefab = visualProfile.GetPrefab(type); if (prefab == null) return; // Ensure scale is always positive to avoid BoxCollider issues float safeScale = Mathf.Max(0.001f, visualProfile.scale); Vector3 worldPos = new Vector3(x * safeScale, 0, z * safeScale); GameObject newObj = Instantiate(prefab, worldPos, Quaternion.identity, _container); newObj.transform.localScale = Vector3.one * safeScale; _spawnedCells[pos] = newObj; if (animate && visualProfile.animationDuration > 0) { StartCoroutine(AnimateCell(newObj.transform)); } } private IEnumerator AnimateCell(Transform target) { if (target == null) yield break; float duration = Mathf.Max(0.01f, visualProfile.animationDuration); float elapsed = 0; Vector3 finalScale = target.localScale; target.localScale = Vector3.one * 0.001f; // Use tiny positive instead of zero while (elapsed < duration) { if (target == null) yield break; elapsed += Time.deltaTime; float t = Mathf.Clamp01(elapsed / duration); float s = Mathf.Sin(t * Mathf.PI * 0.5f); // Ensure s is never negative target.localScale = finalScale * Mathf.Max(0.001f, s); yield return null; } if (target != null) { target.localScale = finalScale; } } } }