Change map

This commit is contained in:
Lucastaa
2026-05-08 08:56:47 +07:00
parent 0b4c323d0a
commit a5b985350a
131 changed files with 4933 additions and 194 deletions

View File

@@ -1,3 +1,5 @@
using UnityEngine;
namespace Hallucinate.GameSetup.Maze
{
/// <summary>
@@ -12,7 +14,26 @@ namespace Hallucinate.GameSetup.Maze
Path, // Temporary path (e.g., Wilson's crawler)
Start, // Entry point
End, // Exit point
StairUp,
StairDown
StairsUp,
StairsDown
}
public enum PieceType
{
None,
Wall,
Vertical_Straight, // Đường thẳng dọc
Horizontal_Straight, // Đường thẳng ngang
Corner, // Góc cua
T_Junction, // Ngã ba
Crossroads, // Ngã tư
Stairs,
StairsUp// Cầu thang (Điểm nối)
}
public struct MazePieceData
{
public PieceType piece; // Hình dạng mảnh ghép
// Bạn có thể thêm rotation nếu cần xoay hướng model sau này
public int rotation;
public GameObject model;
}
}

View File

@@ -9,9 +9,11 @@ namespace Hallucinate.GameSetup.Maze
/// </summary>
public class MazeGrid
{
public int Width { get; private set; }
public int Depth { get; private set; }
public int Width { get; set; }
public int Depth { get; set; }
public int Level { get; set; }
public MazePieceData[,] piecePlace;
public float scale = 1f;
private readonly MazeCellType[,] _cells;
@@ -25,12 +27,20 @@ namespace Hallucinate.GameSetup.Maze
{
Width = width;
Depth = depth;
piecePlace = new MazePieceData[width, 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;
piecePlace[x, z].piece = PieceType.Wall;
}
}
}
public void SetCell(int x, int z, MazeCellType type)

View File

@@ -5,43 +5,34 @@ using UnityEngine;
namespace Hallucinate.GameSetup.Maze
{
/// <summary>
/// Central controller for the Maze system.
/// Manages algorithm selection, debug speed, and regeneration.
/// Central controller for the Multi-floor Maze system.
/// Handles initialization order and cross-floor connections.
/// </summary>
public class MazeManager : MonoBehaviour
{
public enum AlgorithmType { Recursive, Wilsons, Prims, Crawler }
[Header("System Settings")]
public MazeGrid[] mazes;
public float floorHeight = 3.5f;
[Header("System Settings")]
[Tooltip("Số lượng tầng mê cung cần sinh ra")]
[Range(1, 10)] public int floorCount = 2;
public float floorHeight = 4.0f;
public int connectionsPerFloor = 2;
[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("Grid Settings")]
[SerializeField] private AlgorithmType selectedAlgorithm;
[SerializeField] private int width = 20;
[SerializeField] private int depth = 20;
[Header("References")]
[SerializeField] private MazeRenderer mazeRenderer;
[SerializeField] private MazeRenderer rendererPrefab;
[SerializeField] private Transform mazeContainer;
[Header("Corridor Setting")]
public GameObject straightManHoleLadder;
public GameObject straightManHoleUp;
public GameObject deadendManHoleLadder;
public GameObject deadendManHoleUp;
private MazeGrid _grid;
private Coroutine _generationCoroutine;
private List<MazeGrid> _mazeFloors = new List<MazeGrid>();
private List<MazeRenderer> _activeRenderers = new List<MazeRenderer>();
private void Start()
{
Debug.Log("--- Tớ là: " + gameObject.name + " đang gọi Regenerate! ---");
Regenerate();
}
@@ -53,100 +44,117 @@ namespace Hallucinate.GameSetup.Maze
}
}
[ContextMenu("Regenerate")]
//[ContextMenu("Regenerate")]
public void Regenerate()
{
// Bước 1: Khởi tạo tất cả các tầng mê cung
for (int i = 0; i < mazes.Length; i++)
StopAllCoroutines();
ClearExistingMaze();
// 1. Khởi tạo dữ liệu các tầng (Logic Data)
for (int i = 0; i < floorCount; i++)
{
// mazes[i].width = width;
// mazes[i].depth = depth;
// mazes[i].level = i;
// mazes[i].Build(); // Lưu ý: Hàm Build này của bạn nên được tối ưu để KHÔNG Instantiate Model vội.
// FIX: Khởi tạo Object trước khi gán thuộc tính
MazeGrid newFloor = new MazeGrid(width, depth);
newFloor.Level = i;
// 2. Chạy thuật toán sinh mê cung cho tầng này (Sync để đảm bảo có dữ liệu trước khi nối tầng)
IMazeAlgorithm algorithm = GetAlgorithm(selectedAlgorithm);
algorithm.Generate(newFloor);
_mazeFloors.Add(newFloor);
}
// Bước 2: Tạo điểm nối giữa các cặp tầng (0->1, 1->2, 2->3...)
for (int i = 0; i < mazes.Length - 1; i++)
// 3. Tạo điểm nối cầu thang (Stairs) giữa các tầng kề nhau
ConnectFloors();
// 4. Hiển thị 3D cho tất cả các tầng
RenderAllFloors();
}
private void ConnectFloors()
{
if (_mazeFloors.Count < 2) return;
for (int i = 0; i < _mazeFloors.Count - 1; i++)
{
MazeGrid currentFloor = mazes[i];
MazeGrid nextFloor = mazes[i + 1];
MazeGrid currentFloor = _mazeFloors[i];
MazeGrid nextFloor = _mazeFloors[i + 1];
// 2.1: Quét tìm TẤT CẢ các tọa độ (x, z) có thể làm điểm nối
List<Vector2Int> possibleConnections = new List<Vector2Int>();
List<Vector2Int> overlapPaths = new List<Vector2Int>();
for (int z = 0; z < depth; z++)
// Tìm các tọa độ (x, z) mà cả 2 tầng đều là đường đi (Corridor)
for (int z = 1; z < depth - 1; z++)
{
for (int x = 0; x < width; x++)
for (int x = 1; x < width - 1; x++)
{
// Nếu cả Tầng Dưới và Tầng Trên đều đang là đường đi (Path) ở tọa độ này
// (Bạn cần đổi điều kiện này cho khớp với Enum/Class thực tế của bạn)
// bool isCurrentFloorPath =
// currentFloor.piecePlace[x, z].piece == MazeGrid.PieceType.Vertical_Straight;
// bool isNextFloorPath = nextFloor.piecePlace[x, z].piece == MazeGrid.PieceType.Vertical_Straight;
//
// if (isCurrentFloorPath && isNextFloorPath)
// {
// possibleConnections.Add(new Vector2Int(x, z));
// }
if (currentFloor.GetCell(x, z) == MazeCellType.Corridor &&
nextFloor.GetCell(x, z) == MazeCellType.Corridor)
{
overlapPaths.Add(new Vector2Int(x, z));
}
}
}
// 2.2: Chọn ngẫu nhiên N điểm từ danh sách để làm thang nối
int connectionsMade = 0;
// Trộn ngẫu nhiên danh sách để điểm nối rải rác
ShuffleList(overlapPaths);
// Trộn ngẫu nhiên danh sách (Shuffle) để các điểm nối không bị dồn về 1 góc
ShuffleList(possibleConnections);
foreach (Vector2Int pos in possibleConnections)
int actualConnections = Mathf.Min(connectionsPerFloor, overlapPaths.Count);
for (int j = 0; j < actualConnections; j++)
{
if (connectionsMade >= connectionsPerFloor) break; // Đã đủ số lượng đường lên thì dừng
int x = pos.x;
int z = pos.y;
// // Xóa model đường đi cũ (nếu hàm Build() đã lỡ Instantiate)
// if (currentFloor.piecePlace[x, z].model != null) Destroy(currentFloor.piecePlace[x, z].model);
// if (nextFloor.piecePlace[x, z].model != null) Destroy(nextFloor.piecePlace[x, z].model);
//
// // TÍNH TOẠ ĐỘ Y CHUẨN XÁC: Y = Tầng * Độ_Cao_Tầng
// Vector3 upManHolePos = new Vector3(x * currentFloor.scale, currentFloor.level * floorHeight,
// z * currentFloor.scale);
// Vector3 ladderManPos = new Vector3(x * nextFloor.scale, nextFloor.level * floorHeight,
// z * nextFloor.scale);
// // Sinh Model mới tại điểm đã chốt
// currentFloor.piecePlace[x, z].model =
// Instantiate(straightManHoleUp, upManHolePos, Quaternion.identity);
// nextFloor.piecePlace[x, z].model =
// Instantiate(straightManHoleLadder, ladderManPos, Quaternion.identity);
// Cập nhật loại dữ liệu để hệ thống ghi nhận đây là ô cầu thang
// currentFloor.piecePlace[x, z].piece = Maze.PieceType.StairsUp; (Nếu bạn có enum này)
connectionsMade++;
Vector2Int pos = overlapPaths[j];
// Đánh dấu logic để Renderer tự chọn Prefab cầu thang từ VisualProfile
currentFloor.SetCell(pos.x, pos.y, MazeCellType.StairsUp);
nextFloor.SetCell(pos.x, pos.y, MazeCellType.StairsDown);
}
}
}
if (_generationCoroutine != null)
private void RenderAllFloors()
{
Debug.Log("--- Đang chạy hàm này! ---");
for (int i = 0; i < _mazeFloors.Count; i++)
{
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);
if (_activeRenderers.Count > 100) {
Debug.LogError("DỪNG LẠI! Quá nhiều tầng rồi!");
return;
}
// Tạo một Renderer instance cho mỗi tầng
MazeRenderer floorRenderer = Instantiate(rendererPrefab, mazeContainer);
floorRenderer.gameObject.name = $"Floor_Renderer_{i}";
// Đặt vị trí Y của tầng
floorRenderer.transform.localPosition = new Vector3(0, i * floorHeight, 0);
// Khởi tạo hiển thị cho Grid tương ứng
floorRenderer.Initialize(_mazeFloors[i], floorRenderer.transform);
_activeRenderers.Add(floorRenderer);
}
}
private void ClearExistingMaze()
{
foreach (var renderer in _activeRenderers)
{
if (renderer != null)
{
renderer.Clear();
Destroy(renderer.gameObject);
}
}
_activeRenderers.Clear();
_mazeFloors.Clear();
// Xóa sạch các object con cũ trong container (nếu có)
if (mazeContainer != null)
{
foreach (Transform child in mazeContainer)
{
Destroy(child.gameObject);
}
}
}
private void ShuffleList<T>(List<T> list)
{
for (int i = 0; i < list.Count; i++)
@@ -170,4 +178,4 @@ namespace Hallucinate.GameSetup.Maze
};
}
}
}
}

View File

@@ -113,9 +113,9 @@ namespace Hallucinate.GameSetup.Maze
{
_currentGrid = grid;
_container = container;
grid.OnCellChanged += HandleCellChanged;
// Initial render
// ĐỪNG đăng ký cái này vội: grid.OnCellChanged += HandleCellChanged;
for (int z = 0; z < grid.Depth; z++)
{
for (int x = 0; x < grid.Width; x++)
@@ -123,6 +123,9 @@ namespace Hallucinate.GameSetup.Maze
UpdateCellVisual(x, z, grid.GetCell(x, z), false);
}
}
// ĐĂNG KÝ Ở ĐÂY: Sau khi đã vẽ xong hết rồi mới nghe ngóng thay đổi
grid.OnCellChanged += HandleCellChanged;
}
public void Clear()
@@ -164,15 +167,20 @@ namespace Hallucinate.GameSetup.Maze
{
Vector2Int pos = new Vector2Int(x, z);
// 1. DÙNG DestroyImmediate ĐỂ XÓA THẬT SỰ TRƯỚC KHI TẠO MỚI
if (_spawnedCells.TryGetValue(pos, out GameObject oldObj))
{
Destroy(oldObj);
if (oldObj != null)
{
DestroyImmediate(oldObj);
}
_spawnedCells.Remove(pos);
}
GameObject prefab = null;
Quaternion rotation = Quaternion.identity;
// Logic xét duyệt loại prefab
if (type == MazeCellType.Corridor || type == MazeCellType.Processing)
{
(prefab, rotation) = GetCorridorPrefabAndRotation(x, z);
@@ -185,7 +193,7 @@ namespace Hallucinate.GameSetup.Maze
if (prefab == null) return;
float safeScale = Mathf.Max(0.001f, visualProfile.scale);
float modelScaleMultiplier = 0.25f;
float modelScaleMultiplier = 0.167f;
Vector3 localPos = new Vector3(x * safeScale, 0, z * safeScale);

View File

@@ -17,6 +17,7 @@ namespace Hallucinate.GameSetup.Maze
public GameObject pathPrefab;
public GameObject startPrefab;
public GameObject endPrefab;
public GameObject stairsUp;
[Header("Room Pieces")]
public GameObject wallpiece;
@@ -44,6 +45,7 @@ namespace Hallucinate.GameSetup.Maze
MazeCellType.Path => pathPrefab,
MazeCellType.Start => startPrefab,
MazeCellType.End => endPrefab,
MazeCellType.StairsUp => stairsUp,
_ => null
};
}