using System.Collections.Generic; using UnityEngine; using System.Linq; using System.Runtime.CompilerServices; public class MapGenManager : MonoBehaviour { /* ------------------ INSPECTOR FIELDS ------------------ */ [Header("Room Prefabs")] [SerializeField] private List mapPrefab = new(); [Header("Player")] [SerializeField] private GameObject Player; [Header("Corridor Prefabs")] [SerializeField] private GameObject CorridorStraight; [SerializeField] private GameObject CorridorStraightUnlit; [SerializeField] private GameObject DoorCorridor; [Header("Layout")] [SerializeField] private MapLayout layout; [Header("Generation Settings")] [SerializeField] private int RoomDistance = 3; private readonly Dictionary gridToRoom = new(); private readonly Vector3 roomOriginOffset = Vector3.zero; void Start() => GenerateFromLayout(); private void GenerateFromLayout() { if (layout == null || string.IsNullOrWhiteSpace(layout.grid)) { Debug.LogError("Layout asset není přiřazený nebo je prázdný!"); return; } gridToRoom.Clear(); /* ----------- Create a spawn room ----------- */ GameObject spawnPrefab = mapPrefab[0]; Vector3 cellSize = GetPrefabXZ(spawnPrefab); /* ---------- Text layout to grid ---------- */ string[] lines = layout.grid .Split('\n') .Select(line => line.TrimEnd('\r')) .Where(line => !string.IsNullOrWhiteSpace(line)) .ToArray(); Vector2 firstRoomPos = GetFirstOrLastRoom(lines); int bottomRowIdx = (int)firstRoomPos.y; int spawnGridX = (int)firstRoomPos.x; /* ---------- Create spawn room properly ---------- */ Vector3 spawnPos = roomOriginOffset + new Vector3( spawnGridX * (cellSize.x + RoomDistance), // X 0, 0); GameObject spawnRoom = Instantiate(spawnPrefab, spawnPos, Quaternion.identity, transform); gridToRoom[new Vector2Int(spawnGridX, 0)] = spawnRoom; /* ---------- Instantiate player ---------- */ if (Player) { Vector3 playerPos = spawnPos + new Vector3(0, 1, 3); Instantiate(Player, playerPos, Quaternion.identity, transform); } /* ---------- Build the rest of rooms ---------- */ BuildRooms(lines, bottomRowIdx, cellSize); /* ---------- Open walls based on aproximity to other rooms ---------- */ foreach (var keyValuePair in gridToRoom) { Vector2Int g = keyValuePair.Key; RoomHandler rh = keyValuePair.Value.GetComponent(); bool north = gridToRoom.ContainsKey(g + Vector2Int.left); bool south = gridToRoom.ContainsKey(g + Vector2Int.right); bool east = gridToRoom.ContainsKey(g + Vector2Int.up); bool west = gridToRoom.ContainsKey(g + Vector2Int.down); rh.SetEntrances(northOpen: north, southOpen: south, eastOpen: east, westOpen: west); } /* ---------- 5) CHODBY ---------- */ BuildCorridors(); } /// /// Build corridors between rooms /// /// private void BuildCorridors() { float straightZ = CorridorStraight.GetComponent().prefabSize.y; float straightX = CorridorStraight.GetComponent().prefabSize.x; Vector2Int[] stepDirs = { Vector2Int.right, Vector2Int.up }; foreach (var kv in gridToRoom) { Vector2Int cell = kv.Key; GameObject roomA = kv.Value; foreach (Vector2Int dir in stepDirs) { Vector2Int nKey = cell + dir; if (!gridToRoom.TryGetValue(nKey, out GameObject roomB)) continue; Vector3 axis, axisNeg; float halfA, halfB; Quaternion rot; if (dir == Vector2Int.right) { axis = Vector3.right; axisNeg = Vector3.left; halfA = GetPrefabXZ(roomA).x * 0.5f; halfB = GetPrefabXZ(roomB).x * 0.5f; rot = Quaternion.Euler(0, 90, 0); } else { axis = Vector3.forward; axisNeg = Vector3.back; halfA = GetPrefabXZ(roomA).z * 0.5f; halfB = GetPrefabXZ(roomB).z * 0.5f; rot = Quaternion.identity; } Vector3 start = roomA.transform.position + axis * halfA; Vector3 end = roomB.transform.position + axisNeg * halfB; CreateStraightCorridor(start, end, axis, rot, straightZ, straightX); } } } /// /// Build all rooms based on the layout grid /// /// /// /// private void BuildRooms(string[] lines, int bottomRowIdx, Vector3 cellSize) { int rows = lines.Length; for (int rowsIter = 0; rowsIter < rows; rowsIter++) { string line = lines[rowsIter]; int gridZ = bottomRowIdx - rowsIter + 1; for (int x = 0; x < line.Length; x++) { char ch = line[x]; if (ch == '-') continue; if (!char.IsDigit(ch)) { Debug.LogWarning($"Neznámý znak '{ch}' v layoutu – ignoruji."); continue; } int idx = ch - '0'; if (idx >= mapPrefab.Count) { Debug.LogWarning($"Index {idx} mimo rozsah mapPrefab!"); continue; } GameObject prefab = mapPrefab[idx]; Vector3 worldPos = roomOriginOffset + new Vector3( x * (cellSize.x + RoomDistance), 0, gridZ * (cellSize.z + RoomDistance)); GameObject room = Instantiate(prefab, worldPos, Quaternion.identity, transform); gridToRoom[new Vector2Int(x, gridZ)] = room; } } } /// /// Returns the (x, y) coordinates of the room with closest aproximity to spawn or the furthest aproximity to spawn /// /// /// /// Vector2(x, y) where x is the line index and y is the column index private Vector2 GetFirstOrLastRoom(string[] lines, bool last = false) { int colIdx = -1; int spawnGridX = 0; int rows = lines.Length; for (int rowsIter = rows - 1; rowsIter >= 0; rowsIter--) { string line = lines[rowsIter]; for (int c = 0; c < line.Length; c++) { if (char.IsDigit(line[c])) { colIdx = rowsIter; spawnGridX = c; break; } } if (colIdx != -1) break; } if (colIdx == -1) { Debug.LogError("Layout neobsahuje žádnou číslici (místnost)!"); return Vector2.zero; } return new Vector2(spawnGridX, colIdx); } private void CreateStraightCorridor( Vector3 start, Vector3 end, Vector3 axis, Quaternion rot, float stepLenZ, float stepLenX) { float dist = Vector3.Distance(start, end); float step = Mathf.Approximately(Mathf.Abs(axis.z), 1f) ? stepLenZ : stepLenX; int count = Mathf.Max(1, Mathf.FloorToInt(dist / step)); Vector3 segPos = start + axis * (step * 0.5f); var door = Instantiate(DoorCorridor, segPos, rot, transform); door.GetComponent()?.OpenDoor(); for (int i = 1; i < count; i++) { GameObject prefab = (i % 2 == 0) ? CorridorStraight : CorridorStraightUnlit; segPos = start + axis * (i * step + step * 0.5f); Instantiate(prefab, segPos, rot, transform); } } /// /// Returns the size of a prefab room /// /// Choose a prefab from available ones /// Size of the prefab as Vector3 private static Vector3 GetPrefabXZ(GameObject prefab) { var ps = prefab.GetComponent(); return ps ? new Vector3(ps.prefabSize.x, 0, ps.prefabSize.y) : new Vector3(10, 0, 10); } }