This commit is contained in:
2026-04-21 23:28:49 +07:00
parent 3a687a4d58
commit 8de65bb527
45 changed files with 1056 additions and 551 deletions

View File

@@ -5,14 +5,19 @@
</component>
<component name="ChangeListManager">
<list default="true" id="f9183c68-daf0-43b8-be4c-fad79983f91b" name="Changes" comment="">
<change beforePath="$PROJECT_DIR$/.idea/.idea.HALLUCINATE/.idea/encodings.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.HALLUCINATE/.idea/encodings.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/.idea.HALLUCINATE/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.HALLUCINATE/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/GameSetup/Maze/Crawler.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/GameSetup/Maze/Crawler.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/GameSetup/Maze/Crawler.cs" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/GameSetup/Maze/Crawler.cs.meta" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/GameSetup/Maze/Extensions.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/GameSetup/Maze/Extensions.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/GameSetup/Maze/Maze.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/GameSetup/Maze/Maze.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/GameSetup/Maze/Prims.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/GameSetup/Maze/Prims.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/GameSetup/Maze/Recursive.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/GameSetup/Maze/Recursive.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/GameSetup/Maze/Wilsons.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/GameSetup/Maze/Wilsons.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/GameSetup/Maze/Extensions.cs.meta" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/GameSetup/Maze/Extensions.cs.meta" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/GameSetup/Maze/Maze.cs" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/GameSetup/Maze/Maze.cs.meta" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/GameSetup/Maze/Prims.cs" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/GameSetup/Maze/Prims.cs.meta" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/GameSetup/Maze/Recursive.cs" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/GameSetup/Maze/Recursive.cs.meta" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/GameSetup/Maze/Wilsons.cs" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/GameSetup/Maze/Wilsons.cs.meta" beforeDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -137,7 +142,7 @@
<workItem from="1775313757656" duration="8722000" />
<workItem from="1776130728673" duration="7161000" />
<workItem from="1776255558934" duration="1896000" />
<workItem from="1776780627914" duration="1908000" />
<workItem from="1776780627914" duration="6987000" />
</task>
<servers />
</component>

8
Assets/Prefabs/Maze.meta Normal file
View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0988e2233101908428f5f5334d85a1f7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,114 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &7041334646084879511
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 8919327890245084656}
- component: {fileID: 8819792327415332675}
- component: {fileID: 4127344303633613435}
- component: {fileID: 67198264001523534}
m_Layer: 0
m_Name: CorridorPrefab_
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &8919327890245084656
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7041334646084879511}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 70.81325, y: 0.00003, z: 73.80717}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!33 &8819792327415332675
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7041334646084879511}
m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
--- !u!23 &4127344303633613435
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7041334646084879511}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_ForceMeshLod: -1
m_MeshLodSelectionBias: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_GlobalIlluminationMeshLod: 0
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_MaskInteraction: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!65 &67198264001523534
BoxCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7041334646084879511}
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_IsTrigger: 0
m_ProvidesContacts: 0
m_Enabled: 1
serializedVersion: 3
m_Size: {x: 1, y: 1, z: 1}
m_Center: {x: 0, y: 0, z: 0}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 4872dd25f62fbfa48b25a2b636aa6865
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,63 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1001 &8367681202885872253
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
serializedVersion: 3
m_TransformParent: {fileID: 0}
m_Modifications:
- target: {fileID: 4127344303633613435, guid: 4872dd25f62fbfa48b25a2b636aa6865, type: 3}
propertyPath: 'm_Materials.Array.data[0]'
value:
objectReference: {fileID: 2100000, guid: c2e40fa84d53ccd428f640eac126b1fb, type: 2}
- target: {fileID: 7041334646084879511, guid: 4872dd25f62fbfa48b25a2b636aa6865, type: 3}
propertyPath: m_Name
value: ProcessingPrefab
objectReference: {fileID: 0}
- target: {fileID: 8919327890245084656, guid: 4872dd25f62fbfa48b25a2b636aa6865, type: 3}
propertyPath: m_LocalPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8919327890245084656, guid: 4872dd25f62fbfa48b25a2b636aa6865, type: 3}
propertyPath: m_LocalPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8919327890245084656, guid: 4872dd25f62fbfa48b25a2b636aa6865, type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8919327890245084656, guid: 4872dd25f62fbfa48b25a2b636aa6865, type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 8919327890245084656, guid: 4872dd25f62fbfa48b25a2b636aa6865, type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8919327890245084656, guid: 4872dd25f62fbfa48b25a2b636aa6865, type: 3}
propertyPath: m_LocalRotation.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8919327890245084656, guid: 4872dd25f62fbfa48b25a2b636aa6865, type: 3}
propertyPath: m_LocalRotation.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8919327890245084656, guid: 4872dd25f62fbfa48b25a2b636aa6865, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8919327890245084656, guid: 4872dd25f62fbfa48b25a2b636aa6865, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8919327890245084656, guid: 4872dd25f62fbfa48b25a2b636aa6865, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: 4872dd25f62fbfa48b25a2b636aa6865, type: 3}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 1e8b6ed6b01405e4b9e358abc8f7a058
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,63 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1001 &7905038714086068258
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
serializedVersion: 3
m_TransformParent: {fileID: 0}
m_Modifications:
- target: {fileID: 4127344303633613435, guid: 4872dd25f62fbfa48b25a2b636aa6865, type: 3}
propertyPath: 'm_Materials.Array.data[0]'
value:
objectReference: {fileID: 2100000, guid: b34b07a45ba63ce47896c9e674f3a7a7, type: 2}
- target: {fileID: 7041334646084879511, guid: 4872dd25f62fbfa48b25a2b636aa6865, type: 3}
propertyPath: m_Name
value: WallPrefab
objectReference: {fileID: 0}
- target: {fileID: 8919327890245084656, guid: 4872dd25f62fbfa48b25a2b636aa6865, type: 3}
propertyPath: m_LocalPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8919327890245084656, guid: 4872dd25f62fbfa48b25a2b636aa6865, type: 3}
propertyPath: m_LocalPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8919327890245084656, guid: 4872dd25f62fbfa48b25a2b636aa6865, type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8919327890245084656, guid: 4872dd25f62fbfa48b25a2b636aa6865, type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 8919327890245084656, guid: 4872dd25f62fbfa48b25a2b636aa6865, type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8919327890245084656, guid: 4872dd25f62fbfa48b25a2b636aa6865, type: 3}
propertyPath: m_LocalRotation.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8919327890245084656, guid: 4872dd25f62fbfa48b25a2b636aa6865, type: 3}
propertyPath: m_LocalRotation.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8919327890245084656, guid: 4872dd25f62fbfa48b25a2b636aa6865, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8919327890245084656, guid: 4872dd25f62fbfa48b25a2b636aa6865, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8919327890245084656, guid: 4872dd25f62fbfa48b25a2b636aa6865, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: 4872dd25f62fbfa48b25a2b636aa6865, type: 3}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: c49f25c5b1c3e4b43a2bc56717387124
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,70 +0,0 @@
using UnityEngine;
namespace Hallucinate.GameSetup.Maze
{
/// <summary>
/// A maze generation algorithm that "crawls" through the grid in a semi-random walk.
/// It creates long, winding corridors by moving vertically or horizontally.
/// </summary>
public class Crawler : Maze
{
/// <summary>
/// Orchestrates the crawling generation.
/// (Currently empty as per original procedural logic).
/// </summary>
public override void Generate()
{
// Implementation can be expanded as needed.
}
/// <summary>
/// Performs a vertical crawl starting from a random X position at the bottom.
/// </summary>
protected void CrawlV()
{
bool done = false;
int x = Random.Range(1, width - 1);
int z = 1;
while (!done)
{
map[x, z] = Corridor;
if (Random.Range(0, 100) < 50)
{
x += Random.Range(-1, 2);
}
else
{
z += Random.Range(0, 2);
}
done |= (x < 1 || x >= width - 1 || z < 1 || z >= depth - 1);
}
}
/// <summary>
/// Performs a horizontal crawl starting from a random Z position at the left.
/// </summary>
protected void CrawlH()
{
bool done = false;
int x = 1;
int z = Random.Range(1, depth - 1);
while (!done)
{
map[x, z] = Corridor;
if (Random.Range(0, 100) < 50)
{
x += Random.Range(0, 2);
}
else
{
z += Random.Range(-1, 2);
}
done |= (x < 1 || x >= width - 1 || z < 1 || z >= depth - 1);
}
}
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 7b5776943e6d841879f829c725bf4e6b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,75 @@
using System.Collections;
using UnityEngine;
namespace Hallucinate.GameSetup.Maze
{
public class CrawlerAlgorithm : IMazeAlgorithm
{
private const int CrawlChance = 50;
private const int MinBoundary = 1;
public void Generate(MazeGrid grid)
{
CrawlV(grid, 0);
CrawlH(grid, 0);
}
public IEnumerator GenerateStepByStep(MazeGrid grid, float interval)
{
yield return CrawlV(grid, interval);
yield return CrawlH(grid, interval);
}
private IEnumerator CrawlV(MazeGrid grid, float interval)
{
bool done = false;
int x = Random.Range(MinBoundary, grid.Width - MinBoundary);
int z = MinBoundary;
while (!done)
{
grid.SetCell(x, z, MazeCellType.Processing);
if (interval > 0) yield return new WaitForSeconds(interval);
grid.SetCell(x, z, MazeCellType.Corridor);
if (Random.Range(0, 100) < CrawlChance)
{
x += Random.Range(-1, 2);
}
else
{
z += Random.Range(0, 2);
}
done |= (x < MinBoundary || x >= grid.Width - MinBoundary || z < MinBoundary || z >= grid.Depth - MinBoundary);
}
}
private IEnumerator CrawlH(MazeGrid grid, float interval)
{
bool done = false;
int x = MinBoundary;
int z = Random.Range(MinBoundary, grid.Depth - MinBoundary);
while (!done)
{
grid.SetCell(x, z, MazeCellType.Processing);
if (interval > 0) yield return new WaitForSeconds(interval);
grid.SetCell(x, z, MazeCellType.Corridor);
if (Random.Range(0, 100) < CrawlChance)
{
x += Random.Range(0, 2);
}
else
{
z += Random.Range(-1, 2);
}
done |= (x < MinBoundary || x >= grid.Width - MinBoundary || z < MinBoundary || z >= grid.Depth - MinBoundary);
}
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: bd419f9be92beac48b6f551063165e1f

View File

@@ -2,19 +2,13 @@ using System.Collections.Generic;
namespace Hallucinate.GameSetup.Maze.Extensions
{
/// <summary>
/// Provides utility extension methods for maze generation algorithms.
/// </summary>
public static class Extensions
public static class ListExtensions
{
private static System.Random _rng = new System.Random();
/// <summary>
/// Shuffles the elements of an <see cref="IList{T}"/> using the Fisher-Yates algorithm.
/// This is used to randomize directions for maze generation.
/// Shuffles a list using the Fisher-Yates algorithm.
/// </summary>
/// <typeparam name="T">The type of elements in the list.</typeparam>
/// <param name="list">The list to shuffle.</param>
public static void Shuffle<T>(this IList<T> list)
{
int n = list.Count;

View File

@@ -1,11 +1,2 @@
fileFormatVersion: 2
guid: 7d9791b1b03c14f16a245b2d4577c5f9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
guid: 7d9791b1b03c14f16a245b2d4577c5f9

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6e1bb9cd9af7ffe40ad1a740c3c30dd6
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,19 @@
namespace Hallucinate.GameSetup.Maze
{
/// <summary>
/// Interface for all maze generation algorithms.
/// Supports both immediate and step-by-step (animated) generation.
/// </summary>
public interface IMazeAlgorithm
{
/// <summary>
/// Generates the maze immediately in one frame.
/// </summary>
void Generate(MazeGrid grid);
/// <summary>
/// Generates the maze step-by-step for visualization.
/// </summary>
System.Collections.IEnumerator GenerateStepByStep(MazeGrid grid, float interval);
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 46b6a7796ba3c494581e4dcb884da064

View File

@@ -0,0 +1,32 @@
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;
}
// Static predefined directions to eliminate magic numbers in algorithms
public static MapLocation Right => new MapLocation(1, 0);
public static MapLocation Left => new MapLocation(-1, 0);
public static MapLocation Up => new MapLocation(0, 1);
public static MapLocation Down => new MapLocation(0, -1);
/// <summary>
/// Returns a list of all 4 cardinal directions.
/// </summary>
public static System.Collections.Generic.List<MapLocation> Directions => new System.Collections.Generic.List<MapLocation>
{
Right, Up, Left, Down
};
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 987a7c46c96326a44b3a5f179fe61161

View File

@@ -1,178 +0,0 @@
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
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 1039d646c358a4bd5ac697b0446a2f7e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,16 @@
namespace Hallucinate.GameSetup.Maze
{
/// <summary>
/// Defines the state of each cell in the maze.
/// Used to replace magic numbers and drive visual changes.
/// </summary>
public enum MazeCellType
{
Wall, // Solid block
Corridor, // Finalized path
Processing, // Currently being evaluated by algorithm (Debug)
Path, // Temporary path (e.g., Wilson's crawler)
Start, // Entry point
End // Exit point
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: f54ef08fa4922eb4a968d46c7aa71faf

View File

@@ -0,0 +1,71 @@
using System;
using UnityEngine;
namespace Hallucinate.GameSetup.Maze
{
/// <summary>
/// Holds the logical state of the maze grid.
/// Notifies listeners whenever a cell changes to trigger visual updates.
/// </summary>
public class MazeGrid
{
public int Width { get; }
public int Depth { get; }
private readonly MazeCellType[,] _cells;
/// <summary>
/// Event fired when a cell's type is changed.
/// Useful for the Renderer to trigger animations/FX.
/// </summary>
public event Action<int, int, MazeCellType> OnCellChanged;
public MazeGrid(int width, int depth)
{
Width = width;
Depth = depth;
_cells = new MazeCellType[width, depth];
// Initialize all as walls
for (int z = 0; z < depth; z++)
for (int x = 0; x < width; x++)
_cells[x, z] = MazeCellType.Wall;
}
public void SetCell(int x, int z, MazeCellType type)
{
if (IsInBounds(x, z))
{
if (_cells[x, z] != type)
{
_cells[x, z] = type;
OnCellChanged?.Invoke(x, z, type);
}
}
}
public MazeCellType GetCell(int x, int z)
{
if (IsInBounds(x, z))
return _cells[x, z];
return MazeCellType.Wall; // Treat out of bounds as walls
}
public bool IsInBounds(int x, int z)
{
return x >= 0 && x < Width && z >= 0 && z < Depth;
}
public int CountSquareNeighbours(int x, int z, MazeCellType targetType)
{
int count = 0;
if (x <= 0 || x >= Width - 1 || z <= 0 || z >= Depth - 1) return 5;
if (GetCell(x - 1, z) == targetType) count++;
if (GetCell(x + 1, z) == targetType) count++;
if (GetCell(x, z + 1) == targetType) count++;
if (GetCell(x, z - 1) == targetType) count++;
return count;
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: a1a7a252ff0b1014a9690f08897e2e59

View File

@@ -0,0 +1,81 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Hallucinate.GameSetup.Maze
{
/// <summary>
/// Central controller for the Maze system.
/// Manages algorithm selection, debug speed, and regeneration.
/// </summary>
public class MazeManager : MonoBehaviour
{
public enum AlgorithmType { Recursive, Wilsons, Prims, Crawler }
[Header("System Settings")]
[SerializeField] private AlgorithmType selectedAlgorithm;
[SerializeField] private int width = 30;
[SerializeField] private int depth = 30;
[Header("Debug Settings")]
[SerializeField] private bool debugMode = true;
[Range(0.001f, 0.5f)]
[SerializeField] private float visualizationInterval = 0.05f;
[Header("References")]
[SerializeField] private MazeRenderer mazeRenderer;
[SerializeField] private Transform mazeContainer;
private MazeGrid _grid;
private Coroutine _generationCoroutine;
private void Start()
{
Regenerate();
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.R))
{
Regenerate();
}
}
[ContextMenu("Regenerate")]
public void Regenerate()
{
if (_generationCoroutine != null)
{
StopCoroutine(_generationCoroutine);
}
mazeRenderer.Clear();
_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);
}
}
private IMazeAlgorithm GetAlgorithm(AlgorithmType type)
{
return type switch
{
AlgorithmType.Recursive => new RecursiveAlgorithm(),
AlgorithmType.Wilsons => new WilsonsAlgorithm(),
AlgorithmType.Prims => new PrimsAlgorithm(),
AlgorithmType.Crawler => new CrawlerAlgorithm(),
_ => new RecursiveAlgorithm()
};
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 3607adabe0c29c34591af73b414eb17a

View File

@@ -0,0 +1,102 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Hallucinate.GameSetup.Maze
{
/// <summary>
/// Responsible for the visual representation of the maze.
/// Handles spawning, pooling, and animations.
/// </summary>
public class MazeRenderer : MonoBehaviour
{
[SerializeField] private MazeVisualProfile visualProfile;
private readonly Dictionary<Vector2Int, GameObject> _spawnedCells = new Dictionary<Vector2Int, GameObject>();
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()
{
// IMPORTANT: Stop all running animations to prevent accessing destroyed objects
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);
// Remove old visual if exists
if (_spawnedCells.TryGetValue(pos, out GameObject oldObj))
{
Destroy(oldObj);
_spawnedCells.Remove(pos);
}
GameObject prefab = visualProfile.GetPrefab(type);
if (prefab == null) return;
Vector3 worldPos = new Vector3(x * visualProfile.scale, 0, z * visualProfile.scale);
GameObject newObj = Instantiate(prefab, worldPos, Quaternion.identity, _container);
newObj.transform.localScale = Vector3.one * visualProfile.scale;
_spawnedCells[pos] = newObj;
if (animate)
{
StartCoroutine(AnimateCell(newObj.transform));
}
}
private IEnumerator AnimateCell(Transform target)
{
if (target == null) yield break;
float duration = visualProfile.animationDuration;
float elapsed = 0;
Vector3 finalScale = target.localScale;
target.localScale = Vector3.zero;
while (elapsed < duration)
{
// Extra safety check in case the object is destroyed mid-animation
if (target == null) yield break;
elapsed += Time.deltaTime;
float t = elapsed / duration;
float s = Mathf.Sin(t * Mathf.PI * 0.5f);
target.localScale = finalScale * s;
yield return null;
}
if (target != null)
{
target.localScale = finalScale;
}
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: f30df611110713742ab984f5bead5d88

View File

@@ -0,0 +1,34 @@
using UnityEngine;
namespace Hallucinate.GameSetup.Maze
{
[CreateAssetMenu(fileName = "MazeVisualProfile", menuName = "Hallucinate/Maze/Visual Profile")]
public class MazeVisualProfile : ScriptableObject
{
[Header("Prefabs")]
public GameObject wallPrefab;
public GameObject corridorPrefab;
public GameObject processingPrefab;
public GameObject pathPrefab;
public GameObject startPrefab;
public GameObject endPrefab;
[Header("Visualization Settings")]
public float scale = 1f;
public float animationDuration = 0.25f;
public GameObject GetPrefab(MazeCellType type)
{
return type switch
{
MazeCellType.Wall => wallPrefab,
MazeCellType.Corridor => corridorPrefab,
MazeCellType.Processing => processingPrefab,
MazeCellType.Path => pathPrefab,
MazeCellType.Start => startPrefab,
MazeCellType.End => endPrefab,
_ => null
};
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: d3ff96571406a624381b7b0e596a4d1b

View File

@@ -1,51 +0,0 @@
using System.Collections.Generic;
using UnityEngine;
namespace Hallucinate.GameSetup.Maze
{
/// <summary>
/// Implements a simplified version of Prim's algorithm for maze generation.
/// It picks walls at random and converts them into corridors if they only have one corridor neighbor.
/// </summary>
public class Prims : Maze
{
/// <summary>
/// Generates the maze using Prim's algorithm logic.
/// </summary>
public override void Generate()
{
int x = 2;
int z = 2;
map[x, z] = Corridor;
List<MapLocation> walls = new List<MapLocation>
{
new MapLocation(x + 1, z),
new MapLocation(x - 1, z),
new MapLocation(x, z + 1),
new MapLocation(x, z - 1)
};
int countloops = 0;
while (walls.Count > 0 && countloops < 5000)
{
int rwall = Random.Range(0, walls.Count);
x = walls[rwall].x;
z = walls[rwall].z;
walls.RemoveAt(rwall);
if (CountSquareNeighbours(x, z) == 1)
{
map[x, z] = Corridor;
walls.Add(new MapLocation(x + 1, z));
walls.Add(new MapLocation(x - 1, z));
walls.Add(new MapLocation(x, z + 1));
walls.Add(new MapLocation(x, z - 1));
}
countloops++;
}
}
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: f12a5f6746a454e08a295f64a34f5dcf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,95 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Hallucinate.GameSetup.Maze
{
public class PrimsAlgorithm : IMazeAlgorithm
{
private const int InitialX = 2;
private const int InitialZ = 2;
private const int MaxIterations = 10000;
private const int TargetCorridorNeighbours = 1;
public void Generate(MazeGrid grid)
{
int x = InitialX;
int z = InitialZ;
grid.SetCell(x, z, MazeCellType.Corridor);
List<MapLocation> walls = GetNeighbouringWalls(grid, x, z);
int iterations = 0;
while (walls.Count > 0 && iterations < MaxIterations)
{
int rIndex = Random.Range(0, walls.Count);
MapLocation w = walls[rIndex];
walls.RemoveAt(rIndex);
if (grid.CountSquareNeighbours(w.x, w.z, MazeCellType.Corridor) == TargetCorridorNeighbours)
{
grid.SetCell(w.x, w.z, MazeCellType.Corridor);
walls.AddRange(GetNeighbouringWalls(grid, w.x, w.z));
}
iterations++;
}
}
public IEnumerator GenerateStepByStep(MazeGrid grid, float interval)
{
int x = InitialX;
int z = InitialZ;
grid.SetCell(x, z, MazeCellType.Corridor);
yield return new WaitForSeconds(interval);
List<MapLocation> walls = GetNeighbouringWalls(grid, x, z);
foreach(var w in walls) grid.SetCell(w.x, w.z, MazeCellType.Processing);
int iterations = 0;
while (walls.Count > 0 && iterations < MaxIterations)
{
int rIndex = Random.Range(0, walls.Count);
MapLocation w = walls[rIndex];
walls.RemoveAt(rIndex);
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);
var newWalls = GetNeighbouringWalls(grid, w.x, w.z);
foreach(var nw in newWalls)
{
if (grid.GetCell(nw.x, nw.z) == MazeCellType.Wall)
{
grid.SetCell(nw.x, nw.z, MazeCellType.Processing);
walls.Add(nw);
}
}
}
else
{
if(grid.GetCell(w.x, w.z) == MazeCellType.Processing)
grid.SetCell(w.x, w.z, MazeCellType.Wall);
}
iterations++;
}
}
private List<MapLocation> GetNeighbouringWalls(MazeGrid grid, int x, int z)
{
List<MapLocation> neighbours = new List<MapLocation>();
foreach (var dir in MapLocation.Directions)
{
int nx = x + dir.x;
int nz = z + dir.z;
if (grid.IsInBounds(nx, nz) && grid.GetCell(nx, nz) == MazeCellType.Wall)
{
neighbours.Add(new MapLocation(nx, nz));
}
}
return neighbours;
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: edcdd3c0aa9656a4797b83cc675aa629

View File

@@ -1,40 +0,0 @@
using Hallucinate.GameSetup.Maze.Extensions;
using UnityEngine;
namespace Hallucinate.GameSetup.Maze
{
/// <summary>
/// A recursive backtracker algorithm for maze generation.
/// It explores the grid randomly and backtracks when it reaches a dead end.
/// </summary>
public class Recursive : Maze
{
/// <summary>
/// Entry point for the recursive generation.
/// Starts from a fixed position (5, 5).
/// </summary>
public override void Generate()
{
Generate(5, 5);
}
/// <summary>
/// Internal recursive method that carves corridors by exploring neighbors in a random order.
/// </summary>
/// <param name="x">The current X coordinate.</param>
/// <param name="z">The current Z coordinate.</param>
protected void Generate(int x, int z)
{
if (CountSquareNeighbours(x, z) >= 2) return;
map[x, z] = Corridor;
directions.Shuffle();
Generate(x + directions[0].x, z + directions[0].z);
Generate(x + directions[1].x, z + directions[1].z);
Generate(x + directions[2].x, z + directions[2].z);
Generate(x + directions[3].x, z + directions[3].z);
}
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 33bbdb95ccc4b4577a62495732a02d3e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,69 @@
using System.Collections;
using System.Collections.Generic;
using Hallucinate.GameSetup.Maze.Extensions;
using UnityEngine;
namespace Hallucinate.GameSetup.Maze
{
public class RecursiveAlgorithm : IMazeAlgorithm
{
private const int StartX = 5;
private const int StartZ = 5;
private const int DeadEndNeighbourThreshold = 2;
private readonly List<MapLocation> _directions = MapLocation.Directions;
public void Generate(MazeGrid grid)
{
GenerateRecursive(grid, StartX, StartZ);
}
public IEnumerator GenerateStepByStep(MazeGrid grid, float interval)
{
yield return GenerateRecursiveStepByStep(grid, StartX, StartZ, interval);
}
private void GenerateRecursive(MazeGrid grid, int x, int z)
{
if (grid.CountSquareNeighbours(x, z, MazeCellType.Corridor) >= DeadEndNeighbourThreshold) return;
grid.SetCell(x, z, MazeCellType.Corridor);
List<MapLocation> shuffledDirs = new List<MapLocation>(_directions);
shuffledDirs.Shuffle();
foreach (var dir in shuffledDirs)
{
int nx = x + dir.x;
int nz = z + dir.z;
if (grid.IsInBounds(nx, nz))
{
GenerateRecursive(grid, nx, nz);
}
}
}
private IEnumerator GenerateRecursiveStepByStep(MazeGrid grid, int x, int z, float interval)
{
if (grid.CountSquareNeighbours(x, z, MazeCellType.Corridor) >= DeadEndNeighbourThreshold) yield break;
grid.SetCell(x, z, MazeCellType.Processing);
if (interval > 0) yield return new WaitForSeconds(interval);
grid.SetCell(x, z, MazeCellType.Corridor);
List<MapLocation> shuffledDirs = new List<MapLocation>(_directions);
shuffledDirs.Shuffle();
foreach (var dir in shuffledDirs)
{
int nx = x + dir.x;
int nz = z + dir.z;
if (grid.IsInBounds(nx, nz))
{
yield return GenerateRecursiveStepByStep(grid, nx, nz, interval);
}
}
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 2460c0e9379da9741b3d3387aa6c7a8e

View File

@@ -1,132 +0,0 @@
using System.Collections.Generic;
using UnityEngine;
namespace Hallucinate.GameSetup.Maze
{
/// <summary>
/// Implements Wilson's algorithm for generating a Uniform Spanning Tree of the grid.
/// It uses loop-erased random walks to connect unvisited cells to the existing maze.
/// </summary>
public class Wilsons : Maze
{
private List<MapLocation> _notUsed = new List<MapLocation>();
/// <summary>
/// Generates the maze using Wilson's algorithm logic.
/// </summary>
public override void Generate()
{
// Create a starting cell
int x = Random.Range(2, width - 1);
int z = Random.Range(2, depth - 1);
map[x, z] = Path;
while (GetAvailableCells() > 1)
{
RandomWalk();
}
}
/// <summary>
/// Counts how many neighbors are already part of the finalized "Path".
/// </summary>
private int CountSquareMazeNeighbours(int x, int z)
{
int count = 0;
for (int d = 0; d < directions.Count; d++)
{
int nx = x + directions[d].x;
int nz = z + directions[d].z;
if (map[nx, nz] == Path)
{
count++;
}
}
return count;
}
/// <summary>
/// Scans the grid for cells that are not yet connected to the maze.
/// </summary>
/// <returns>The number of available (unused) cells.</returns>
private int GetAvailableCells()
{
_notUsed.Clear();
for (int z = 1; z < depth - 1; z++)
{
for (int x = 1; x < width - 1; x++)
{
if (CountSquareMazeNeighbours(x, z) == 0)
{
_notUsed.Add(new MapLocation(x, z));
}
}
}
return _notUsed.Count;
}
/// <summary>
/// Performs a random walk from an unused cell until it hits the existing maze.
/// </summary>
private void RandomWalk()
{
List<MapLocation> inWalk = new List<MapLocation>();
int rStartIndex = Random.Range(0, _notUsed.Count);
int cx = _notUsed[rStartIndex].x;
int cz = _notUsed[rStartIndex].z;
inWalk.Add(new MapLocation(cx, cz));
int loopCount = 0;
bool validPath = false;
while (cx > 0 && cx < width - 1 && cz > 0 && cz < depth - 1 && loopCount < 5000 && !validPath)
{
map[cx, cz] = Corridor;
if (CountSquareMazeNeighbours(cx, cz) > 1)
{
break;
}
int rd = Random.Range(0, directions.Count);
int nx = cx + directions[rd].x;
int nz = cz + directions[rd].z;
if (CountSquareNeighbours(nx, nz) < 2)
{
cx = nx;
cz = nz;
inWalk.Add(new MapLocation(cx, cz));
}
validPath = CountSquareMazeNeighbours(cx, cz) == 1;
loopCount++;
}
if (validPath)
{
map[cx, cz] = Corridor;
Debug.Log("Path Found");
foreach (MapLocation m in inWalk)
{
map[m.x, m.z] = Path;
}
inWalk.Clear();
}
else
{
foreach (MapLocation m in inWalk)
{
map[m.x, m.z] = Wall;
}
inWalk.Clear();
}
}
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 502e6aa24e7de43dfb6d20ecd0745176
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,118 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Hallucinate.GameSetup.Maze
{
public class WilsonsAlgorithm : IMazeAlgorithm
{
private const int MinBoundary = 2;
private const int MaxIterationSafety = 5000;
private const int TargetPathNeighbours = 1;
private readonly List<MapLocation> _directions = new List<MapLocation>()
{
new MapLocation(1, 0), new MapLocation(0, 1), new MapLocation(-1, 0), new MapLocation(0, -1)
};
public void Generate(MazeGrid grid)
{
int x = Random.Range(MinBoundary, grid.Width - MinBoundary);
int z = Random.Range(MinBoundary, grid.Depth - MinBoundary);
grid.SetCell(x, z, MazeCellType.Corridor);
while (GetAvailableCells(grid).Count > 1)
{
PerformRandomWalk(grid, null, 0);
}
}
public IEnumerator GenerateStepByStep(MazeGrid grid, float interval)
{
int x = Random.Range(MinBoundary, grid.Width - MinBoundary);
int z = Random.Range(MinBoundary, grid.Depth - MinBoundary);
grid.SetCell(x, z, MazeCellType.Corridor);
yield return new WaitForSeconds(interval);
while (true)
{
var available = GetAvailableCells(grid);
if (available.Count <= 1) break;
yield return PerformRandomWalk(grid, available, interval);
}
}
private List<MapLocation> GetAvailableCells(MazeGrid grid)
{
List<MapLocation> available = new List<MapLocation>();
for (int z = 1; z < grid.Depth - 1; z++)
{
for (int x = 1; x < grid.Width - 1; x++)
{
if (CountPathNeighbours(grid, x, z) == 0)
available.Add(new MapLocation(x, z));
}
}
return available;
}
private int CountPathNeighbours(MazeGrid grid, int x, int z)
{
int count = 0;
foreach (var d in _directions)
{
if (grid.GetCell(x + d.x, z + d.z) == MazeCellType.Corridor) count++;
}
return count;
}
private IEnumerator PerformRandomWalk(MazeGrid grid, List<MapLocation> available, float interval)
{
List<MapLocation> inWalk = new List<MapLocation>();
int rStart = Random.Range(0, available.Count);
int cx = available[rStart].x;
int cz = available[rStart].z;
inWalk.Add(new MapLocation(cx, cz));
bool pathFound = false;
int safety = 0;
while (grid.IsInBounds(cx, cz) && !pathFound && safety < MaxIterationSafety)
{
grid.SetCell(cx, cz, MazeCellType.Processing);
if (interval > 0) yield return new WaitForSeconds(interval);
if (CountPathNeighbours(grid, cx, cz) > 1) break;
int rd = Random.Range(0, _directions.Count);
int nx = cx + _directions[rd].x;
int nz = cz + _directions[rd].z;
if (grid.CountSquareNeighbours(nx, nz, MazeCellType.Processing) < 2)
{
cx = nx;
cz = nz;
inWalk.Add(new MapLocation(cx, cz));
}
pathFound = CountPathNeighbours(grid, cx, cz) == TargetPathNeighbours;
safety++;
}
if (pathFound)
{
foreach (var m in inWalk)
{
grid.SetCell(m.x, m.z, MazeCellType.Corridor);
if (interval > 0) yield return new WaitForSeconds(interval * 0.5f);
}
}
else
{
foreach (var m in inWalk)
grid.SetCell(m.x, m.z, MazeCellType.Wall);
}
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 61156f8986612ca49a0672ad5542380f

View File

@@ -0,0 +1,22 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d3ff96571406a624381b7b0e596a4d1b, type: 3}
m_Name: DefaultMazeProfile
m_EditorClassIdentifier: Assembly-CSharp::Hallucinate.GameSetup.Maze.MazeVisualProfile
wallPrefab: {fileID: 0}
corridorPrefab: {fileID: 0}
processingPrefab: {fileID: 0}
pathPrefab: {fileID: 0}
startPrefab: {fileID: 0}
endPrefab: {fileID: 0}
scale: 1
animationDuration: 0.25

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 15b745b0bb979b84ea937c679ee0f1ed
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant: