Support per-floor variable maze sizes

Add configuration to vary floor sizes: FloorSizeMode enum, varyFloorSize, floorSizeMode and manualFloorSizes in MazeReworkConfig. MazeReworkGenerator now accepts explicit width/depth via an overload, threads width/depth through generation, and adds optional endBounds to placement logic; dead-end/furthest searches respect bounds. MazeReworkManager computes per-floor seeds and dynamic sizes (manual or randomized), instantiates per-floor generators, and passes end bounds between floors for both animated and instantaneous generation. Also includes minor IDE workspace.xml metadata formatting changes.
This commit is contained in:
2026-07-04 16:39:29 +07:00
parent 0745556ae6
commit 315c14c651
4 changed files with 157 additions and 40 deletions

View File

@@ -12,7 +12,9 @@
<component name="ChangeListManager">
<list default="true" id="d308d1cb-09fc-4331-ba20-00f7b43d1576" name="Changes" comment="">
<change beforePath="$PROJECT_DIR$/.idea/.idea.BABA_YAGA/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.BABA_YAGA/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Baba_yaga/GameSetup/MazeRework/MazeReworkSpawner.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Baba_yaga/GameSetup/MazeRework/MazeReworkSpawner.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Baba_yaga/GameSetup/MazeRework/MazeReworkConfig.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Baba_yaga/GameSetup/MazeRework/MazeReworkConfig.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Baba_yaga/GameSetup/MazeRework/MazeReworkGenerator.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Baba_yaga/GameSetup/MazeRework/MazeReworkGenerator.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Baba_yaga/GameSetup/MazeRework/MazeReworkManager.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Baba_yaga/GameSetup/MazeRework/MazeReworkManager.cs" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -36,10 +38,10 @@
<setting file="file://$PROJECT_DIR$/Library/PackageCache/com.wooshii.foldericons@201a18f355d3/FolderIcons/Editor/FolderIcons.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Library/PackageCache/com.wooshii.foldericons@201a18f355d3/FolderIcons/Editor/FolderIconsEditor.asmdef" root0="SKIP_HIGHLIGHTING" />
</component>
<component name="KubernetesApiPersistence"><![CDATA[{}]]></component>
<component name="KubernetesApiProvider"><![CDATA[{
"isMigrated": true
}]]></component>
<component name="KubernetesApiPersistence">{}</component>
<component name="KubernetesApiProvider">{
&quot;isMigrated&quot;: true
}</component>
<component name="McpProjectServerCommands">
<commands />
<urls />
@@ -146,6 +148,8 @@
<workItem from="1781765548760" duration="8210000" />
<workItem from="1782445122703" duration="1948000" />
<workItem from="1782825955934" duration="1783000" />
<workItem from="1783149945807" duration="606000" />
<workItem from="1783156047890" duration="1863000" />
</task>
<servers />
</component>

View File

@@ -18,6 +18,12 @@ namespace Baba_yaga.GameSetup.MazeRework
/// <summary>
/// Configuration asset containing parameters for the reworked maze generation.
/// </summary>
public enum FloorSizeMode
{
Manual,
Random
}
[CreateAssetMenu(fileName = "MazeReworkConfig", menuName = "BABA_YAGA/MazeRework/Config")]
public class MazeReworkConfig : ScriptableObject
{
@@ -36,6 +42,22 @@ namespace Baba_yaga.GameSetup.MazeRework
[MinValue(5)]
public int depth = 21;
[FoldoutGroup("Grid Dimensions")]
[Tooltip("If true, allows you to vary the floor size across multiple floors.")]
public bool varyFloorSize = false;
[FoldoutGroup("Grid Dimensions")]
[ShowIf("varyFloorSize")]
[Tooltip("Random will pick random sizes up to the max width/depth. Manual uses the list below.")]
[EnumToggleButtons]
public FloorSizeMode floorSizeMode = FloorSizeMode.Random;
[FoldoutGroup("Grid Dimensions")]
[ShowIf("@varyFloorSize && floorSizeMode == FloorSizeMode.Manual")]
[ListDrawerSettings(ShowIndexLabels = true)]
[Tooltip("List of manual sizes for each floor (Index 0 = Floor 0, etc). Must be odd numbers >= 5.")]
public System.Collections.Generic.List<Vector2Int> manualFloorSizes = new System.Collections.Generic.List<Vector2Int>();
[FoldoutGroup("Seeding")]
[Tooltip("If true, a random seed will be generated at runtime.")]
public bool useRandomSeed = true;

View File

@@ -49,10 +49,21 @@ namespace Baba_yaga.GameSetup.MazeRework
}
private readonly MazeReworkConfig _config;
private readonly int _width;
private readonly int _depth;
public MazeReworkGenerator(MazeReworkConfig config)
{
_config = config;
_width = config != null ? config.width : 21;
_depth = config != null ? config.depth : 21;
}
public MazeReworkGenerator(MazeReworkConfig config, int width, int depth)
{
_config = config;
_width = width;
_depth = depth;
}
/// <summary>
@@ -69,12 +80,12 @@ namespace Baba_yaga.GameSetup.MazeRework
/// Generates a 2D layout of the maze of size config.width by config.depth using a specific seed.
/// Generates the maze grid.
/// </summary>
public MazeReworkCellType[,] Generate(int seed, Vector2Int? forcedStart = null, Vector2Int? forcedDirection = null)
public MazeReworkCellType[,] Generate(int seed, Vector2Int? forcedStart = null, Vector2Int? forcedDirection = null, Vector2Int? endBounds = null)
{
if (_config == null) return null;
int width = _config.width;
int depth = _config.depth;
int width = _width;
int depth = _depth;
var grid = new MazeReworkCellType[width, depth];
// 1. Initialize all cells as Wall
@@ -129,7 +140,7 @@ namespace Baba_yaga.GameSetup.MazeRework
CarveLoops(grid, rng, width, depth);
// 8. Place Start & End points
PlaceStartAndEnd(grid, rooms, width, depth, rng, forcedStart, forcedDirection, null);
PlaceStartAndEnd(grid, rooms, width, depth, rng, forcedStart, forcedDirection, null, endBounds);
return grid;
}
@@ -149,12 +160,12 @@ namespace Baba_yaga.GameSetup.MazeRework
return GenerateAnimated(seed);
}
public (MazeReworkCellType[,] grid, List<MazeCellChange> history) GenerateAnimated(int seed, Vector2Int? forcedStart = null, Vector2Int? forcedDirection = null)
public (MazeReworkCellType[,] grid, List<MazeCellChange> history) GenerateAnimated(int seed, Vector2Int? forcedStart = null, Vector2Int? forcedDirection = null, Vector2Int? endBounds = null)
{
if (_config == null) return (null, null);
int width = _config.width;
int depth = _config.depth;
int width = _width;
int depth = _depth;
var grid = new MazeReworkCellType[width, depth];
var history = new List<MazeCellChange>(width * depth * 2);
@@ -219,7 +230,7 @@ namespace Baba_yaga.GameSetup.MazeRework
// 8. Place Start & End points (no highlighting needed for this step in animation)
currentPhase = MazeAnimationPhase.StartEnd;
PlaceStartAndEnd(grid, rooms, width, depth, rng, forcedStart, forcedDirection, recordNoHighlight);
PlaceStartAndEnd(grid, rooms, width, depth, rng, forcedStart, forcedDirection, history, endBounds);
return (grid, history);
}
@@ -469,9 +480,12 @@ namespace Baba_yaga.GameSetup.MazeRework
}
}
private void PlaceStartAndEnd(MazeReworkCellType[,] grid, List<Room> rooms, int width, int depth, System.Random rng, Vector2Int? forcedStart, Vector2Int? forcedDirection,
Action<int, int, MazeReworkCellType> onCellChanged = null)
private void PlaceStartAndEnd(MazeReworkCellType[,] grid, List<Room> rooms, int width, int depth, System.Random rng, Vector2Int? forcedStart, Vector2Int? forcedDirection, List<MazeCellChange> history = null, Vector2Int? endBounds = null)
{
Action<int, int, MazeReworkCellType> onCellChanged = history != null
? (x, z, type) => history.Add(new MazeCellChange(x, z, type, MazeCellHighlight.None, MazeAnimationPhase.StartEnd))
: (Action<int, int, MazeReworkCellType>)null;
Vector2Int startPt = new Vector2Int(-1, -1);
if (forcedStart.HasValue)
@@ -480,7 +494,7 @@ namespace Baba_yaga.GameSetup.MazeRework
}
else
{
startPt = FindDeadEnd(grid, width, depth, rng, new Vector2Int(-1, -1));
startPt = FindDeadEnd(grid, width, depth, rng, new Vector2Int(-1, -1), endBounds);
if (startPt.x == -1)
{
// Fallback to config start if no corridors exist
@@ -493,17 +507,22 @@ namespace Baba_yaga.GameSetup.MazeRework
grid[startPt.x, startPt.y] = MazeReworkCellType.Start;
onCellChanged?.Invoke(startPt.x, startPt.y, MazeReworkCellType.Start);
Vector2Int endPt = FindDeadEnd(grid, width, depth, rng, startPt);
Vector2Int endPt = FindDeadEnd(grid, width, depth, rng, startPt, endBounds);
if (endPt.x == -1)
{
// Fallback: just pick the furthest corridor cell from startPt
endPt = FindFurthestCorridor(grid, width, depth, startPt);
endPt = FindFurthestCorridor(grid, width, depth, startPt, endBounds);
if (endPt.x == -1)
{
// Extreme fallback
int ex = width - 2, ez = depth - 2;
if (endBounds.HasValue)
{
ex = endBounds.Value.x - 2;
ez = endBounds.Value.y - 2;
}
EnsureValidOddCoordinates(width, depth, ref ex, ref ez);
endPt = new Vector2Int(ex, ez);
}
@@ -518,14 +537,17 @@ namespace Baba_yaga.GameSetup.MazeRework
EnforceSingleConnection(grid, endPt.x, endPt.y, null);
}
private Vector2Int FindDeadEnd(MazeReworkCellType[,] grid, int width, int depth, System.Random rng, Vector2Int exclude)
private Vector2Int FindDeadEnd(MazeReworkCellType[,] grid, int width, int depth, System.Random rng, Vector2Int exclude, Vector2Int? bounds = null)
{
List<Vector2Int> deadEnds = new List<Vector2Int>();
List<Vector2Int> corridors = new List<Vector2Int>();
int maxW = bounds.HasValue ? Mathf.Min(width, bounds.Value.x) : width;
int maxD = bounds.HasValue ? Mathf.Min(depth, bounds.Value.y) : depth;
for (int z = 1; z < depth - 1; z++)
for (int z = 1; z < maxD - 1; z++)
{
for (int x = 1; x < width - 1; x++)
for (int x = 1; x < maxW - 1; x++)
{
if (grid[x, z] == MazeReworkCellType.Corridor)
{
@@ -551,14 +573,17 @@ namespace Baba_yaga.GameSetup.MazeRework
return new Vector2Int(-1, -1);
}
private Vector2Int FindFurthestCorridor(MazeReworkCellType[,] grid, int width, int depth, Vector2Int from)
private Vector2Int FindFurthestCorridor(MazeReworkCellType[,] grid, int width, int depth, Vector2Int from, Vector2Int? bounds = null)
{
Vector2Int best = new Vector2Int(-1, -1);
float maxDist = -1f;
int maxW = bounds.HasValue ? Mathf.Min(width, bounds.Value.x) : width;
int maxD = bounds.HasValue ? Mathf.Min(depth, bounds.Value.y) : depth;
for (int z = 1; z < depth - 1; z++)
for (int z = 1; z < maxD - 1; z++)
{
for (int x = 1; x < width - 1; x++)
for (int x = 1; x < maxW - 1; x++)
{
if (grid[x, z] == MazeReworkCellType.Corridor)
{

View File

@@ -122,34 +122,83 @@ namespace Baba_yaga.GameSetup.MazeRework
ClearMaze();
if (animator != null) animator.StopAnimation();
var generator = new MazeReworkGenerator(config);
if (animateGeneration && animator != null)
{
if (Application.isPlaying)
{
StartCoroutine(GenerateAndSpawnAnimatedRoutine(generator));
StartCoroutine(GenerateAndSpawnAnimatedRoutine());
}
else
{
Debug.LogWarning("[MazeReworkManager] Animation is only supported in Play Mode. Falling back to instantaneous generation.");
GenerateInstantaneous(generator);
GenerateInstantaneous();
}
}
else
{
GenerateInstantaneous(generator);
GenerateInstantaneous();
}
}
private IEnumerator GenerateAndSpawnAnimatedRoutine(MazeReworkGenerator generator)
private void GetDynamicFloorSize(int floorIndex, int seed, out int width, out int depth)
{
width = config.width;
depth = config.depth;
if (config.varyFloorSize)
{
if (config.floorSizeMode == FloorSizeMode.Manual)
{
if (config.manualFloorSizes != null && floorIndex < config.manualFloorSizes.Count)
{
width = config.manualFloorSizes[floorIndex].x;
depth = config.manualFloorSizes[floorIndex].y;
}
}
else // Random
{
System.Random rng = new System.Random(seed);
// Generate random odd sizes between 5 and config.width/depth
int wRange = (config.width - 5) / 2;
int dRange = (config.depth - 5) / 2;
width = 5 + rng.Next(0, wRange + 1) * 2;
depth = 5 + rng.Next(0, dRange + 1) * 2;
}
// Ensure they are odd and at least 5
if (width % 2 == 0) width--;
if (depth % 2 == 0) depth--;
if (width < 5) width = 5;
if (depth < 5) depth = 5;
}
}
private IEnumerator GenerateAndSpawnAnimatedRoutine()
{
int[] floorSeeds = new int[floorCount];
Vector2Int[] floorSizes = new Vector2Int[floorCount];
for (int i = 0; i < floorCount; i++)
{
floorSeeds[i] = config.useRandomSeed ? System.Guid.NewGuid().GetHashCode() : config.seed + i * 1000;
GetDynamicFloorSize(i, floorSeeds[i], out int fW, out int fD);
floorSizes[i] = new Vector2Int(fW, fD);
}
Vector2Int? nextStartPos = null;
Vector2Int? nextStartDir = null;
for (int i = 0; i < floorCount; i++)
{
int seed = config.useRandomSeed ? System.Guid.NewGuid().GetHashCode() : config.seed + i * 1000;
var (grid, history) = generator.GenerateAnimated(seed, nextStartPos, nextStartDir);
int fWidth = floorSizes[i].x;
int fDepth = floorSizes[i].y;
var generator = new MazeReworkGenerator(config, fWidth, fDepth);
Vector2Int? endBounds = null;
if (i < floorCount - 1)
{
endBounds = floorSizes[i + 1];
}
var (grid, history) = generator.GenerateAnimated(floorSeeds[i], nextStartPos, nextStartDir, endBounds);
if (i > 0 && nextStartPos.HasValue)
{
@@ -159,9 +208,9 @@ namespace Baba_yaga.GameSetup.MazeRework
if (i < floorCount - 1)
{
Vector2Int endPos = new Vector2Int(-1, -1);
for (int z = 0; z < config.depth; z++)
for (int z = 0; z < fDepth; z++)
{
for (int x = 0; x < config.width; x++)
for (int x = 0; x < fWidth; x++)
{
if (grid[x, z] == MazeReworkCellType.End) { endPos = new Vector2Int(x, z); break; }
}
@@ -192,14 +241,32 @@ namespace Baba_yaga.GameSetup.MazeRework
Debug.Log($"[MazeReworkManager] All floors animated! Beginning world position: {BeginningWorldPosition}");
}
private void GenerateInstantaneous(MazeReworkGenerator generator)
private void GenerateInstantaneous()
{
int[] floorSeeds = new int[floorCount];
Vector2Int[] floorSizes = new Vector2Int[floorCount];
for (int i = 0; i < floorCount; i++)
{
floorSeeds[i] = config.useRandomSeed ? System.Guid.NewGuid().GetHashCode() : config.seed + i * 1000;
GetDynamicFloorSize(i, floorSeeds[i], out int fW, out int fD);
floorSizes[i] = new Vector2Int(fW, fD);
}
Vector2Int? nextStartPos = null;
Vector2Int? nextStartDir = null;
for (int i = 0; i < floorCount; i++)
{
int seed = config.useRandomSeed ? System.Guid.NewGuid().GetHashCode() : config.seed + i * 1000;
var grid = generator.Generate(seed, nextStartPos, nextStartDir);
int fWidth = floorSizes[i].x;
int fDepth = floorSizes[i].y;
var generator = new MazeReworkGenerator(config, fWidth, fDepth);
Vector2Int? endBounds = null;
if (i < floorCount - 1)
{
endBounds = floorSizes[i + 1];
}
var grid = generator.Generate(floorSeeds[i], nextStartPos, nextStartDir, endBounds);
// If this is not the first floor, replace the Start with StairsDown to connect to the floor below
if (i > 0 && nextStartPos.HasValue)
@@ -207,13 +274,12 @@ namespace Baba_yaga.GameSetup.MazeRework
grid[nextStartPos.Value.x, nextStartPos.Value.y] = MazeReworkCellType.StairsDown;
}
// If there is a floor above this one, convert the End cell to a StairsUp
if (i < floorCount - 1)
{
Vector2Int endPos = new Vector2Int(-1, -1);
for (int z = 0; z < config.depth; z++)
for (int z = 0; z < fDepth; z++)
{
for (int x = 0; x < config.width; x++)
for (int x = 0; x < fWidth; x++)
{
if (grid[x, z] == MazeReworkCellType.End)
{