diff --git a/3D blobici/Assets/Scenes/GenTest.unity b/3D blobici/Assets/Scenes/GenTest.unity index c38f2ca..75a0e9c 100644 --- a/3D blobici/Assets/Scenes/GenTest.unity +++ b/3D blobici/Assets/Scenes/GenTest.unity @@ -128,8 +128,8 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 23489964} - - component: {fileID: 23489963} - component: {fileID: 23489965} + - component: {fileID: 23489966} m_Layer: 0 m_Name: Dungeon m_TagString: Untagged @@ -137,32 +137,6 @@ GameObject: m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!114 &23489963 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 23489962} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: fba07e5a285f4d84f93955f44f02c76c, type: 3} - m_Name: - m_EditorClassIdentifier: - mapPrefab: - - {fileID: 819094401162878122, guid: 03f2147e5a186fc408b959faa2f97e86, type: 3} - - {fileID: 2783560673348224804, guid: b7111ccd107e56741a9790a50ab8e190, type: 3} - - {fileID: 7069902989848030098, guid: e8e0ba1faefe88f47b927a477b87ed22, type: 3} - - {fileID: 8524395191423973796, guid: 37b1623de3a4b8140bc4fd60f59c6329, type: 3} - - {fileID: 4362243528253867212, guid: 04cd18e800b8e854491a95529a91b3a1, type: 3} - - {fileID: 5061069654894918266, guid: 6ac586441811a834fbf148a96b779734, type: 3} - Player: {fileID: 6983871523237736218, guid: f0df263e5be65a041848d5a8bab85af1, type: 3} - CorridorStraight: {fileID: 8047827979703692770, guid: 1a5d554c0c76caf4195cae47e098b79d, type: 3} - CorridorStraightUnlit: {fileID: 2016417306107577256, guid: 92d9025262a022a499862d352c2724ee, type: 3} - DoorCorridor: {fileID: 5603417387143431118, guid: 9eccbf5a500a0d2429e6008a713a49fd, type: 3} - layout: {fileID: 11400000, guid: 8379cbdc96f21cc47a95cab23068d69a, type: 2} - useGlobalNavMesh: 1 - RoomDistance: 40 --- !u!4 &23489964 Transform: m_ObjectHideFlags: 0 @@ -210,6 +184,29 @@ MonoBehaviour: m_MinRegionArea: 2 m_NavMeshData: {fileID: 0} m_BuildHeightMesh: 0 +--- !u!114 &23489966 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 23489962} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fba07e5a285f4d84f93955f44f02c76c, type: 3} + m_Name: + m_EditorClassIdentifier: + cellSize: 1 + wallHeight: 5 + roomCountMin: 12 + roomCountMax: 16 + maxRoomSizeMin: 28 + maxRoomSizeMax: 40 + wallMaterial: {fileID: 0} + floorMaterial: {fileID: 0} + doorMaterial: {fileID: 0} + playerPrefab: {fileID: 6983871523237736218, guid: f0df263e5be65a041848d5a8bab85af1, type: 3} + generateOnStart: 1 --- !u!1 &1862002347 GameObject: m_ObjectHideFlags: 0 diff --git a/3D blobici/Assets/Scripts/MainGame/GameHandler.cs b/3D blobici/Assets/Scripts/MainGame/GameHandler.cs index ff8674e..73dd53c 100644 --- a/3D blobici/Assets/Scripts/MainGame/GameHandler.cs +++ b/3D blobici/Assets/Scripts/MainGame/GameHandler.cs @@ -10,9 +10,9 @@ public class GameHandler : MonoBehaviour void Awake() { - mapGen = GetComponent(); + //mapGen = GetComponent(); - mapGen.OnGenerationComplete += HandleStart; + //mapGen.OnGenerationComplete += HandleStart; } void Start() { @@ -25,14 +25,14 @@ public class GameHandler : MonoBehaviour } - private void HandleStart(MapGenManager map) - { - var Rooms = map.GridToRoom; + //private void HandleStart(MapGenManager map) + //{ + // var Rooms = map.GridToRoom; - /*----- For now only open the doors -----*/ - foreach (var room in mapGen.GridToRoom) - { - var rh = room.Value.GetComponent(); - } - } + // /*----- For now only open the doors -----*/ + // foreach (var room in mapGen.GridToRoom) + // { + // var rh = room.Value.GetComponent(); + // } + //} } diff --git a/3D blobici/Assets/Scripts/MapGen/MapGenManager.cs b/3D blobici/Assets/Scripts/MapGen/MapGenManager.cs index 570d5c4..5094a17 100644 --- a/3D blobici/Assets/Scripts/MapGen/MapGenManager.cs +++ b/3D blobici/Assets/Scripts/MapGen/MapGenManager.cs @@ -1,301 +1,516 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using Unity.AI.Navigation; +using System.Collections.Generic; using UnityEngine; -using UnityEngine.AI; public class MapGenManager : MonoBehaviour { - /* ------------------ INSPECTOR FIELDS ------------------ */ - [Header("Room Prefabs")] - [SerializeField] private List mapPrefab = new(); + [Header("Generátor")] + [Tooltip("Velikost jedné buňky matice v Unity units.")] + public float cellSize = 1f; - [Header("Player")] - [SerializeField] private GameObject Player; + [Tooltip("Výška zdi.")] + public float wallHeight = 1f; - [Header("Corridor Prefabs")] - [SerializeField] private GameObject CorridorStraight; - [SerializeField] private GameObject CorridorStraightUnlit; - [SerializeField] private GameObject DoorCorridor; + [Tooltip("Počet místností (rozsah).")] + public int roomCountMin = 10; + public int roomCountMax = 20; - [Header("Layout")] - [SerializeField] private MapLayout layout; + [Tooltip("Maximální velikost místnosti v buňkách (rozsah).")] + public int maxRoomSizeMin = 14; + public int maxRoomSizeMax = 20; - [Header("NavMesh Settings")] - [SerializeField] private bool useGlobalNavMesh = true; - private NavMeshSurface globalNavMeshSurface; + [Header("Materiály")] + public Material wallMaterial; + public Material floorMaterial; + public Material doorMaterial; - [Header("Generation Settings")] - [SerializeField] private int RoomDistance = 3; + [Header("Hráč")] + [Tooltip("Prefab hráče – bude spawnut uprostřed první místnosti.")] + public GameObject playerPrefab; - private readonly Dictionary gridToRoom = new(); - public event Action OnGenerationComplete; - public IReadOnlyDictionary GridToRoom => gridToRoom; + [Header("Chování")] + public bool generateOnStart = true; - void Start() => StartCoroutine(GenerateWithDelay()); + private int[][] genMatrix; + private int roomCount; + private int maxRoomSize; - private IEnumerator GenerateWithDelay() + private int MATRIX_SIZE_X = 10; + private int MATRIX_SIZE_Y = 10; + private const int ROOM_PADDING = 1; + + // Šířka chodby v buňkách (1 = původní, 3 = o jeden blok víc na každou stranu) + private const int CORRIDOR_WIDTH = 3; + + private System.Random rng = new System.Random(); + private List rooms = new List(); + + private GameObject wallsParent; + private GameObject floorParent; + private GameObject doorsParent; + + private const int TILE_EMPTY = 0; + private const int TILE_WALL = 1; + private const int TILE_DOOR = 2; + + private void Start() { - yield return null; // Počkej jeden frame - GenerateFromLayout(); + if (generateOnStart) + Generate(); } - private void GenerateFromLayout() + public void Generate() { - 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()); + ClearPrevious(); + RunGenerator(); + ConnectRooms(); + BuildMeshes(); + SpawnPlayer(); } - /// - /// Builds corridors and doors between rooms. - /// - private void BuildCorridors() + private void SpawnPlayer() { - // Jediné dva směry, které musíme zkontrolovat z každé místnosti (right/up) - Vector2Int[] directions = { Vector2Int.right, Vector2Int.up }; + if (playerPrefab == null || rooms.Count == 0) return; - // 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; + Room spawnRoom = rooms[0]; + float centerCol = (spawnRoom.start_x + spawnRoom.end_x) / 2f; + float centerRow = (spawnRoom.start_y + spawnRoom.end_y) / 2f; - foreach (var kv in gridToRoom) + Vector3 spawnPos = new Vector3(centerCol * cellSize, 1f, centerRow * cellSize); + GameObject player = Instantiate(playerPrefab, spawnPos, Quaternion.identity); + + Transform inner = player.transform.Find("Player"); + if (inner != null) inner.tag = "Player"; + } + + private void ClearPrevious() + { + if (wallsParent != null) Destroy(wallsParent); + if (floorParent != null) Destroy(floorParent); + if (doorsParent != null) Destroy(doorsParent); + + wallsParent = new GameObject("Walls"); + floorParent = new GameObject("Floor"); + doorsParent = new GameObject("Doors"); + + wallsParent.transform.SetParent(transform); + floorParent.transform.SetParent(transform); + doorsParent.transform.SetParent(transform); + + MATRIX_SIZE_X = 10; + MATRIX_SIZE_Y = 10; + rooms.Clear(); + } + + // ══════════════════════════════════════════════════════════════════════════ + // GENERÁTOR + // ══════════════════════════════════════════════════════════════════════════ + + private void RunGenerator() + { + roomCount = rng.Next(roomCountMin, roomCountMax); + maxRoomSize = rng.Next(maxRoomSizeMin, maxRoomSizeMax); + + genMatrix = new int[MATRIX_SIZE_Y][]; + for (int i = 0; i < MATRIX_SIZE_Y; i++) + genMatrix[i] = new int[MATRIX_SIZE_X]; + + // FIX: zajistíme dostatek místa pro první místnost PŘED jejím vykreslením, + // jinak by GenerateRoomMatrix ořezal její rozměry na počáteční 10×10. + EnsureMatrixSize(maxRoomSize + ROOM_PADDING + 1, maxRoomSize + ROOM_PADDING + 1); + + Room firstRoom = CreateAndCarveRoom(0, 0); + firstRoom.possibleNeighbourDir[2] = false; // doleva ven z mapy + firstRoom.possibleNeighbourDir[3] = false; // nahoru ven z mapy + rooms.Add(firstRoom); + + Queue expandQueue = new Queue(); + expandQueue.Enqueue(firstRoom); + + int safety = 0, maxSafety = roomCount * 50; + + while (rooms.Count < roomCount && expandQueue.Count > 0 && safety < maxSafety) { - Vector2Int cell = kv.Key; - GameObject roomA = kv.Value; + safety++; + Room current = expandQueue.Dequeue(); + if (!current.canExpand()) continue; - foreach (Vector2Int dir in directions) + var dirKeys = new List(current.getPossibleDirections().Keys); + foreach (int dirKey in dirKeys) { - 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) + if (rooms.Count >= roomCount) break; + Room newRoom = TryGrowNeighbour(current, dirKey); + if (newRoom != null) { - axis = Vector3.right; - lenStraight = straightLenX; - lenDoor = doorLenX; - rot = Quaternion.Euler(0, 90, 0); + rooms.Add(newRoom); + current.addNeighbour(newRoom); + newRoom.addNeighbour(current); + expandQueue.Enqueue(newRoom); } - 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) + private void ConnectRooms() { - 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++) + var connectedPairs = new HashSet<(int, int)>(); + foreach (Room roomA in rooms) { - 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++) + foreach (Room roomB in roomA.getNeighbours()) { - 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; + int ha = roomA.GetHashCode(), hb = roomB.GetHashCode(); + var pair = ha < hb ? (ha, hb) : (hb, ha); + if (connectedPairs.Contains(pair)) continue; + connectedPairs.Add(pair); + CarveCorridor(roomA, roomB); } } } - private IEnumerator FinishGenerationAfterDelay() + private void CarveCorridor(Room a, Room b) { - yield return new WaitForSeconds(0.5f); + int half = CORRIDOR_WIDTH / 2; // pro width=3 → half=1 - /* ----------- Set entrances ----------- */ - foreach (var kv in gridToRoom) + if (a.end_x < b.start_x || b.end_x < a.start_x) { - 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); + // Horizontální chodba (místnosti vedle sebe v ose X) + Room left = a.end_x < b.start_x ? a : b; + Room right = left == a ? b : a; + + // Vnitřní překryv (bez krajních zdí místností) + int overlapStart = Mathf.Max(left.start_y + 1, right.start_y + 1); + int overlapEnd = Mathf.Min(left.end_y - 1, right.end_y - 1); + if (overlapEnd - overlapStart < CORRIDOR_WIDTH - 1) return; + + // Střed chodby – náhodně v bezpečné zóně tak, aby se chodba vešla + int centerMin = overlapStart + half; + int centerMax = overlapEnd - half; + if (centerMin > centerMax) return; + int centerY = rng.Next(centerMin, centerMax + 1); + + for (int dy = -half; dy <= half; dy++) + { + int y = centerY + dy; + // Vykopaný průchod od zdi levé místnosti po zeď pravé + for (int x = left.end_x; x <= right.start_x; x++) + SetTile(x, y, TILE_EMPTY); + + // Dveře na hranici místností + SetTile(left.end_x, y, TILE_DOOR); + SetTile(right.start_x, y, TILE_DOOR); + } + + // Boční zdi chodby (nad a pod ní), jen v mezeře mezi místnostmi + for (int x = left.end_x + 1; x < right.start_x; x++) + { + SafeSetWall(x, centerY - half - 1, left, right); + SafeSetWall(x, centerY + half + 1, left, right); + } } - - /* ----------- Build corridors ----------- */ - BuildCorridors(); - - /* ----------- Toggle all doors ----------- */ - foreach (var keyValuePair in gridToRoom) + else if (a.end_y < b.start_y || b.end_y < a.start_y) { - RoomHandler rh = keyValuePair.Value.GetComponent(); - rh.ToggleAllDoors(); - } + // Vertikální chodba (místnosti nad sebou v ose Y) + Room top = a.end_y < b.start_y ? a : b; + Room bottom = top == a ? b : a; - // Vytvoř globální NavMesh - if (useGlobalNavMesh) - { - yield return new WaitForSeconds(0.2f); - CreateGlobalNavMesh(); - } + int overlapStart = Mathf.Max(top.start_x + 1, bottom.start_x + 1); + int overlapEnd = Mathf.Min(top.end_x - 1, bottom.end_x - 1); + if (overlapEnd - overlapStart < CORRIDOR_WIDTH - 1) return; - OnGenerationComplete?.Invoke(this); + int centerMin = overlapStart + half; + int centerMax = overlapEnd - half; + if (centerMin > centerMax) return; + int centerX = rng.Next(centerMin, centerMax + 1); + + for (int dx = -half; dx <= half; dx++) + { + int x = centerX + dx; + for (int y = top.end_y; y <= bottom.start_y; y++) + SetTile(x, y, TILE_EMPTY); + + SetTile(x, top.end_y, TILE_DOOR); + SetTile(x, bottom.start_y, TILE_DOOR); + } + + for (int y = top.end_y + 1; y < bottom.start_y; y++) + { + SafeSetWall(centerX - half - 1, y, top, bottom); + SafeSetWall(centerX + half + 1, y, top, bottom); + } + } } - private void CreateGlobalNavMesh() + private void SafeSetWall(int x, int y, Room r1, Room r2) { - // Odstraň všechny existující NavMeshSurface z místností - foreach (var room in gridToRoom.Values) + if (x < 0 || y < 0 || y >= genMatrix.Length || x >= genMatrix[y].Length) return; + bool inside1 = x > r1.start_x && x < r1.end_x && y > r1.start_y && y < r1.end_y; + bool inside2 = x > r2.start_x && x < r2.end_x && y > r2.start_y && y < r2.end_y; + if (!inside1 && !inside2) genMatrix[y][x] = TILE_WALL; + } + + private void SetTile(int x, int y, int value) + { + if (y < 0 || y >= genMatrix.Length || x < 0 || x >= genMatrix[y].Length) return; + genMatrix[y][x] = value; + } + + // ══════════════════════════════════════════════════════════════════════════ + // MESH BUILDING + // ══════════════════════════════════════════════════════════════════════════ + + private void BuildMeshes() + { + var wallInstances = new List(); + var floorInstances = new List(); + var doorInstances = new List(); + + Mesh cubeMesh = CreateCubeMesh(cellSize, wallHeight, cellSize); + Mesh floorMesh = CreateCubeMesh(cellSize, cellSize * 0.1f, cellSize); + + float wallY = wallHeight / 2f; + float floorY = -(cellSize * 0.1f) / 2f; + float doorY = cellSize * 0.05f; + + for (int row = 0; row < genMatrix.Length; row++) { - NavMeshSurface roomSurface = room.GetComponent(); - if (roomSurface != null) + for (int col = 0; col < genMatrix[row].Length; col++) { - Destroy(roomSurface); + int tile = genMatrix[row][col]; + float worldX = col * cellSize; + float worldZ = row * cellSize; + + switch (tile) + { + case TILE_WALL: + wallInstances.Add(MakeCombineInstance(cubeMesh, + new Vector3(worldX, wallY, worldZ))); + break; + case TILE_DOOR: + floorInstances.Add(MakeCombineInstance(floorMesh, + new Vector3(worldX, floorY, worldZ))); + doorInstances.Add(MakeCombineInstance(floorMesh, + new Vector3(worldX, doorY, worldZ))); + break; + case TILE_EMPTY: + floorInstances.Add(MakeCombineInstance(floorMesh, + new Vector3(worldX, floorY, worldZ))); + break; + } } } - // Vytvoř globální NavMeshSurface - globalNavMeshSurface = gameObject.AddComponent(); - globalNavMeshSurface.collectObjects = CollectObjects.Children; - globalNavMeshSurface.useGeometry = NavMeshCollectGeometry.PhysicsColliders; + if (wallInstances.Count > 0) BuildCombinedObject(wallInstances, cubeMesh, wallsParent, wallMaterial, "WallMesh", addCollider: true); + if (floorInstances.Count > 0) BuildCombinedObject(floorInstances, floorMesh, floorParent, floorMaterial, "FloorMesh", addCollider: true); + if (doorInstances.Count > 0) BuildCombinedObject(doorInstances, floorMesh, doorsParent, doorMaterial, "DoorMesh", addCollider: false); - // Bake globální NavMesh - globalNavMeshSurface.BuildNavMesh(); + Destroy(cubeMesh); + Destroy(floorMesh); } - /* ============================================================ */ - /* HELPERS */ - /* ============================================================ */ - private static Vector3 GetPrefabXZ(GameObject prefab) + private CombineInstance MakeCombineInstance(Mesh mesh, Vector3 position) + => new CombineInstance { mesh = mesh, transform = Matrix4x4.Translate(position) }; + + private void BuildCombinedObject(List instances, Mesh templateMesh, + GameObject parent, Material mat, string meshName, bool addCollider) { - var ps = prefab.GetComponent(); - return ps ? new Vector3(ps.prefabSize.x, 0, ps.prefabSize.y) : new Vector3(10, 0, 10); + Mesh combined = new Mesh(); + combined.name = meshName; + combined.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; + combined.CombineMeshes(instances.ToArray(), true, true); + combined.RecalculateNormals(); + combined.RecalculateBounds(); + + var go = new GameObject(meshName); + go.transform.SetParent(parent.transform); + go.AddComponent().sharedMesh = combined; + go.AddComponent().sharedMaterial = + mat != null ? mat : new Material(Shader.Find("Standard")); + + if (addCollider) + go.AddComponent().sharedMesh = combined; } - private Vector2 GetFirstOrLastRoom(string[] lines) + private Mesh CreateCubeMesh(float w, float h, float d) { - for (int r = lines.Length - 1; r >= 0; r--) + var mesh = new Mesh(); + float hw = w / 2f, hh = h / 2f, hd = d / 2f; + + mesh.vertices = new Vector3[] { - 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; + new(-hw,-hh, hd), new( hw,-hh, hd), new( hw, hh, hd), new(-hw, hh, hd), + new( hw,-hh,-hd), new(-hw,-hh,-hd), new(-hw, hh,-hd), new( hw, hh,-hd), + new(-hw,-hh,-hd), new(-hw,-hh, hd), new(-hw, hh, hd), new(-hw, hh,-hd), + new( hw,-hh, hd), new( hw,-hh,-hd), new( hw, hh,-hd), new( hw, hh, hd), + new(-hw, hh, hd), new( hw, hh, hd), new( hw, hh,-hd), new(-hw, hh,-hd), + new(-hw,-hh,-hd), new( hw,-hh,-hd), new( hw,-hh, hd), new(-hw,-hh, hd), + }; + // Winding order → normály dovnitř (hráč vidí vnitřní povrch zdí) + mesh.triangles = new int[] + { + 0, 1, 2, 0, 2, 3, + 4, 5, 6, 4, 6, 7, + 8, 9,10, 8,10,11, + 12,13,14, 12,14,15, + 16,17,18, 16,18,19, + 20,21,22, 20,22,23, + }; + mesh.uv = new Vector2[] + { + new(0,0),new(1,0),new(1,1),new(0,1), + new(0,0),new(1,0),new(1,1),new(0,1), + new(0,0),new(1,0),new(1,1),new(0,1), + new(0,0),new(1,0),new(1,1),new(0,1), + new(0,0),new(1,0),new(1,1),new(0,1), + new(0,0),new(1,0),new(1,1),new(0,1), + }; + mesh.RecalculateNormals(); + return mesh; } -} + + // ══════════════════════════════════════════════════════════════════════════ + // GENERÁTOR – pomocné metody + // ══════════════════════════════════════════════════════════════════════════ + + private int[] GenerateRoomMatrix(int start_x, int start_y) + { + int x_stop = start_x + rng.Next(maxRoomSize / 2, maxRoomSize + 1); + int y_stop = start_y + rng.Next(maxRoomSize / 2, maxRoomSize + 1); + + if (x_stop >= MATRIX_SIZE_X) x_stop = MATRIX_SIZE_X - 1; + if (y_stop >= MATRIX_SIZE_Y) y_stop = MATRIX_SIZE_Y - 1; + + for (int y = start_y; y <= y_stop; y++) + for (int x = start_x; x <= x_stop; x++) + genMatrix[y][x] = (x == start_x || x == x_stop || y == start_y || y == y_stop) + ? TILE_WALL : TILE_EMPTY; + + return new int[] { x_stop, y_stop }; + } + + private Room CreateAndCarveRoom(int sx, int sy) + { + int[] end = GenerateRoomMatrix(sx, sy); + return new Room(sx, sy, end[0], end[1]); + } + + private bool HasValidOverlapWithParent(Room parent, Room child, int direction) + { + if (direction == 0 || direction == 2) + return (Mathf.Min(parent.end_y, child.end_y) - Mathf.Max(parent.start_y, child.start_y)) >= CORRIDOR_WIDTH + 2; + return (Mathf.Min(parent.end_x, child.end_x) - Mathf.Max(parent.start_x, child.start_x)) >= CORRIDOR_WIDTH + 2; + } + + private Room TryGrowNeighbour(Room from, int direction) + { + int est = maxRoomSize, minOverlap = CORRIDOR_WIDTH + 2; + int nsx, nsy; + + switch (direction) + { + case 0: + nsx = from.end_x + 1 + ROOM_PADDING; + nsy = rng.Next(from.start_y - est + minOverlap, from.end_y - minOverlap + 2); break; + case 1: + nsx = rng.Next(from.start_x - est + minOverlap, from.end_x - minOverlap + 2); + nsy = from.end_y + 1 + ROOM_PADDING; break; + case 2: + nsx = from.start_x - 1 - ROOM_PADDING - est; + nsy = rng.Next(from.start_y - est + minOverlap, from.end_y - minOverlap + 2); break; + case 3: + nsx = rng.Next(from.start_x - est + minOverlap, from.end_x - minOverlap + 2); + nsy = from.start_y - 1 - ROOM_PADDING - est; break; + default: return null; + } + + if (nsx < 0) nsx = 0; + if (nsy < 0) nsy = 0; + + EnsureMatrixSize(nsx + maxRoomSize + ROOM_PADDING + 1, + nsy + maxRoomSize + ROOM_PADDING + 1); + + if (WouldOverlapAny(nsx, nsy, nsx + maxRoomSize, nsy + maxRoomSize)) + return null; + + Room newRoom = CreateAndCarveRoom(nsx, nsy); + + if (OverlapsExistingRoom(newRoom) || !HasValidOverlapWithParent(from, newRoom, direction)) + { + EraseRoom(newRoom); + return null; + } + + from.blockDirection(direction); + return newRoom; + } + + private void EraseRoom(Room room) + { + for (int y = room.start_y; y <= room.end_y; y++) + for (int x = room.start_x; x <= room.end_x; x++) + if (y >= 0 && y < genMatrix.Length && x >= 0 && x < genMatrix[y].Length) + genMatrix[y][x] = TILE_EMPTY; + } + + private bool WouldOverlapAny(int sx, int sy, int ex, int ey) + { + foreach (Room r in rooms) + if (RectsOverlap(sx - ROOM_PADDING, sy - ROOM_PADDING, ex + ROOM_PADDING, ey + ROOM_PADDING, + r.start_x, r.start_y, r.end_x, r.end_y)) return true; + return false; + } + + private bool OverlapsExistingRoom(Room candidate) + { + foreach (Room r in rooms) + { + if (r == candidate) continue; + if (RectsOverlap(candidate.start_x - ROOM_PADDING, candidate.start_y - ROOM_PADDING, + candidate.end_x + ROOM_PADDING, candidate.end_y + ROOM_PADDING, + r.start_x, r.start_y, r.end_x, r.end_y)) return true; + } + return false; + } + + private static bool RectsOverlap(int ax1, int ay1, int ax2, int ay2, + int bx1, int by1, int bx2, int by2) + => ax1 <= bx2 && ax2 >= bx1 && ay1 <= by2 && ay2 >= by1; + + private void EnsureMatrixSize(int reqW, int reqH) + { + bool resized = false; + while (reqW >= MATRIX_SIZE_X) { MATRIX_SIZE_X *= 2; resized = true; } + while (reqH >= MATRIX_SIZE_Y) { MATRIX_SIZE_Y *= 2; resized = true; } + if (resized) ResizeArray(ref genMatrix, MATRIX_SIZE_Y, MATRIX_SIZE_X); + } + + private void ResizeArray(ref int[][] original, int newH, int newW) + { + System.Array.Resize(ref original, newH); + for (int i = 0; i < newH; i++) + { + if (original[i] == null) original[i] = new int[newW]; + else System.Array.Resize(ref original[i], newW); + } + } + + private class Room + { + public int start_x, start_y, end_x, end_y; + public List neighbours = new List(); + public Dictionary possibleNeighbourDir; + + public Room(int sx, int sy, int ex, int ey) + { + start_x = sx; start_y = sy; end_x = ex; end_y = ey; + possibleNeighbourDir = new Dictionary + { {0,true},{1,true},{2,true},{3,true} }; + } + + public List getNeighbours() => neighbours; + public void addNeighbour(Room r) => neighbours.Add(r); + public void blockDirection(int d) => possibleNeighbourDir[d] = false; + public Dictionary getPossibleDirections() => possibleNeighbourDir; + public bool canExpand() => + possibleNeighbourDir[0] || possibleNeighbourDir[1] || + possibleNeighbourDir[2] || possibleNeighbourDir[3]; + } +} \ No newline at end of file diff --git a/3D blobici/Assets/Scripts/MapGen/MapManager1.cs b/3D blobici/Assets/Scripts/MapGen/MapManager1.cs new file mode 100644 index 0000000..18792b6 --- /dev/null +++ b/3D blobici/Assets/Scripts/MapGen/MapManager1.cs @@ -0,0 +1,536 @@ +using System.Collections.Generic; +using UnityEngine; + +/// +/// Dungeon generátor pro Unity 3D. +/// Připojte tento skript na prázdný GameObject ve scéně. +/// +/// NASTAVENÍ V INSPEKTORU: +/// Cell Size – velikost jedné buňky matice v Unity units (default 1) +/// Wall Height – výška zdi (default 1) +/// Wall Material – material pro zdi (může být null → default/pink) +/// Floor Material– material pro podlahu (může být null) +/// Generate On Start – zavolá Generate() automaticky při startu +/// +public class MapManager : MonoBehaviour +{ + [Header("Generátor")] + [Tooltip("Velikost jedné buňky matice v Unity units.")] + public float cellSize = 1f; + + [Tooltip("Výška zdi.")] + public float wallHeight = 1f; + + [Tooltip("Počet místností (rozsah).")] + public int roomCountMin = 10; + public int roomCountMax = 20; + + [Tooltip("Maximální velikost místnosti v buňkách (rozsah).")] + public int maxRoomSizeMin = 14; + public int maxRoomSizeMax = 20; + + [Header("Materiály")] + public Material wallMaterial; + public Material floorMaterial; + public Material doorMaterial; + + [Header("Chování")] + public bool generateOnStart = true; + + // ── Interní stav ────────────────────────────────────────────────────────── + private int[][] genMatrix; + private int roomCount; + private int maxRoomSize; + + private int MATRIX_SIZE_X = 10; + private int MATRIX_SIZE_Y = 10; + private const int ROOM_PADDING = 1; + + private System.Random rng = new System.Random(); + private List rooms = new List(); + + // Rodičovské objekty pro přehlednost v hierarchii + private GameObject wallsParent; + private GameObject floorParent; + private GameObject doorsParent; + + // TileType musí odpovídat hodnotám v matici + private const int TILE_EMPTY = 0; + private const int TILE_WALL = 1; + private const int TILE_DOOR = 2; + + // ── Unity lifecycle ─────────────────────────────────────────────────────── + private void Start() + { + if (generateOnStart) + Generate(); + } + + // ── Veřejné API ────────────────────────────────────────────────────────── + + /// Vygeneruje nový dungeon (odstraní předchozí). + public void Generate() + { + ClearPrevious(); + RunGenerator(); + ConnectRooms(); + BuildMeshes(); + } + + // ── Čištění ────────────────────────────────────────────────────────────── + + private void ClearPrevious() + { + if (wallsParent != null) Destroy(wallsParent); + if (floorParent != null) Destroy(floorParent); + if (doorsParent != null) Destroy(doorsParent); + + wallsParent = new GameObject("Walls"); + floorParent = new GameObject("Floor"); + doorsParent = new GameObject("Doors"); + + wallsParent.transform.SetParent(transform); + floorParent.transform.SetParent(transform); + doorsParent.transform.SetParent(transform); + + MATRIX_SIZE_X = 10; + MATRIX_SIZE_Y = 10; + rooms.Clear(); + } + + // ══════════════════════════════════════════════════════════════════════════ + // LOGIKA GENERÁTORU (portováno z původního C# kódu) + // ══════════════════════════════════════════════════════════════════════════ + + private void RunGenerator() + { + roomCount = rng.Next(roomCountMin, roomCountMax); + maxRoomSize = rng.Next(maxRoomSizeMin, maxRoomSizeMax); + + genMatrix = new int[MATRIX_SIZE_Y][]; + for (int i = 0; i < MATRIX_SIZE_Y; i++) + genMatrix[i] = new int[MATRIX_SIZE_X]; + + Room firstRoom = CreateAndCarveRoom(0, 0); + firstRoom.possibleNeighbourDir[0] = false; + firstRoom.possibleNeighbourDir[1] = false; + rooms.Add(firstRoom); + + Queue expandQueue = new Queue(); + expandQueue.Enqueue(firstRoom); + + int safety = 0; + int maxSafety = roomCount * 50; + + while (rooms.Count < roomCount && expandQueue.Count > 0 && safety < maxSafety) + { + safety++; + Room current = expandQueue.Dequeue(); + if (!current.canExpand()) continue; + + foreach (var dir in current.getPossibleDirections()) + { + if (rooms.Count >= roomCount) break; + Room newRoom = TryGrowNeighbour(current, dir.Key); + if (newRoom != null) + { + rooms.Add(newRoom); + current.addNeighbour(newRoom); + newRoom.addNeighbour(current); + expandQueue.Enqueue(newRoom); + } + } + } + } + + private void ConnectRooms() + { + var connectedPairs = new HashSet<(int, int)>(); + + foreach (Room roomA in rooms) + { + foreach (Room roomB in roomA.getNeighbours()) + { + int ha = roomA.GetHashCode(); + int hb = roomB.GetHashCode(); + var pair = ha < hb ? (ha, hb) : (hb, ha); + if (connectedPairs.Contains(pair)) continue; + connectedPairs.Add(pair); + CarveCorridor(roomA, roomB); + } + } + } + + private void CarveCorridor(Room a, Room b) + { + // Horizontální sousedství + if (a.end_x < b.start_x || b.end_x < a.start_x) + { + Room left = a.end_x < b.start_x ? a : b; + Room right = left == a ? b : a; + + int overlapStartY = Mathf.Max(left.start_y, right.start_y); + int overlapEndY = Mathf.Min(left.end_y, right.end_y); + if (overlapStartY > overlapEndY) return; + + int targetY = (overlapStartY == overlapEndY) + ? overlapStartY + : rng.Next(overlapStartY + 1, overlapEndY); + + for (int x = left.end_x; x <= right.start_x; x++) + SetTile(x, targetY, TILE_EMPTY); + + SetTile(left.end_x, targetY, TILE_DOOR); + SetTile(right.start_x, targetY, TILE_DOOR); + + for (int x = left.end_x + 1; x < right.start_x; x++) + { + SafeSetWall(x, targetY - 1, left, right); + SafeSetWall(x, targetY + 1, left, right); + } + } + // Vertikální sousedství + else if (a.end_y < b.start_y || b.end_y < a.start_y) + { + Room top = a.end_y < b.start_y ? a : b; + Room bottom = top == a ? b : a; + + int overlapStartX = Mathf.Max(top.start_x, bottom.start_x); + int overlapEndX = Mathf.Min(top.end_x, bottom.end_x); + if (overlapStartX > overlapEndX) return; + + int targetX = (overlapStartX == overlapEndX) + ? overlapStartX + : rng.Next(overlapStartX + 1, overlapEndX); + + for (int y = top.end_y; y <= bottom.start_y; y++) + SetTile(targetX, y, TILE_EMPTY); + + SetTile(targetX, top.end_y, TILE_DOOR); + SetTile(targetX, bottom.start_y, TILE_DOOR); + + for (int y = top.end_y + 1; y < bottom.start_y; y++) + { + SafeSetWall(targetX - 1, y, top, bottom); + SafeSetWall(targetX + 1, y, top, bottom); + } + } + } + + private void SafeSetWall(int x, int y, Room r1, Room r2) + { + if (x < 0 || y < 0 || y >= genMatrix.Length || x >= genMatrix[y].Length) return; + bool inside1 = x > r1.start_x && x < r1.end_x && y > r1.start_y && y < r1.end_y; + bool inside2 = x > r2.start_x && x < r2.end_x && y > r2.start_y && y < r2.end_y; + if (!inside1 && !inside2) + genMatrix[y][x] = TILE_WALL; + } + + private void SetTile(int x, int y, int value) + { + if (y < 0 || y >= genMatrix.Length || x < 0 || x >= genMatrix[y].Length) return; + genMatrix[y][x] = value; + } + + // ══════════════════════════════════════════════════════════════════════════ + // MESH BUILDING + // Klíčová optimalizace: místo N GameObject cubů se vše sloučí do 3 meshů + // (zdi, podlaha, dveře) → 3 draw cally celkem bez ohledu na velikost dungeonu. + // ══════════════════════════════════════════════════════════════════════════ + + private void BuildMeshes() + { + var wallInstances = new List(); + var floorInstances = new List(); + var doorInstances = new List(); + + // Sdílené primitivní meshe (vytvoříme je jednou a použijeme jako šablonu) + Mesh cubeMesh = CreateCubeMesh(cellSize, wallHeight, cellSize); + Mesh floorMesh = CreateCubeMesh(cellSize, cellSize * 0.1f, cellSize); + + for (int row = 0; row < genMatrix.Length; row++) + { + for (int col = 0; col < genMatrix[row].Length; col++) + { + int tile = genMatrix[row][col]; + if (tile == TILE_EMPTY) continue; + + // Pozice v Unity: X = sloupec, Y = výška, Z = řádek + Vector3 pos = new Vector3(col * cellSize, 0f, row * cellSize); + + if (tile == TILE_WALL) + { + wallInstances.Add(MakeCombineInstance(cubeMesh, pos)); + } + else if (tile == TILE_DOOR) + { + doorInstances.Add(MakeCombineInstance(floorMesh, + pos + new Vector3(0f, wallHeight * 0.05f, 0f))); + } + } + } + + // Podlaha: jedna plocha pod celou mapou pro každou neprázdnou buňku + for (int row = 0; row < genMatrix.Length; row++) + { + for (int col = 0; col < genMatrix[row].Length; col++) + { + if (genMatrix[row][col] == TILE_EMPTY || genMatrix[row][col] == TILE_DOOR) + { + Vector3 pos = new Vector3(col * cellSize, -cellSize * 0.05f, row * cellSize); + floorInstances.Add(MakeCombineInstance(floorMesh, pos)); + } + } + } + + // Sestavení finálních objektů + if (wallInstances.Count > 0) BuildCombinedObject(wallInstances, cubeMesh, wallsParent, wallMaterial, "WallMesh", true); + if (floorInstances.Count > 0) BuildCombinedObject(floorInstances, floorMesh, floorParent, floorMaterial, "FloorMesh", false); + if (doorInstances.Count > 0) BuildCombinedObject(doorInstances, floorMesh, doorsParent, doorMaterial, "DoorMesh", false); + + // Uvolníme dočasné meshe (nepotřebujeme je po sloučení) + Destroy(cubeMesh); + Destroy(floorMesh); + } + + private CombineInstance MakeCombineInstance(Mesh mesh, Vector3 position) + { + return new CombineInstance + { + mesh = mesh, + transform = Matrix4x4.Translate(position) + }; + } + + private void BuildCombinedObject(List instances, Mesh templateMesh, + GameObject parent, Material mat, string meshName, bool addCollider) + { + Mesh combined = new Mesh(); + combined.name = meshName; + // IndexFormat.UInt32 umožní meshe s více než 65k vrcholy (velké dungeony) + combined.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; + combined.CombineMeshes(instances.ToArray(), true, true); + combined.RecalculateNormals(); + combined.RecalculateBounds(); + + var go = new GameObject(meshName); + go.transform.SetParent(parent.transform); + + var mf = go.AddComponent(); + mf.sharedMesh = combined; + + var mr = go.AddComponent(); + mr.sharedMaterial = mat != null ? mat : new Material(Shader.Find("Standard")); + + if (addCollider) + { + // MeshCollider na sloučeném zdi-meshi → fyzika funguje bez extra objektů + var mc = go.AddComponent(); + mc.sharedMesh = combined; + } + } + + // ── Pomocné: vytvoří jednoduchý kvádrový mesh o daných rozměrech ───────── + private Mesh CreateCubeMesh(float w, float h, float d) + { + var mesh = new Mesh(); + + float hw = w / 2f, hh = h / 2f, hd = d / 2f; + + mesh.vertices = new Vector3[] + { + // Přední stěna + new(-hw,-hh, hd), new( hw,-hh, hd), new( hw, hh, hd), new(-hw, hh, hd), + // Zadní stěna + new( hw,-hh,-hd), new(-hw,-hh,-hd), new(-hw, hh,-hd), new( hw, hh,-hd), + // Levá stěna + new(-hw,-hh,-hd), new(-hw,-hh, hd), new(-hw, hh, hd), new(-hw, hh,-hd), + // Pravá stěna + new( hw,-hh, hd), new( hw,-hh,-hd), new( hw, hh,-hd), new( hw, hh, hd), + // Horní stěna + new(-hw, hh, hd), new( hw, hh, hd), new( hw, hh,-hd), new(-hw, hh,-hd), + // Spodní stěna + new(-hw,-hh,-hd), new( hw,-hh,-hd), new( hw,-hh, hd), new(-hw,-hh, hd), + }; + + mesh.triangles = new int[] + { + 0, 2, 1, 0, 3, 2, // přední + 4, 6, 5, 4, 7, 6, // zadní + 8,10, 9, 8,11,10, // levá + 12,14,13, 12,15,14, // pravá + 16,18,17, 16,19,18, // horní + 20,22,21, 20,23,22, // spodní + }; + + mesh.uv = new Vector2[] + { + new(0,0),new(1,0),new(1,1),new(0,1), + new(0,0),new(1,0),new(1,1),new(0,1), + new(0,0),new(1,0),new(1,1),new(0,1), + new(0,0),new(1,0),new(1,1),new(0,1), + new(0,0),new(1,0),new(1,1),new(0,1), + new(0,0),new(1,0),new(1,1),new(0,1), + }; + + mesh.RecalculateNormals(); + return mesh; + } + + // ══════════════════════════════════════════════════════════════════════════ + // GENERÁTOR – INTERNÍ TŘÍDY A METODY (beze změny z původního kódu) + // ══════════════════════════════════════════════════════════════════════════ + + private int[] GenerateRoomMatrix(int start_x, int start_y) + { + int x_stop = start_x + rng.Next(maxRoomSize / 2, maxRoomSize + 1); + int y_stop = start_y + rng.Next(maxRoomSize / 2, maxRoomSize + 1); + + if (x_stop >= MATRIX_SIZE_X) x_stop = MATRIX_SIZE_X - 1; + if (y_stop >= MATRIX_SIZE_Y) y_stop = MATRIX_SIZE_Y - 1; + + for (int y = start_y; y <= y_stop; y++) + { + for (int x = start_x; x <= x_stop; x++) + { + genMatrix[y][x] = (x == start_x || x == x_stop || y == start_y || y == y_stop) + ? TILE_WALL + : TILE_EMPTY; + } + } + return new int[] { x_stop, y_stop }; + } + + private Room CreateAndCarveRoom(int sx, int sy) + { + int[] end = GenerateRoomMatrix(sx, sy); + return new Room(sx, sy, end[0], end[1]); + } + + private bool HasValidOverlapWithParent(Room parent, Room child, int direction) + { + if (direction == 0 || direction == 2) + { + return (Mathf.Min(parent.end_y, child.end_y) - Mathf.Max(parent.start_y, child.start_y)) >= 3; + } + return (Mathf.Min(parent.end_x, child.end_x) - Mathf.Max(parent.start_x, child.start_x)) >= 3; + } + + private Room TryGrowNeighbour(Room from, int direction) + { + int est = maxRoomSize, minOverlap = 3; + int nsx, nsy; + + switch (direction) + { + case 0: + nsx = from.end_x + 1 + ROOM_PADDING; + nsy = rng.Next(from.start_y - est + minOverlap, from.end_y - minOverlap + 2); break; + case 1: + nsx = rng.Next(from.start_x - est + minOverlap, from.end_x - minOverlap + 2); + nsy = from.end_y + 1 + ROOM_PADDING; break; + case 2: + nsx = from.start_x - 1 - ROOM_PADDING - est; + nsy = rng.Next(from.start_y - est + minOverlap, from.end_y - minOverlap + 2); break; + case 3: + nsx = rng.Next(from.start_x - est + minOverlap, from.end_x - minOverlap + 2); + nsy = from.start_y - 1 - ROOM_PADDING - est; break; + default: return null; + } + + if (nsx < 0) nsx = 0; + if (nsy < 0) nsy = 0; + + EnsureMatrixSize(nsx + maxRoomSize + ROOM_PADDING + 1, + nsy + maxRoomSize + ROOM_PADDING + 1); + + if (WouldOverlapAny(nsx, nsy, nsx + maxRoomSize, nsy + maxRoomSize)) + return null; + + Room newRoom = CreateAndCarveRoom(nsx, nsy); + + if (OverlapsExistingRoom(newRoom) || !HasValidOverlapWithParent(from, newRoom, direction)) + { + EraseRoom(newRoom); + return null; + } + + from.blockDirection(direction); + return newRoom; + } + + private void EraseRoom(Room room) + { + for (int y = room.start_y; y <= room.end_y; y++) + for (int x = room.start_x; x <= room.end_x; x++) + if (y >= 0 && y < genMatrix.Length && x >= 0 && x < genMatrix[y].Length) + genMatrix[y][x] = TILE_EMPTY; + } + + private bool WouldOverlapAny(int sx, int sy, int ex, int ey) + { + foreach (Room r in rooms) + if (RectsOverlap(sx - ROOM_PADDING, sy - ROOM_PADDING, ex + ROOM_PADDING, ey + ROOM_PADDING, + r.start_x, r.start_y, r.end_x, r.end_y)) return true; + return false; + } + + private bool OverlapsExistingRoom(Room candidate) + { + foreach (Room r in rooms) + { + if (r == candidate) continue; + if (RectsOverlap(candidate.start_x - ROOM_PADDING, candidate.start_y - ROOM_PADDING, + candidate.end_x + ROOM_PADDING, candidate.end_y + ROOM_PADDING, + r.start_x, r.start_y, r.end_x, r.end_y)) return true; + } + return false; + } + + private static bool RectsOverlap(int ax1, int ay1, int ax2, int ay2, + int bx1, int by1, int bx2, int by2) + => ax1 <= bx2 && ax2 >= bx1 && ay1 <= by2 && ay2 >= by1; + + private void EnsureMatrixSize(int reqW, int reqH) + { + bool resized = false; + while (reqW >= MATRIX_SIZE_X) { MATRIX_SIZE_X *= 2; resized = true; } + while (reqH >= MATRIX_SIZE_Y) { MATRIX_SIZE_Y *= 2; resized = true; } + if (resized) ResizeArray(ref genMatrix, MATRIX_SIZE_Y, MATRIX_SIZE_X); + } + + private void ResizeArray(ref int[][] original, int newH, int newW) + { + System.Array.Resize(ref original, newH); + for (int i = 0; i < newH; i++) + { + if (original[i] == null) original[i] = new int[newW]; + else System.Array.Resize(ref original[i], newW); + } + } + + // ── Třída Room (vnořená, aby byl soubor self-contained) ────────────────── + + private class Room + { + public int start_x, start_y, end_x, end_y; + public List neighbours = new List(); + public Dictionary possibleNeighbourDir; + + public Room(int sx, int sy, int ex, int ey) + { + start_x = sx; start_y = sy; end_x = ex; end_y = ey; + possibleNeighbourDir = new Dictionary + { {0,true},{1,true},{2,true},{3,true} }; + } + + public List getNeighbours() => neighbours; + public void addNeighbour(Room r) => neighbours.Add(r); + public void blockDirection(int d) => possibleNeighbourDir[d] = false; + // addNeighbour(int) v původním kódu blokovalo směr – zachováváme jako alias: + public Dictionary getPossibleDirections() => possibleNeighbourDir; + public bool canExpand() => + possibleNeighbourDir[0] || possibleNeighbourDir[1] || + possibleNeighbourDir[2] || possibleNeighbourDir[3]; + } +} \ No newline at end of file diff --git a/3D blobici/Assets/Scripts/MapGen/MapManager1.cs.meta b/3D blobici/Assets/Scripts/MapGen/MapManager1.cs.meta new file mode 100644 index 0000000..00ae492 --- /dev/null +++ b/3D blobici/Assets/Scripts/MapGen/MapManager1.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 5d9ee91532380784aa5134fbb9be90cb diff --git a/3D blobici/ProjectSettings/ProjectSettings.asset b/3D blobici/ProjectSettings/ProjectSettings.asset index 9adc1a6..b8b67c4 100644 --- a/3D blobici/ProjectSettings/ProjectSettings.asset +++ b/3D blobici/ProjectSettings/ProjectSettings.asset @@ -379,6 +379,103 @@ PlayerSettings: m_Height: 36 m_Kind: 0 m_SubKind: + - m_BuildTarget: iPhone + m_Icons: + - m_Textures: [] + m_Width: 180 + m_Height: 180 + m_Kind: 0 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 120 + m_Height: 120 + m_Kind: 0 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 167 + m_Height: 167 + m_Kind: 0 + m_SubKind: iPad + - m_Textures: [] + m_Width: 152 + m_Height: 152 + m_Kind: 0 + m_SubKind: iPad + - m_Textures: [] + m_Width: 76 + m_Height: 76 + m_Kind: 0 + m_SubKind: iPad + - m_Textures: [] + m_Width: 120 + m_Height: 120 + m_Kind: 3 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 80 + m_Height: 80 + m_Kind: 3 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 80 + m_Height: 80 + m_Kind: 3 + m_SubKind: iPad + - m_Textures: [] + m_Width: 40 + m_Height: 40 + m_Kind: 3 + m_SubKind: iPad + - m_Textures: [] + m_Width: 87 + m_Height: 87 + m_Kind: 1 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 58 + m_Height: 58 + m_Kind: 1 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 29 + m_Height: 29 + m_Kind: 1 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 58 + m_Height: 58 + m_Kind: 1 + m_SubKind: iPad + - m_Textures: [] + m_Width: 29 + m_Height: 29 + m_Kind: 1 + m_SubKind: iPad + - m_Textures: [] + m_Width: 60 + m_Height: 60 + m_Kind: 2 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 40 + m_Height: 40 + m_Kind: 2 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 40 + m_Height: 40 + m_Kind: 2 + m_SubKind: iPad + - m_Textures: [] + m_Width: 20 + m_Height: 20 + m_Kind: 2 + m_SubKind: iPad + - m_Textures: [] + m_Width: 1024 + m_Height: 1024 + m_Kind: 4 + m_SubKind: App Store m_BuildTargetBatching: - m_BuildTarget: Standalone m_StaticBatching: 1