Files
BABA_YAGA/Assets/Scripts/GameSetup/Maze/Maze.cs
scove 3a687a4d58 Refactor maze scripts: namespaces & cleanup
Move Maze-related scripts into the Hallucinate.GameSetup.Maze namespace and perform a broad refactor and cleanup. Make MapLocation a readonly struct, add Corridor/Wall/Path constants, and convert Maze into a clearer base class with serialized fields (width, depth, scale, mapParentObject), proper initialization, virtual Generate, and safer DrawMap behavior. Update neighbor-count helpers, tighten method visibility, and improve algorithm implementations (Crawler, Prims, Recursive, Wilsons) to use the new constants and more robust logic (including logging and loop guards). Add a Fisher–Yates Shuffle extension with a static RNG under Hallucinate.GameSetup.Maze.Extensions. Also update IDE metadata (.idea encodings.xml and workspace.xml) to record file encodings and some project settings.
2026-04-21 21:44:26 +07:00

179 lines
5.6 KiB
C#

using System.Collections.Generic;
using UnityEngine;
namespace Hallucinate.GameSetup.Maze
{
/// <summary>
/// Represents a 2D coordinate on the maze grid.
/// Used as a lightweight value type to avoid GC allocations.
/// </summary>
public readonly struct MapLocation
{
public readonly int x;
public readonly int z;
public MapLocation(int _x, int _z)
{
x = _x;
z = _z;
}
}
/// <summary>
/// The base class for maze generation.
/// Handles map initialization, basic neighbor counting logic, and debug rendering.
/// </summary>
public class Maze : MonoBehaviour
{
#region Constants
public const byte Corridor = 0;
public const byte Wall = 1;
public const byte Path = 2;
#endregion
#region Fields
[Header("Maze Settings")]
[SerializeField] protected int width = 30; // x length
[SerializeField] protected int depth = 30; // z length
[SerializeField] protected int scale = 6;
[Header("Hierarchy Settings")]
[UnityEngine.Serialization.FormerlySerializedAs("_mapParentObjet")]
[SerializeField] protected Transform mapParentObject;
/// <summary>
/// List of cardinal directions used for neighbor checking and navigation.
/// </summary>
protected List<MapLocation> directions = new List<MapLocation>()
{
new MapLocation(1, 0),
new MapLocation(0, 1),
new MapLocation(-1, 0),
new MapLocation(0, -1)
};
/// <summary>
/// 2D array representing the maze grid.
/// 0 = Corridor, 1 = Wall, 2 = Special Path.
/// </summary>
public byte[,] map;
#endregion
protected virtual void Start()
{
InitialiseMap();
Generate();
DrawMap();
}
/// <summary>
/// Initializes the map array and fills it with walls.
/// </summary>
protected void InitialiseMap()
{
map = new byte[width, depth];
for (int z = 0; z < depth; z++)
{
for (int x = 0; x < width; x++)
{
map[x, z] = Wall;
}
}
}
/// <summary>
/// Virtual method to be overridden by specific maze generation algorithms.
/// Default implementation creates a random noise-based map.
/// </summary>
public virtual void Generate()
{
for (int z = 0; z < depth; z++)
{
for (int x = 0; x < width; x++)
{
if (Random.Range(0, 100) < 50)
{
map[x, z] = Corridor;
}
}
}
}
/// <summary>
/// Renders the maze in the scene using Unity primitives.
/// </summary>
protected void DrawMap()
{
for (int z = 0; z < depth; z++)
{
for (int x = 0; x < width; x++)
{
if (map[x, z] == Wall)
{
Vector3 pos = new Vector3(x * scale, 0, z * scale);
GameObject wall = GameObject.CreatePrimitive(PrimitiveType.Cube);
wall.transform.localScale = new Vector3(scale, scale, scale);
wall.transform.position = pos;
if (mapParentObject != null)
{
wall.transform.SetParent(mapParentObject);
}
}
}
}
}
#region Helpers
/// <summary>
/// Counts the number of horizontal and vertical neighbors that are corridors.
/// </summary>
/// <param name="x">X coordinate.</param>
/// <param name="z">Z coordinate.</param>
/// <returns>The count of square neighbors.</returns>
public int CountSquareNeighbours(int x, int z)
{
int count = 0;
if (x <= 0 || x >= width - 1 || z <= 0 || z >= depth - 1) return 5;
if (map[x - 1, z] == Corridor) count++;
if (map[x + 1, z] == Corridor) count++;
if (map[x, z + 1] == Corridor) count++;
if (map[x, z - 1] == Corridor) count++;
return count;
}
/// <summary>
/// Counts the number of diagonal neighbors that are corridors.
/// </summary>
/// <param name="x">X coordinate.</param>
/// <param name="z">Z coordinate.</param>
/// <returns>The count of diagonal neighbors.</returns>
public int CountDiagonalNeighbours(int x, int z)
{
int count = 0;
if (x <= 0 || x >= width - 1 || z <= 0 || z >= depth - 1) return 5;
if (map[x - 1, z - 1] == Corridor) count++;
if (map[x + 1, z + 1] == Corridor) count++;
if (map[x - 1, z + 1] == Corridor) count++;
if (map[x + 1, z - 1] == Corridor) count++;
return count;
}
/// <summary>
/// Counts all neighbors (square + diagonal) that are corridors.
/// </summary>
/// <param name="x">X coordinate.</param>
/// <param name="z">Z coordinate.</param>
/// <returns>Total neighbor count.</returns>
public int CountAllNeighbours(int x, int z)
{
return CountSquareNeighbours(x, z) + CountDiagonalNeighbours(x, z);
}
#endregion
}
}