From bb8db47ecc511620ef80d1d7bbf124494a47e809 Mon Sep 17 00:00:00 2001 From: vladislove Date: Sun, 11 Oct 2026 19:07:55 +0300 Subject: [PATCH] =?UTF-8?q?=D1=81=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20=D1=81=D1=82=D0=B5=D0=BD=20=D0=BA=D0=B0=D0=BA=20=D0=B8?= =?UTF-8?q?=D0=BD=D1=84=D0=BE=D1=80=D0=BC=D0=B0=D1=86=D0=B8=D0=B8,=20?= =?UTF-8?q?=D0=B5=D1=89=D1=91=20=D0=BD=D0=B5=20=D0=BE=D0=B1=D1=8C=D0=B5?= =?UTF-8?q?=D0=BA=D1=82=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Scripts/LevelGenerating/FloorData.cs | 4 + .../_Project/Scripts/LevelGenerating/Room.cs | 6 +- .../Scripts/LevelGenerating/ShellBuilder.cs | 151 ++++++++++++++++++ .../LevelGenerating/ShellBuilder.cs.meta | 3 + .../Scripts/LevelGenerating/WallsGenerator.cs | 17 +- 5 files changed, 174 insertions(+), 7 deletions(-) create mode 100644 Assets/_Project/Scripts/LevelGenerating/ShellBuilder.cs create mode 100644 Assets/_Project/Scripts/LevelGenerating/ShellBuilder.cs.meta diff --git a/Assets/_Project/Scripts/LevelGenerating/FloorData.cs b/Assets/_Project/Scripts/LevelGenerating/FloorData.cs index a8486b2..170559c 100644 --- a/Assets/_Project/Scripts/LevelGenerating/FloorData.cs +++ b/Assets/_Project/Scripts/LevelGenerating/FloorData.cs @@ -30,6 +30,9 @@ namespace _Project.Scripts.LevelGenerating public readonly List Rooms = new List(); public readonly Dictionary Depth = new Dictionary(); + // Сегменты стен (заполняет ShellBuilder.Build) + public readonly List Walls = new List(); + public Room Start; // стартовая комната (вход с предыдущего этажа) public Room Lift; // комната с лифтом (выход на следующий этаж) @@ -74,6 +77,7 @@ namespace _Project.Scripts.LevelGenerating { Rooms.Clear(); Depth.Clear(); + Walls.Clear(); Start = null; Lift = null; } diff --git a/Assets/_Project/Scripts/LevelGenerating/Room.cs b/Assets/_Project/Scripts/LevelGenerating/Room.cs index 60b1245..2ab4a1e 100644 --- a/Assets/_Project/Scripts/LevelGenerating/Room.cs +++ b/Assets/_Project/Scripts/LevelGenerating/Room.cs @@ -56,7 +56,7 @@ namespace _Project.Scripts.LevelGenerating this.rt = rt; } - public Room(Vector2Int position, int width, int length, RoomType rt, bool isWallable, + public Room(Vector2Int position, int width, int length, RoomType rt, bool isClipped, EntranceToRoomType entrance, List exits) { Position = position; @@ -64,7 +64,7 @@ namespace _Project.Scripts.LevelGenerating Length = length; this.rt = rt; this.entrance = entrance; - this.isWallable = isWallable; + this.isClipped = isClipped; this.exits = exits; } @@ -73,7 +73,7 @@ namespace _Project.Scripts.LevelGenerating public Vector2Int Position; // левый нижний угол, в модулях public RoomType rt { get; set; } public EntranceToRoomType entrance { get; set; } - public bool isWallable { get; set; } // по факту: комнату ужали об границу карты (стоит переименовать) + public bool isClipped { get; set; } public List exits { get; set; } public Vector2 Center => new Vector2(Position.x + Length / 2f, Position.y + Width / 2f); diff --git a/Assets/_Project/Scripts/LevelGenerating/ShellBuilder.cs b/Assets/_Project/Scripts/LevelGenerating/ShellBuilder.cs new file mode 100644 index 0000000..127b9de --- /dev/null +++ b/Assets/_Project/Scripts/LevelGenerating/ShellBuilder.cs @@ -0,0 +1,151 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace _Project.Scripts.LevelGenerating +{ + public enum SegmentKind { Wall, Corner } + + // Куда смотрит видимая сторона сегмента (внутрь комнаты или коридора). + // Для Corner - какой это угол комнаты (SW = минимальные X и Z). + public enum SegmentFacing { XP, XN, ZP, ZN, CornerSW, CornerSE, CornerNW, CornerNE } + + // Один сегмент стены = одна клетка (1 модуль = 0.5 м) по периметру. + // Поля публичные, чтобы потом можно было сохранять в файл. + [System.Serializable] + public struct WallSegment + { + public Vector2Int Cell; + public SegmentKind Kind; + public SegmentFacing Facing; + + public WallSegment(Vector2Int cell, SegmentKind kind, SegmentFacing facing) + { + Cell = cell; + Kind = kind; + Facing = facing; + } + } + + // ===================================================================== + // Строит сегменты стен из FloorData. Спавна здесь нет, только данные. + // + // Правила: + // - Стены стоят ВНУТРИ прямоугольника комнаты: это внешнее кольцо клеток шириной 1 модуль. + // - У коридора стены только по двум длинным сторонам, торцы открыты. + // - Проём в стене комнаты равен ВНУТРЕННЕЙ ширине коридора (ширина коридора минус 2 клетки, + // то есть минус по 0.5 м стены с каждой стороны). Иначе проём был бы шире прохода. + // - Стык стены коридора со стеной комнаты - Т-образный, угловой элемент там не ставится. + // + // НЕ ЗАБЫТЬ: ширина коридора должна быть минимум 3 клетки, иначе проёма не будет. + // ===================================================================== + public static class ShellBuilder + { + public static void Build(FloorData floor) + { + floor.Walls.Clear(); + var doors = CollectDoorCells(floor); + + foreach (var r in floor.Rooms) + { + if (r.rt == RoomType.Corridor) AddCorridorWalls(floor, r); + else AddRoomWalls(floor, r, doors); + } + } + + // Коридор идёт вдоль X, если его выход по оси X (запасной вариант: длинная сторона) + private static bool IsAlongX(Room c) + { + if (c.exits != null && c.exits.Count > 0) + return c.exits[0] == ExitsInRoomType.XAxisP || c.exits[0] == ExitsInRoomType.XAxisN; + return c.Length >= c.Width; + } + + // Клетки стен комнат, которые нужно убрать под проёмы коридоров + private static HashSet CollectDoorCells(FloorData floor) + { + var set = new HashSet(); + + foreach (var c in floor.Corridors) + { + int x0 = c.Position.x, z0 = c.Position.y; + + if (IsAlongX(c)) + { + // слева стена комнаты в колонке x0 - 1, справа в колонке x0 + Length + for (int z = z0 + 1; z < z0 + c.Width - 1; z++) + { + set.Add(new Vector2Int(x0 - 1, z)); + set.Add(new Vector2Int(x0 + c.Length, z)); + } + } + else + { + // снизу стена комнаты в ряду z0 - 1, сверху в ряду z0 + Width + for (int x = x0 + 1; x < x0 + c.Length - 1; x++) + { + set.Add(new Vector2Int(x, z0 - 1)); + set.Add(new Vector2Int(x, z0 + c.Width)); + } + } + } + return set; + } + + private static void AddRoomWalls(FloorData floor, Room r, HashSet doors) + { + int x0 = r.Position.x, z0 = r.Position.y; + int x1 = x0 + r.Length - 1, z1 = z0 + r.Width - 1; + + // углы + Add(floor, new Vector2Int(x0, z0), SegmentKind.Corner, SegmentFacing.CornerSW); + Add(floor, new Vector2Int(x1, z0), SegmentKind.Corner, SegmentFacing.CornerSE); + Add(floor, new Vector2Int(x0, z1), SegmentKind.Corner, SegmentFacing.CornerNW); + Add(floor, new Vector2Int(x1, z1), SegmentKind.Corner, SegmentFacing.CornerNE); + + // нижняя и верхняя стены (идут вдоль X) + for (int x = x0 + 1; x < x1; x++) + { + AddWall(floor, new Vector2Int(x, z0), SegmentFacing.ZP, doors); + AddWall(floor, new Vector2Int(x, z1), SegmentFacing.ZN, doors); + } + + // левая и правая стены (идут вдоль Z) + for (int z = z0 + 1; z < z1; z++) + { + AddWall(floor, new Vector2Int(x0, z), SegmentFacing.XP, doors); + AddWall(floor, new Vector2Int(x1, z), SegmentFacing.XN, doors); + } + } + + private static void AddCorridorWalls(FloorData floor, Room c) + { + int x0 = c.Position.x, z0 = c.Position.y; + + if (IsAlongX(c)) + { + for (int x = x0; x < x0 + c.Length; x++) + { + Add(floor, new Vector2Int(x, z0), SegmentKind.Wall, SegmentFacing.ZP); + Add(floor, new Vector2Int(x, z0 + c.Width - 1), SegmentKind.Wall, SegmentFacing.ZN); + } + } + else + { + for (int z = z0; z < z0 + c.Width; z++) + { + Add(floor, new Vector2Int(x0, z), SegmentKind.Wall, SegmentFacing.XP); + Add(floor, new Vector2Int(x0 + c.Length - 1, z), SegmentKind.Wall, SegmentFacing.XN); + } + } + } + + private static void AddWall(FloorData floor, Vector2Int cell, SegmentFacing facing, HashSet doors) + { + if (doors.Contains(cell)) return; // здесь проём + Add(floor, cell, SegmentKind.Wall, facing); + } + + private static void Add(FloorData floor, Vector2Int cell, SegmentKind kind, SegmentFacing facing) => + floor.Walls.Add(new WallSegment(cell, kind, facing)); + } +} \ No newline at end of file diff --git a/Assets/_Project/Scripts/LevelGenerating/ShellBuilder.cs.meta b/Assets/_Project/Scripts/LevelGenerating/ShellBuilder.cs.meta new file mode 100644 index 0000000..a20c2a6 --- /dev/null +++ b/Assets/_Project/Scripts/LevelGenerating/ShellBuilder.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a50a2fbc9cd247ff94eba16018870886 +timeCreated: 1791732492 \ No newline at end of file diff --git a/Assets/_Project/Scripts/LevelGenerating/WallsGenerator.cs b/Assets/_Project/Scripts/LevelGenerating/WallsGenerator.cs index ed24b20..53c87ca 100644 --- a/Assets/_Project/Scripts/LevelGenerating/WallsGenerator.cs +++ b/Assets/_Project/Scripts/LevelGenerating/WallsGenerator.cs @@ -123,6 +123,7 @@ namespace _Project.Scripts.LevelGenerating if (!ok) Debug.LogError("Лифт не помещается: проверь размеры карты, лифта и коридора"); AssignRoles(); + ShellBuilder.Build(_data); // сегменты стен (только данные, без спавна) SpawnMarkers(); // временно return _data; @@ -133,7 +134,7 @@ namespace _Project.Scripts.LevelGenerating { foreach (var r in _data.UnassignedRooms.ToList()) { - var pool = r.isWallable ? ClippedRoles : FullRoles; + var pool = r.isClipped ? ClippedRoles : FullRoles; r.rt = pool[Random.Range(0, pool.Length)]; } } @@ -222,7 +223,7 @@ namespace _Project.Scripts.LevelGenerating lift.rt = RoomType.Lift; // тип нужно выставить ДО AddRoom, чтобы FloorData запомнил Lift lift.exits = new List(); lift.entrance = ToEntrance(dir); - lift.isWallable = false; + lift.isClipped = false; _data.AddRoom(lift, liftDepth); _parent[lift] = (parent, dir); @@ -432,7 +433,7 @@ namespace _Project.Scripts.LevelGenerating newRoom.entrance = ToEntrance(dir); if (!FitRoom(prevRoom, newRoom, dir, out bool shrunk)) return false; - newRoom.isWallable = shrunk; + newRoom.isClipped = shrunk; // Пересечения: комната (с зазором, без prevRoom) и коридор к ней RectInt roomArea = Inflate(RoomRect(newRoom), Mathf.Min(_margin, _corrLen)); @@ -568,6 +569,14 @@ namespace _Project.Scripts.LevelGenerating Vector3 c = LevelGrid.ToWorld(r.Center); Gizmos.DrawWireCube(c, new Vector3(r.Length * LevelGrid.Module, 0.1f, r.Width * LevelGrid.Module)); } + + // Сегменты стен: чёрные - стена, красные - угол + foreach (var s in _data.Walls) + { + Gizmos.color = s.Kind == SegmentKind.Corner ? Color.red : Color.black; + Vector3 pos = LevelGrid.ToWorld(new Vector2(s.Cell.x + 0.5f, s.Cell.y + 0.5f), 0.5f); + Gizmos.DrawCube(pos, new Vector3(LevelGrid.Module, 1f, LevelGrid.Module) * 0.9f); + } } } -} +} \ No newline at end of file