This commit is contained in:
2026-06-26 00:21:29 +07:00
parent 5d1af952b1
commit 4fa3fb2c15
16 changed files with 221 additions and 2011 deletions

View File

@@ -16,13 +16,13 @@ namespace Hallucinate.GameSetup.Maze
for (int i = 0; i < HorizontalCrawlerCount; i++) CrawlH(grid, 0);
}
public IEnumerator GenerateStepByStep(MazeGrid grid, float interval)
public IEnumerator GenerateStepByStep(MazeGrid grid, int cellsPerFrame)
{
for (int i = 0; i < VerticalCrawlerCount; i++) yield return CrawlV(grid, interval);
for (int i = 0; i < HorizontalCrawlerCount; i++) yield return CrawlH(grid, interval);
for (int i = 0; i < VerticalCrawlerCount; i++) yield return CrawlV(grid, cellsPerFrame);
for (int i = 0; i < HorizontalCrawlerCount; i++) yield return CrawlH(grid, cellsPerFrame);
}
private IEnumerator CrawlV(MazeGrid grid, float interval)
private IEnumerator CrawlV(MazeGrid grid, int cellsPerFrame)
{
bool done = false;
int x = Random.Range(MinBoundary, grid.Width - MinBoundary);
@@ -31,7 +31,12 @@ namespace Hallucinate.GameSetup.Maze
while (!done)
{
grid.SetCell(x, z, MazeCellType.Processing);
if (interval > 0) yield return new WaitForSeconds(interval);
MazeManager.cellsProcessedThisFrame++;
if (MazeManager.cellsProcessedThisFrame >= cellsPerFrame)
{
MazeManager.cellsProcessedThisFrame = 0;
yield return null;
}
grid.SetCell(x, z, MazeCellType.Corridor);
@@ -48,7 +53,7 @@ namespace Hallucinate.GameSetup.Maze
}
}
private IEnumerator CrawlH(MazeGrid grid, float interval)
private IEnumerator CrawlH(MazeGrid grid, int cellsPerFrame)
{
bool done = false;
int x = MinBoundary;
@@ -57,7 +62,12 @@ namespace Hallucinate.GameSetup.Maze
while (!done)
{
grid.SetCell(x, z, MazeCellType.Processing);
if (interval > 0) yield return new WaitForSeconds(interval);
MazeManager.cellsProcessedThisFrame++;
if (MazeManager.cellsProcessedThisFrame >= cellsPerFrame)
{
MazeManager.cellsProcessedThisFrame = 0;
yield return null;
}
grid.SetCell(x, z, MazeCellType.Corridor);

View File

@@ -14,6 +14,6 @@ namespace Hallucinate.GameSetup.Maze
/// <summary>
/// Generates the maze step-by-step for visualization.
/// </summary>
System.Collections.IEnumerator GenerateStepByStep(MazeGrid grid, float interval);
System.Collections.IEnumerator GenerateStepByStep(MazeGrid grid, int cellsPerFrame);
}
}

View File

@@ -37,13 +37,25 @@ namespace Hallucinate.GameSetup.Maze
[PropertyRange(5, 200)]
[SerializeField] private int depth = 30;
[BoxGroup("Debug")]
[SerializeField] private bool debugMode = true;
[BoxGroup("Animation")]
[SerializeField] private bool animateGeneration = true;
[BoxGroup("Debug")]
[ShowIf(nameof(debugMode))]
[PropertyRange(0.001f, 0.5f)]
[SerializeField] private float visualizationInterval = 0.05f;
[BoxGroup("Animation")]
[ShowIf(nameof(animateGeneration))]
[PropertyRange(1, 500)]
[LabelText("Generation Speed (Cells/Frame)")]
[SerializeField] private int generationSpeed = 50;
public static int cellsProcessedThisFrame;
[BoxGroup("Animation")]
[ShowIf(nameof(animateGeneration))]
public MazeRenderer.CellAnimationType cellAnimationType = MazeRenderer.CellAnimationType.ScaleUp;
[BoxGroup("Progress")]
[ProgressBar(0, 100)]
[ShowInInspector, ReadOnly]
private float completionPercentage;
[BoxGroup("References")]
[Required]
@@ -74,6 +86,7 @@ namespace Hallucinate.GameSetup.Maze
private MazeGrid _grid;
private Coroutine _generationCoroutine;
private HashSet<Vector3Int> _modifiedCells = new HashSet<Vector3Int>();
private void Start()
{
@@ -88,6 +101,25 @@ namespace Hallucinate.GameSetup.Maze
}
}
[ContextMenu("Clear Maze")]
[Button("Clear Maze", ButtonSizes.Large)]
public void ClearMaze()
{
if (_generationCoroutine != null)
{
StopCoroutine(_generationCoroutine);
_generationCoroutine = null;
}
if (mazeRenderer != null)
{
mazeRenderer.Clear();
}
completionPercentage = 0f;
_modifiedCells?.Clear();
}
[ContextMenu("Regenerate")]
[Button("Regenerate Maze", ButtonSizes.Large)]
public void Regenerate()
@@ -109,24 +141,85 @@ namespace Hallucinate.GameSetup.Maze
mazes = new MazeGrid[1];
}
if (_generationCoroutine != null)
ClearMaze();
mazeRenderer.currentAnimationType = cellAnimationType;
if (animateGeneration)
{
StopCoroutine(_generationCoroutine);
_generationCoroutine = StartCoroutine(GenerateMazeRoutine());
}
else
{
GenerateMazeInstant();
}
}
mazeRenderer.Clear();
private void GenerateMazeInstant()
{
_modifiedCells.Clear();
completionPercentage = 0f;
// Step 1: Initialize all maze floors
for (int i = 0; i < mazes.Length; i++)
{
mazes[i] = new MazeGrid(width, depth);
mazes[i].Level = i;
// Generate each floor using the selected algorithm
IMazeAlgorithm algorithmForFloor = GetAlgorithm(selectedAlgorithm);
algorithmForFloor.Generate(mazes[i]);
}
GenerateConnections();
for (int i = 0; i < mazes.Length; i++)
{
mazeRenderer.Initialize(mazes[i], mazeContainer, i == 0);
}
if (mazes.Length > 0) _grid = mazes[0];
completionPercentage = 100f;
}
private IEnumerator GenerateMazeRoutine()
{
_modifiedCells.Clear();
completionPercentage = 0f;
for (int i = 0; i < mazes.Length; i++)
{
mazes[i] = new MazeGrid(width, depth);
mazes[i].Level = i;
int floorIndex = i;
mazes[i].OnCellChanged += (x, z, type) =>
{
if (type != MazeCellType.Wall)
{
_modifiedCells.Add(new Vector3Int(x, floorIndex, z));
int totalCells = width * depth * mazes.Length;
// Approximate the progress to reach roughly 100% since algorithms don't visit all cells
float fillRatio = 0.6f;
completionPercentage = Mathf.Clamp((_modifiedCells.Count / ((float)totalCells * fillRatio)) * 100f, 0, 99f);
}
};
mazeRenderer.Initialize(mazes[i], mazeContainer, i == 0);
IMazeAlgorithm algorithmForFloor = GetAlgorithm(selectedAlgorithm);
cellsProcessedThisFrame = 0;
yield return StartCoroutine(algorithmForFloor.GenerateStepByStep(mazes[i], generationSpeed));
}
GenerateConnections();
if (mazes.Length > 0) _grid = mazes[0];
completionPercentage = 100f;
_generationCoroutine = null;
}
private void GenerateConnections()
{
// Step 2: Create connections between adjacent floors
for (int i = 0; i < mazes.Length - 1; i++)
{
@@ -167,35 +260,8 @@ namespace Hallucinate.GameSetup.Maze
connectionsMade++;
}
}
// Step 3: Render all floors
if (mazes.Length > 0)
{
for (int i = 0; i < mazes.Length; i++)
{
mazeRenderer.Initialize(mazes[i], mazeContainer, i == 0);
}
_grid = mazes[0];
}
else
{
// _grid = new MazeGrid(width, depth);
// mazeRenderer.Initialize(_grid, mazeContainer);
IMazeAlgorithm algorithm = GetAlgorithm(selectedAlgorithm);
if (debugMode)
{
_generationCoroutine = StartCoroutine(algorithm.GenerateStepByStep(_grid, visualizationInterval));
}
else
{
algorithm.Generate(_grid);
}
_grid = new MazeGrid(width, depth);
mazeRenderer.Initialize(_grid, mazeContainer);
}
}
private void ShuffleList<T>(List<T> list)
{
for (int i = 0; i < list.Count; i++)

View File

@@ -22,6 +22,11 @@ namespace Hallucinate.GameSetup.Maze
public float Scale => visualProfile != null ? visualProfile.scale : 1f;
public enum CellAnimationType { None, ScaleUp, DropDown, SpinIn }
[HideInInspector]
public CellAnimationType currentAnimationType = CellAnimationType.ScaleUp;
[ShowInInspector]
[ReadOnly]
[BoxGroup("Runtime")]
@@ -294,10 +299,27 @@ namespace Hallucinate.GameSetup.Maze
{
if (target == null) yield break;
if (currentAnimationType == CellAnimationType.None) yield break;
float duration = Mathf.Max(0.01f, visualProfile.animationDuration);
float elapsed = 0;
Vector3 finalScale = target.localScale;
target.localScale = Vector3.one * 0.001f;
Vector3 finalPosition = target.localPosition;
Quaternion finalRotation = target.localRotation;
if (currentAnimationType == CellAnimationType.ScaleUp)
{
target.localScale = Vector3.one * 0.001f;
}
else if (currentAnimationType == CellAnimationType.DropDown)
{
target.localPosition = finalPosition + Vector3.up * 5f;
}
else if (currentAnimationType == CellAnimationType.SpinIn)
{
target.localScale = Vector3.one * 0.001f;
target.localRotation = finalRotation * Quaternion.Euler(0, 180, 0);
}
while (elapsed < duration)
{
@@ -305,16 +327,32 @@ namespace Hallucinate.GameSetup.Maze
elapsed += Time.deltaTime;
float t = Mathf.Clamp01(elapsed / duration);
float s = Mathf.Sin(t * Mathf.PI * 0.5f);
target.localScale = finalScale * Mathf.Max(0.001f, s);
if (currentAnimationType == CellAnimationType.ScaleUp)
{
float s = Mathf.Sin(t * Mathf.PI * 0.5f);
target.localScale = finalScale * Mathf.Max(0.001f, s);
}
else if (currentAnimationType == CellAnimationType.DropDown)
{
float s = Mathf.Sin(t * Mathf.PI * 0.5f);
target.localPosition = Vector3.Lerp(finalPosition + Vector3.up * 5f, finalPosition, s);
}
else if (currentAnimationType == CellAnimationType.SpinIn)
{
float s = Mathf.Sin(t * Mathf.PI * 0.5f);
target.localScale = finalScale * Mathf.Max(0.001f, s);
target.localRotation = Quaternion.Lerp(finalRotation * Quaternion.Euler(0, 180, 0), finalRotation, s);
}
yield return null;
}
if (target != null)
{
// Scale
target.localScale = finalScale;
target.localPosition = finalPosition;
target.localRotation = finalRotation;
}
}
}

View File

@@ -48,7 +48,7 @@ namespace Hallucinate.GameSetup.Maze
}
}
public IEnumerator GenerateStepByStep(MazeGrid grid, float interval)
public IEnumerator GenerateStepByStep(MazeGrid grid, int cellsPerFrame)
{
InitializeNoise(grid);
@@ -75,7 +75,7 @@ namespace Hallucinate.GameSetup.Maze
}
}
yield return GenerateRecursiveStepByStep(grid, 5, 5, interval);
yield return GenerateRecursiveStepByStep(grid, 5, 5, cellsPerFrame);
}
private void InitializeNoise(MazeGrid grid)
@@ -127,7 +127,7 @@ namespace Hallucinate.GameSetup.Maze
}
}
private IEnumerator GenerateRecursiveStepByStep(MazeGrid grid, int x, int z, float interval)
private IEnumerator GenerateRecursiveStepByStep(MazeGrid grid, int x, int z, int cellsPerFrame)
{
if (!grid.IsInBounds(x, z)) yield break;
if (GetNoiseAt(x, z, grid.Width) < CorridorThreshold) yield break;
@@ -136,7 +136,12 @@ namespace Hallucinate.GameSetup.Maze
if (grid.CountSquareNeighbours(x, z, MazeCellType.Corridor) >= DeadEndNeighbourThreshold) yield break;
grid.SetCell(x, z, MazeCellType.Processing);
if (interval > 0) yield return new WaitForSeconds(interval);
MazeManager.cellsProcessedThisFrame++;
if (MazeManager.cellsProcessedThisFrame >= cellsPerFrame)
{
MazeManager.cellsProcessedThisFrame = 0;
yield return null;
}
grid.SetCell(x, z, MazeCellType.Corridor);
@@ -145,7 +150,7 @@ namespace Hallucinate.GameSetup.Maze
foreach (var dir in shuffledDirs)
{
yield return GenerateRecursiveStepByStep(grid, x + dir.x, z + dir.z, interval);
yield return GenerateRecursiveStepByStep(grid, x + dir.x, z + dir.z, cellsPerFrame);
}
}
}

View File

@@ -39,12 +39,12 @@ namespace Hallucinate.GameSetup.Maze
}
}
public IEnumerator GenerateStepByStep(MazeGrid grid, float interval)
public IEnumerator GenerateStepByStep(MazeGrid grid, int cellsPerFrame)
{
int x = InitialX;
int z = InitialZ;
grid.SetCell(x, z, MazeCellType.Corridor);
yield return new WaitForSeconds(interval);
yield return null;
List<MapLocation> walls = GetNeighbouringWalls(grid, x, z);
foreach(var w in walls) grid.SetCell(w.x, w.z, MazeCellType.Processing);
@@ -59,7 +59,12 @@ namespace Hallucinate.GameSetup.Maze
if (grid.CountSquareNeighbours(w.x, w.z, MazeCellType.Corridor) == TargetCorridorNeighbours)
{
grid.SetCell(w.x, w.z, MazeCellType.Corridor);
if (interval > 0) yield return new WaitForSeconds(interval);
MazeManager.cellsProcessedThisFrame++;
if (MazeManager.cellsProcessedThisFrame >= cellsPerFrame)
{
MazeManager.cellsProcessedThisFrame = 0;
yield return null;
}
foreach (var nw in GetNeighbouringWalls(grid, w.x, w.z))
{

View File

@@ -18,9 +18,9 @@ namespace Hallucinate.GameSetup.Maze
GenerateRecursive(grid, StartX, StartZ);
}
public IEnumerator GenerateStepByStep(MazeGrid grid, float interval)
public IEnumerator GenerateStepByStep(MazeGrid grid, int cellsPerFrame)
{
yield return GenerateRecursiveStepByStep(grid, StartX, StartZ, interval);
yield return GenerateRecursiveStepByStep(grid, StartX, StartZ, cellsPerFrame);
}
private void GenerateRecursive(MazeGrid grid, int x, int z)
@@ -43,12 +43,17 @@ namespace Hallucinate.GameSetup.Maze
}
}
private IEnumerator GenerateRecursiveStepByStep(MazeGrid grid, int x, int z, float interval)
private IEnumerator GenerateRecursiveStepByStep(MazeGrid grid, int x, int z, int cellsPerFrame)
{
if (grid.CountSquareNeighbours(x, z, MazeCellType.Corridor) >= DeadEndNeighbourThreshold) yield break;
grid.SetCell(x, z, MazeCellType.Processing);
if (interval > 0) yield return new WaitForSeconds(interval);
MazeManager.cellsProcessedThisFrame++;
if (MazeManager.cellsProcessedThisFrame >= cellsPerFrame)
{
MazeManager.cellsProcessedThisFrame = 0;
yield return null;
}
grid.SetCell(x, z, MazeCellType.Corridor);
@@ -61,7 +66,7 @@ namespace Hallucinate.GameSetup.Maze
int nz = z + dir.z;
if (grid.IsInBounds(nx, nz))
{
yield return GenerateRecursiveStepByStep(grid, nx, nz, interval);
yield return GenerateRecursiveStepByStep(grid, nx, nz, cellsPerFrame);
}
}
}

View File

@@ -32,17 +32,17 @@ namespace Hallucinate.GameSetup.Maze
}
}
public IEnumerator GenerateStepByStep(MazeGrid grid, float interval)
public IEnumerator GenerateStepByStep(MazeGrid grid, int cellsPerFrame)
{
int x = Random.Range(MinBoundary, grid.Width - 1);
int z = Random.Range(MinBoundary, grid.Depth - 1);
grid.SetCell(x, z, MazeCellType.Corridor);
yield return new WaitForSeconds(interval);
yield return null;
int safety = 0;
while (GetAvailableCells(grid) > 1 && safety < MaxIterationSafety)
{
yield return RandomWalk(grid, interval);
yield return RandomWalk(grid, cellsPerFrame);
safety++;
}
}
@@ -123,7 +123,7 @@ namespace Hallucinate.GameSetup.Maze
}
}
private IEnumerator RandomWalk(MazeGrid grid, float interval)
private IEnumerator RandomWalk(MazeGrid grid, int cellsPerFrame)
{
if (_notUsed.Count == 0) yield break;
@@ -139,7 +139,12 @@ namespace Hallucinate.GameSetup.Maze
while (cx > 0 && cx < grid.Width - 1 && cz > 0 && cz < grid.Depth - 1 && loop < MaxWalkSteps && !validPath)
{
grid.SetCell(cx, cz, MazeCellType.Processing); // State 0
if (interval > 0) yield return new WaitForSeconds(interval);
MazeManager.cellsProcessedThisFrame++;
if (MazeManager.cellsProcessedThisFrame >= cellsPerFrame)
{
MazeManager.cellsProcessedThisFrame = 0;
yield return null;
}
if (CountFinalizedNeighbours(grid, cx, cz) > 1) break;