From 435fbccad580d3792738b736f71ceb6f4e2b1f15 Mon Sep 17 00:00:00 2001 From: Scove Date: Tue, 9 Jun 2026 22:48:04 +0700 Subject: [PATCH] update --- .../Plugins/Native/BackroomsNoise/README.md | 30 ++++ .../Native/BackroomsNoise/README.md.meta | 7 + Assets/Scripts/GameSetup/Maze/MazeManager.cs | 3 +- .../GameSetup/Maze/NoiseRecursiveGenerator.cs | 152 ++++++++++++++++++ .../Maze/NoiseRecursiveGenerator.cs.meta | 2 + 5 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 Assets/Plugins/Native/BackroomsNoise/README.md create mode 100644 Assets/Plugins/Native/BackroomsNoise/README.md.meta create mode 100644 Assets/Scripts/GameSetup/Maze/NoiseRecursiveGenerator.cs create mode 100644 Assets/Scripts/GameSetup/Maze/NoiseRecursiveGenerator.cs.meta diff --git a/Assets/Plugins/Native/BackroomsNoise/README.md b/Assets/Plugins/Native/BackroomsNoise/README.md new file mode 100644 index 00000000..5383caab --- /dev/null +++ b/Assets/Plugins/Native/BackroomsNoise/README.md @@ -0,0 +1,30 @@ +# BackroomsNoise C++ Plugin + +This native plugin provides high-performance noise generation for the Backrooms map system using **FastNoiseLite**. + +## Files +- `BackroomsNoise.cpp`: C wrapper for Unity P/Invoke. +- `FastNoiseLite.h`: The noise library (ensure you have the full version from [GitHub](https://github.com/Auburn/FastNoiseLite)). + +## How to Build (Windows MSVC) + +1. Open **Developer Command Prompt for VS**. +2. Navigate to this directory. +3. Run: + ```cmd + cl /LD /O2 /EHsc BackroomsNoise.cpp /Fe:BackroomsNoise.dll + ``` +4. Copy `BackroomsNoise.dll` to the `Assets/Plugins/` folder in Unity. + +## How to Build (Cross-platform with CMake) + +1. Create a `CMakeLists.txt`: + ```cmake + cmake_minimum_required(VERSION 3.10) + project(BackroomsNoise) + add_library(BackroomsNoise SHARED BackroomsNoise.cpp) + ``` +2. Build as usual. + +## Usage in Unity +The `NativeNoiseProvider.cs` script will automatically attempt to load `BackroomsNoise.dll`. If it fails, it will fall back to Unity's built-in `Mathf.PerlinNoise`. diff --git a/Assets/Plugins/Native/BackroomsNoise/README.md.meta b/Assets/Plugins/Native/BackroomsNoise/README.md.meta new file mode 100644 index 00000000..76af727d --- /dev/null +++ b/Assets/Plugins/Native/BackroomsNoise/README.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: ebf9d2e10b290754b8cae46ebb44cbb0 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/GameSetup/Maze/MazeManager.cs b/Assets/Scripts/GameSetup/Maze/MazeManager.cs index ef1741b4..d219925d 100644 --- a/Assets/Scripts/GameSetup/Maze/MazeManager.cs +++ b/Assets/Scripts/GameSetup/Maze/MazeManager.cs @@ -11,7 +11,7 @@ namespace Hallucinate.GameSetup.Maze /// public class MazeManager : MonoBehaviour { - public enum AlgorithmType { Recursive, Wilsons, Prims, Crawler } + public enum AlgorithmType { Recursive, Wilsons, Prims, Crawler, NoiseRecursive } [BoxGroup("Generation")] [InfoBox("Set the array size to control how many maze floors are generated. Runtime grid data is rebuilt when Regenerate runs.")] @@ -215,6 +215,7 @@ namespace Hallucinate.GameSetup.Maze AlgorithmType.Wilsons => new WilsonsAlgorithm(), AlgorithmType.Prims => new PrimsAlgorithm(), AlgorithmType.Crawler => new CrawlerAlgorithm(), + AlgorithmType.NoiseRecursive => new NoiseRecursiveGenerator(), _ => new RecursiveAlgorithm() }; } diff --git a/Assets/Scripts/GameSetup/Maze/NoiseRecursiveGenerator.cs b/Assets/Scripts/GameSetup/Maze/NoiseRecursiveGenerator.cs new file mode 100644 index 00000000..3d48f3d0 --- /dev/null +++ b/Assets/Scripts/GameSetup/Maze/NoiseRecursiveGenerator.cs @@ -0,0 +1,152 @@ +using System.Collections; +using System.Collections.Generic; +using Hallucinate.GameSetup.Maze.Extensions; +using Hallucinate.GameSetup.Maze.Native; +using UnityEngine; + +namespace Hallucinate.GameSetup.Maze +{ + /// + /// Advanced generator that combines C++ Native Noise with a Recursive Backtracking algorithm. + /// Creates a hybrid layout of large rooms and chaotic corridors. + /// + public class NoiseRecursiveGenerator : IMazeAlgorithm + { + private readonly List _directions = MapLocation.Directions; + private float[] _noiseMap; + private int _seed = 1337; + + // Thresholds + private const float RoomThreshold = 0.5f; + private const float CorridorThreshold = -0.3f; + private const int DeadEndNeighbourThreshold = 2; + + public void Generate(MazeGrid grid) + { + InitializeNoise(grid); + + // Step 1: Pre-place rooms based on noise peaks + for (int z = 0; z < grid.Depth; z++) + { + for (int x = 0; x < grid.Width; x++) + { + float noise = GetNoiseAt(x, z, grid.Width); + if (noise > RoomThreshold) + { + grid.SetCell(x, z, MazeCellType.Corridor); + } + } + } + + // Step 2: Run recursive carving in the "connectable" zones + // We start from a few points to ensure coverage + for (int i = 0; i < 5; i++) + { + int startX = Random.Range(1, grid.Width - 1); + int startZ = Random.Range(1, grid.Depth - 1); + GenerateRecursive(grid, startX, startZ); + } + } + + public IEnumerator GenerateStepByStep(MazeGrid grid, float interval) + { + InitializeNoise(grid); + + // Visual feedback for noise pre-placement + for (int z = 0; z < grid.Depth; z++) + { + for (int x = 0; x < grid.Width; x++) + { + float noise = GetNoiseAt(x, z, grid.Width); + if (noise > RoomThreshold) + { + grid.SetCell(x, z, MazeCellType.Processing); + } + } + if (z % 5 == 0) yield return null; + } + + for (int z = 0; z < grid.Depth; z++) + { + for (int x = 0; x < grid.Width; x++) + { + if (grid.GetCell(x, z) == MazeCellType.Processing) + grid.SetCell(x, z, MazeCellType.Corridor); + } + } + + yield return GenerateRecursiveStepByStep(grid, 5, 5, interval); + } + + private void InitializeNoise(MazeGrid grid) + { + _noiseMap = new float[grid.Width * grid.Depth]; + using (var provider = new NativeNoiseProvider(_seed, 0.05f)) + { + if (provider.IsInitialized) + { + provider.FillBuffer(0, 0, grid.Width, grid.Depth, _noiseMap); + } + else + { + // Fallback to Unity Perlin + for (int z = 0; z < grid.Depth; z++) + { + for (int x = 0; x < grid.Width; x++) + { + _noiseMap[z * grid.Width + x] = Mathf.PerlinNoise(x * 0.1f, z * 0.1f) * 2f - 1f; + } + } + } + } + } + + private float GetNoiseAt(int x, int z, int width) + { + if (_noiseMap == null) return 0f; + return _noiseMap[z * width + x]; + } + + private void GenerateRecursive(MazeGrid grid, int x, int z) + { + // Boundary and Noise check + if (!grid.IsInBounds(x, z)) return; + if (GetNoiseAt(x, z, grid.Width) < CorridorThreshold) return; + if (grid.GetCell(x, z) == MazeCellType.Corridor) return; + + if (grid.CountSquareNeighbours(x, z, MazeCellType.Corridor) >= DeadEndNeighbourThreshold) return; + + grid.SetCell(x, z, MazeCellType.Corridor); + + List shuffledDirs = new List(_directions); + shuffledDirs.Shuffle(); + + foreach (var dir in shuffledDirs) + { + GenerateRecursive(grid, x + dir.x, z + dir.z); + } + } + + private IEnumerator GenerateRecursiveStepByStep(MazeGrid grid, int x, int z, float interval) + { + if (!grid.IsInBounds(x, z)) yield break; + if (GetNoiseAt(x, z, grid.Width) < CorridorThreshold) yield break; + if (grid.GetCell(x, z) == MazeCellType.Corridor) yield break; + + 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 shuffledDirs = new List(_directions); + shuffledDirs.Shuffle(); + + foreach (var dir in shuffledDirs) + { + yield return GenerateRecursiveStepByStep(grid, x + dir.x, z + dir.z, interval); + } + } + } +} diff --git a/Assets/Scripts/GameSetup/Maze/NoiseRecursiveGenerator.cs.meta b/Assets/Scripts/GameSetup/Maze/NoiseRecursiveGenerator.cs.meta new file mode 100644 index 00000000..8cb9a347 --- /dev/null +++ b/Assets/Scripts/GameSetup/Maze/NoiseRecursiveGenerator.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 22e17049420e98f43a692fd3e7d7d261 \ No newline at end of file