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.
179 lines
5.6 KiB
C#
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
|
|
}
|
|
}
|