151 lines
6.7 KiB
C#
151 lines
6.7 KiB
C#
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<Vector2Int> CollectDoorCells(FloorData floor)
|
||
{
|
||
var set = new HashSet<Vector2Int>();
|
||
|
||
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<Vector2Int> 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<Vector2Int> 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));
|
||
}
|
||
} |