diff --git a/3D blobici/Assets/Scripts/MapGen/MapGenManager.cs b/3D blobici/Assets/Scripts/MapGen/MapGenManager.cs index b153193..3906ed1 100644 --- a/3D blobici/Assets/Scripts/MapGen/MapGenManager.cs +++ b/3D blobici/Assets/Scripts/MapGen/MapGenManager.cs @@ -1,11 +1,13 @@ 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 List(); + [SerializeField] private List mapPrefab = new(); [Header("Player")] [SerializeField] private GameObject Player; @@ -21,12 +23,13 @@ public class MapGenManager : MonoBehaviour [Header("Generation Settings")] [SerializeField] private int RoomDistance = 3; - private Dictionary gridToRoom = new(); - + private readonly Dictionary gridToRoom = new(); private readonly Vector3 roomOriginOffset = Vector3.zero; + void Start() => GenerateFromLayout(); + private void GenerateFromLayout() { if (layout == null || string.IsNullOrWhiteSpace(layout.grid)) @@ -37,167 +40,216 @@ public class MapGenManager : MonoBehaviour gridToRoom.Clear(); - /* ---------- 0) VYTVOŘÍME SPAWN-ROOM ---------- */ + /* ----------- Create a spawn room ----------- */ GameObject spawnPrefab = mapPrefab[0]; - PrefabSize spawnSizeComp = spawnPrefab.GetComponent(); - Vector3 cellSize = spawnSizeComp != null - ? new Vector3(spawnSizeComp.prefabSize.x, 0, spawnSizeComp.prefabSize.y) - : new Vector3(10, 0, 10); // fallback + 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); - Vector3 spawnPos = roomOriginOffset; GameObject spawnRoom = Instantiate(spawnPrefab, spawnPos, Quaternion.identity, transform); - gridToRoom[new Vector2Int(0, 0)] = spawnRoom; + gridToRoom[new Vector2Int(spawnGridX, 0)] = spawnRoom; - // hráče necháme na spawnu - if (Player != null) + /* ---------- Instantiate player ---------- */ + if (Player) { Vector3 playerPos = spawnPos + new Vector3(0, 1, 3); Instantiate(Player, playerPos, Quaternion.identity, transform); } - /* ---------- 1) PARSOVÁNÍ LAYOUTU ---------- */ - string[] lines = layout.grid - .Split('\n') - .Select(l => l.TrimEnd('\r')) - .Where(l => !string.IsNullOrEmpty(l)) - .ToArray(); + /* ---------- Build the rest of rooms ---------- */ - int rows = lines.Length; + BuildRooms(lines, bottomRowIdx, cellSize); - /* ---------- 2) SMYČKA PŘES GRID ------------- */ - for (int z = 0; z < rows; z++) + /* ---------- Open walls based on aproximity to other rooms ---------- */ + foreach (var keyValuePair in gridToRoom) { - string line = lines[rows - 1 - z]; // dolní řádek layoutu = nejnižší Z v textu - for (int x = 0; x < line.Length; x++) - { - char c = line[x]; - if (c == '-') continue; // prázdné místo - - if (!char.IsDigit(c)) - { - Debug.LogWarning($"Neznámý znak {c} v layoutu – ignorováno."); - continue; - } - - int index = c - '0'; - if (index >= mapPrefab.Count) - { - Debug.LogWarning($"Index {index} mimo rozsah mapPrefab!"); - continue; - } - - GameObject prefab = mapPrefab[index]; - - PrefabSize roomSize = prefab.GetComponent(); - Vector3 rCell = roomSize != null - ? new Vector3(roomSize.prefabSize.x, 0, roomSize.prefabSize.y) - : cellSize; // použij spawn-size jako fallback - - Vector3 worldPos = roomOriginOffset + new Vector3( - x * (cellSize.x + RoomDistance), - 0, - (z + 1) * (cellSize.z + RoomDistance)); - - GameObject room = Instantiate(prefab, worldPos, Quaternion.identity, transform); - gridToRoom[new Vector2Int(x, z + 1)] = room; - } - } - - /* ---------- 3) OTEVŘEME ZDI VŠEM MÍSTNOSTEM ---------- */ - foreach (var kvp in gridToRoom) - { - Vector2Int g = kvp.Key; - RoomHandler rh = kvp.Value.GetComponent(); + 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); + rh.SetEntrances(northOpen: north, southOpen: south, eastOpen: east, westOpen: west); } + /* ---------- 5) CHODBY ---------- */ BuildCorridors(); } + /// + /// Build corridors between rooms + /// + /// private void BuildCorridors() { - // velikosti přímých dílů - float straightLengthZ = CorridorStraight.GetComponent().prefabSize.y; - float straightLengthX = CorridorStraight.GetComponent().prefabSize.x; - - // Abychom každý pár řešili jen jednou, projdeme jen Right a Up souseda + float straightZ = CorridorStraight.GetComponent().prefabSize.y; + float straightX = CorridorStraight.GetComponent().prefabSize.x; Vector2Int[] stepDirs = { Vector2Int.right, Vector2Int.up }; - foreach (var kvp in gridToRoom) + foreach (var kv in gridToRoom) { - Vector2Int cell = kvp.Key; - GameObject roomA = kvp.Value; + Vector2Int cell = kv.Key; + GameObject roomA = kv.Value; foreach (Vector2Int dir in stepDirs) { - Vector2Int neighKey = cell + dir; - if (!gridToRoom.TryGetValue(neighKey, out GameObject roomB)) - continue; // není soused – žádná chodba + Vector2Int nKey = cell + dir; + if (!gridToRoom.TryGetValue(nKey, out GameObject roomB)) continue; - // 1) Urči směr v prostoru - Vector3 axis, axisNeg; // +směr a −směr podél stejné osy - float halfA, halfB; // půl šířky/hloubky pro start/end bod - Quaternion corridorRot; // natočení pro koridor + Vector3 axis, axisNeg; + float halfA, halfB; + Quaternion rot; - if (dir == Vector2Int.right) // grid +X (svět +X) + if (dir == Vector2Int.right) { - axis = Vector3.right; - axisNeg = Vector3.left; - halfA = roomA.GetComponent().prefabSize.x * 0.5f; - halfB = roomB.GetComponent().prefabSize.x * 0.5f; - corridorRot = Quaternion.Euler(0, 90, 0); // koridor leží po X + 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 // dir == Vector2Int.up → svět +Z + else { - axis = Vector3.forward; - axisNeg = Vector3.back; - halfA = roomA.GetComponent().prefabSize.y * 0.5f; - halfB = roomB.GetComponent().prefabSize.y * 0.5f; - corridorRot = Quaternion.identity; // prefab míří po +Z + axis = Vector3.forward; axisNeg = Vector3.back; + halfA = GetPrefabXZ(roomA).z * 0.5f; + halfB = GetPrefabXZ(roomB).z * 0.5f; + rot = Quaternion.identity; } - // 2) Vypočti start a end body (středy zdí) - Vector3 startPos = roomA.transform.position + axis * halfA; - Vector3 endPos = roomB.transform.position + axisNeg * halfB; + Vector3 start = roomA.transform.position + axis * halfA; + Vector3 end = roomB.transform.position + axisNeg * halfB; - // 3) Vytvoř sekvenci dílů - CreateStraightCorridor(startPos, endPos, axis, corridorRot, - straightLengthZ, straightLengthX); + CreateStraightCorridor(start, end, axis, rot, straightZ, straightX); } } } - /// Vytvoří přímý koridor od startu k endu podél 'axis'. + + /// + /// 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 fullDist = Vector3.Distance(start, end); - float stepLen = Mathf.Approximately(Mathf.Abs(axis.z), 1f) ? stepLenZ : 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)); - int segmentCount = Mathf.Max(1, Mathf.FloorToInt(fullDist / stepLen)); + Vector3 segPos = start + axis * (step * 0.5f); + var door = Instantiate(DoorCorridor, segPos, rot, transform); + door.GetComponent()?.OpenDoor(); - // 0) dveřní díl - Vector3 segPos = start + axis * (stepLen * 0.5f); - GameObject door = Instantiate(DoorCorridor, segPos, rot, transform); - door.GetComponent()?.OpenDoor(); // pokud existuje - - // 1) zbytek – střídavě osvětlené / neosvětlené - for (int i = 1; i < segmentCount; i++) + for (int i = 1; i < count; i++) { GameObject prefab = (i % 2 == 0) ? CorridorStraight : CorridorStraightUnlit; - - segPos = start + axis * (i * stepLen + stepLen * 0.5f); + segPos = start + axis * (i * step + step * 0.5f); Instantiate(prefab, segPos, rot, transform); } } -} \ No newline at end of file + + /// + /// 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); + } +}