using System; using System.Collections; using System.Collections.Generic; using System.Linq; using Unity.AI.Navigation; using UnityEngine; using UnityEngine.AI; 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("NavMesh Settings")] [SerializeField] private bool useGlobalNavMesh = true; private NavMeshSurface globalNavMeshSurface; [Header("Generation Settings")] [SerializeField] private int RoomDistance = 3; private readonly Dictionary gridToRoom = new(); public event Action OnGenerationComplete; public IReadOnlyDictionary GridToRoom => gridToRoom; void Start() => StartCoroutine(GenerateWithDelay()); private IEnumerator GenerateWithDelay() { yield return null; // Počkej jeden frame 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); string[] lines = layout.grid.Split('\n') .Select(l => l.TrimEnd('\r')) .Where(l => !string.IsNullOrWhiteSpace(l)) .ToArray(); Vector2 first = GetFirstOrLastRoom(lines); int bottomRow = (int)first.y; int spawnX = (int)first.x; Vector3 spawnPos = new Vector3(spawnX * (cellSize.x + RoomDistance), 0, 0); GameObject spawnRoom = Instantiate(spawnPrefab, spawnPos, Quaternion.identity, transform); gridToRoom[new Vector2Int(spawnX, 0)] = spawnRoom; /* ----------- Spawn the player ----------- */ if (Player) { GameObject p = Instantiate(Player, spawnPos + new Vector3(0, 1, 3), Quaternion.identity, transform); Transform inner = p.transform.Find("Player"); if (inner) inner.tag = "Player"; } /* ----------- Build rooms ----------- */ BuildRooms(lines, bottomRow, cellSize); // Použij coroutine pro dokončení generování StartCoroutine(FinishGenerationAfterDelay()); } /// /// Builds corridors and doors between rooms. /// private void BuildCorridors() { // Jediné dva směry, které musíme zkontrolovat z každé místnosti (right/up) Vector2Int[] directions = { Vector2Int.right, Vector2Int.up }; // Délky prefebů – podél X a Z osy float straightLenX = CorridorStraight.GetComponent().prefabSize.x; float straightLenZ = CorridorStraight.GetComponent().prefabSize.y; float doorLenX = DoorCorridor.GetComponent().prefabSize.x; float doorLenZ = DoorCorridor.GetComponent().prefabSize.y; foreach (var kv in gridToRoom) { Vector2Int cell = kv.Key; GameObject roomA = kv.Value; foreach (Vector2Int dir in directions) { Vector2Int nKey = cell + dir; if (!gridToRoom.TryGetValue(nKey, out GameObject roomB)) continue; // Handle geometry Vector3 axis; float lenStraight, lenDoor; Quaternion rot; if (dir == Vector2Int.right) { axis = Vector3.right; lenStraight = straightLenX; lenDoor = doorLenX; rot = Quaternion.Euler(0, 90, 0); } else // Vector2Int.up { axis = Vector3.forward; lenStraight = straightLenZ; lenDoor = doorLenZ; rot = Quaternion.identity; } // Wall calculation float halfA = Vector3.Scale(GetPrefabXZ(roomA), axis).magnitude * 0.5f; float halfB = Vector3.Scale(GetPrefabXZ(roomB), axis).magnitude * 0.5f; Vector3 wallA = roomA.transform.position + axis * halfA; Vector3 wallB = roomB.transform.position - axis * halfB; // Doors Vector3 doorPos = (wallA + wallB) * 0.5f; GameObject doorGO = Instantiate(DoorCorridor, doorPos, rot, transform); DoorAnimation anim = doorGO.GetComponent(); // Register the corridor to both rooms RoomHandler rhA = roomA.GetComponent(); RoomHandler rhB = roomB.GetComponent(); if (dir == Vector2Int.right) { rhA.RegisterDoor(RoomHandler.Side.East, anim); rhB.RegisterDoor(RoomHandler.Side.West, anim); } else { rhA.RegisterDoor(RoomHandler.Side.North, anim); rhB.RegisterDoor(RoomHandler.Side.South, anim); } // ROVNÉ SEGMENTY z obou stran dveří Vector3 doorEdgeA = doorPos - axis * (lenDoor * 0.5f); Vector3 doorEdgeB = doorPos + axis * (lenDoor * 0.5f); PlaceStraightSegments(doorEdgeA, wallA, -axis, rot, lenStraight); PlaceStraightSegments(doorEdgeB, wallB, axis, rot, lenStraight); } } } /// /// Vyplní úsek mezi startEdge (hrana dveří nebo předchozího dílu) /// a wallEdge (vnější hrana stěny místnosti) rovnými segmenty tak, /// aby žádný segment nepřečníval do stěny. /// private void PlaceStraightSegments( Vector3 startEdge, Vector3 wallEdge, Vector3 direction, Quaternion rot, float len) { const float EPS = 0.01f; float dist = Vector3.Distance(startEdge, wallEdge); if (dist < EPS) return; int fullCount = Mathf.FloorToInt(dist / len); float remainder = dist - fullCount * len; // Full segments Vector3 firstPivot = startEdge + direction * (len * 0.5f); for (int i = 0; i < fullCount; i++) { Vector3 pos = firstPivot + direction * (i * len); GameObject prefab = (i % 2 == 0) ? CorridorStraight : CorridorStraightUnlit; Instantiate(prefab, pos, rot, transform); } // Short segment to fill the gap if (remainder > EPS) { Vector3 remPivot = wallEdge - direction * (remainder * 0.5f); GameObject last = Instantiate(CorridorStraightUnlit, remPivot, rot, transform); Vector3 sc = last.transform.localScale; sc.z *= remainder / len; last.transform.localScale = sc; } } /* ============================================================ */ /* ROOMS */ /* ============================================================ */ private void BuildRooms(string[] lines, int bottomRowIdx, Vector3 cellSize) { for (int r = 0; r < lines.Length; r++) { string line = lines[r]; int gridZ = bottomRowIdx - r + 1; for (int x = 0; x < line.Length; x++) { char ch = line[x]; if (ch == '-' || !char.IsDigit(ch)) continue; int idx = ch - '0'; if (idx >= mapPrefab.Count) continue; Vector3 pos = new Vector3(x * (cellSize.x + RoomDistance), 0, gridZ * (cellSize.z + RoomDistance)); GameObject room = Instantiate(mapPrefab[idx], pos, Quaternion.identity, transform); gridToRoom[new Vector2Int(x, gridZ)] = room; } } } private IEnumerator FinishGenerationAfterDelay() { yield return new WaitForSeconds(0.5f); /* ----------- Set entrances ----------- */ foreach (var kv in gridToRoom) { Vector2Int g = kv.Key; RoomHandler rh = kv.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(north, south, east, west); } /* ----------- Build corridors ----------- */ BuildCorridors(); /* ----------- Toggle all doors ----------- */ foreach (var keyValuePair in gridToRoom) { RoomHandler rh = keyValuePair.Value.GetComponent(); rh.ToggleAllDoors(); } // Vytvoř globální NavMesh if (useGlobalNavMesh) { yield return new WaitForSeconds(0.2f); CreateGlobalNavMesh(); } OnGenerationComplete?.Invoke(this); } private void CreateGlobalNavMesh() { // Odstraň všechny existující NavMeshSurface z místností foreach (var room in gridToRoom.Values) { NavMeshSurface roomSurface = room.GetComponent(); if (roomSurface != null) { Destroy(roomSurface); } } // Vytvoř globální NavMeshSurface globalNavMeshSurface = gameObject.AddComponent(); globalNavMeshSurface.collectObjects = CollectObjects.Children; globalNavMeshSurface.useGeometry = NavMeshCollectGeometry.PhysicsColliders; // Bake globální NavMesh globalNavMeshSurface.BuildNavMesh(); } /* ============================================================ */ /* HELPERS */ /* ============================================================ */ 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); } private Vector2 GetFirstOrLastRoom(string[] lines) { for (int r = lines.Length - 1; r >= 0; r--) { string l = lines[r]; int c = l.ToCharArray().ToList().FindIndex(char.IsDigit); if (c != -1) return new Vector2(c, r); } Debug.LogError("Layout neobsahuje žádnou číslici (místnost)!"); return Vector2.zero; } }